Flutter Flare,Rive,它可以用于背景吗? [英] Flutter Flare, Rive, is it able to use for background?
本文介绍了Flutter Flare,Rive,它可以用于背景吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我刚刚为Flutter-Flare成功实现了Flutter小部件
但是我找不到用于背景(容器)的示例
这可能吗?
解决方案
使用官方示例代码
完整代码
import'package:flutter/material.dart';导入'package:flare_flutter/flare_actor.dart';导入'package:flutter/rendering.dart';导入'dart:async';导入'dart:math';导入'dart:ui';导入'package:flare_flutter/flare.dart';导入'package:flare_dart/math/mat2d.dart';导入'package:flare_dart/math/vec2d.dart';导入'package:flare_flutter/flare_controls.dart';//改编了以下有用的功能://https://github.com/flutter/flutter/blob/master/packages/flutter/test/material/text_field_test.dart//返回第一个可编辑的渲染RenderEditable findRenderEditable(RenderObject root){RenderEditable renderEditable;void recursiveFinder(RenderObject child){如果(子项是RenderEditable){renderEditable = child;返回;}child.visitChildren(recursiveFinder);}root.visitChildren(recursiveFinder);返回renderEditable;}列表< TextSelectionPoint>全球化(Iterable< TextSelectionPoint>点,RenderBox框){返回点.map< TextSelectionPoint>(((TextSelectionPoint点){返回TextSelectionPoint(box.localToGlobal(point.point),点方向);}).toList();}偏移量getCaretPosition(RenderBox box){最终的RenderEditable renderEditable = findRenderEditable(box);如果(!renderEditable.hasFocus){返回null;}最终List< TextSelectionPoint>端点= globalize(renderEditable.getEndpointsForSelection(renderEditable.selection),renderEditable,);返回端点[0] .point + const Offset(0.0,-2.0);}SigninButton类扩展了StatelessWidget {最终的Widget子对象;最终渐变梯度;最终双倍宽度;最终双倍高度;最终功能onPressed;const SigninButton({关键@需要this.child,这个梯度this.width = double.infinity,this.height = 50.0,this.onPressed,}):super(key:key);@override窗口小部件build(BuildContext context){返回容器(宽度:宽度,高度:50.0,装饰:BoxDecoration(borderRadius:BorderRadius.circular(25.0),渐变:LinearGradient(颜色:<颜色> [颜色来自RGBO(160,92,147,1.0),来自RGBO的颜色(115、82、135、1.0)],)),子代:材质(颜色:Colors.transparent,子:InkWell(onTap:onPressed,孩子:中心(孩子:孩子,)),),);}}TeddyController类扩展了FlareControls {//存储对我们的面部控制节点(在Flare中的"ctrl_look"节点)的引用ActorNode _faceControl;//存储矩阵,以将全局Flutter坐标转换为Flare世界坐标.Mat2D _globalToFlareWorld = Mat2D();//在Flutter全局坐标中插入符号.Vec2D _caretGlobal = Vec2D();//喇叭在Flare世界坐标中.Vec2D _caretWorld = Vec2D();//将原点存储在世界和本地变换空间中.Vec2D _faceOrigin = Vec2D();Vec2D _faceOriginLocal = Vec2D();bool _hasFocus = false;//如此多的像素向前凝视.静态const double _projectGaze = 60.0;字符串_password;@override布尔前进(FlutterActorArtboard画板,已消失两次){超前(画板,已过去);Vec2D targetTranslation;如果(_hasFocus){//在Flare世界空间中插入符号.Vec2D.transformMat2D(_caretWorld,_caretGlobal,_globalToFlareWorld);//为了使它更有趣,我们还将添加一个正弦垂直偏移._caretWorld [1] + =sin(新的DateTime.now().millisecondsSinceEpoch/300.0)* 70.0;//计算方向向量.Vec2D toCaret = Vec2D.subtract(Vec2D(),_caretWorld,_faceOrigin);Vec2D.normalize(toCaret,toCaret);Vec2D.scale(toCaret,toCaret,_projectGaze);//计算使我们进入人脸"ctrl_face"空间的变换.Mat2D toFaceTransform = Mat2D();如果(Mat2D.invert(toFaceTransform,_faceControl.parent.worldTransform)){//将toCaret放到本地空间中,请注意,我们正在使用方向向量//不是翻译,因此无需翻译即可转换Vec2D.transformMat2(toCaret,toCaret,toFaceTransform);//我们最后的"ctrl_face"位置是原始人脸平移加上此方向向量targetTranslation = Vec2D.add(Vec2D(),toCaret,_faceOriginLocal);}} 别的 {targetTranslation = Vec2D.clone(_faceOriginLocal);}//我们可以将_faceControl.translation设置为targetTranslation,但我们希望将其平滑地动画化为该目标//因此,为了保持速度,无论帧速率如何,我们都以经过时间的比例向其插值.Vec2D差异=Vec2D.subtract(Vec2D(),targetTranslation,_faceControl.translation);Vec2D frameTranslation = Vec2D.add(Vec2D(),_faceControl.translation,Vec2D.scale(diff,diff,min(1.0,经过* 5.0)));_faceControl.translation = frameTranslation;返回true;}//获取`ctrl_face`节点的引用,并存储其原始翻译的副本.@override无效的initialize(FlutterActorArtboard画板){super.initialize(画板);_faceControl = artboard.getNode("ctrl_face");如果(_faceControl!= null){_faceControl.getWorldTranslation(_faceOrigin);Vec2D.copy(_faceOriginLocal,_faceControl.translation);}play("idle");}onCompleted(字符串名称){play("idle");}//当视图转换更改时由[FlareActor]调用.//更新将Global-Flutter坐标转换为Flare-World-coordinates的矩阵.@overridevoid setViewTransform(Mat2D viewTransform){Mat2D.invert(_globalToFlareWorld,viewTransform);}//将[Offset]转换为[Vec2D].//如果未提供插入记号,请降低[_hasFocus]标志.void lookAt(Offset caret){如果(插入符号==空){_hasFocus = false;返回;}_caretGlobal [0] = caret.dx;_caretGlobal [1] = caret.dy;_hasFocus = true;}void setPassword(字符串值){_password =值;}bool _isCoveringEyes = false;coverEyes(cover){如果(_isCoveringEyes == Cover){返回;}_isCoveringEyes =封面;如果(封面){play("hands_up");} 别的 {play("hands_down");}}无效的commitPassword(){如果(_password =="bears"){play("success");} 别的 {play("fail");}}}typedef void CaretMoved(Offset globalCaretPosition);typedef void TextChanged(String text);//Helper小部件以跟踪插入符号的位置.TrackingTextInput类扩展了StatefulWidget {TrackingTextInput({键,this.onCaretMoved,this.onTextChanged,this.hint,this.label,this.isObscured = false}):super(key:键);CaretMoved上的最终CaretMoved;最后的TextChanged onTextChanged;最终的字符串提示;最终的String标签;最终布尔值模糊不清;@override_TrackingTextInputState createState()=>_TrackingTextInputState();}类_TrackingTextInputState扩展了State< TrackingTextInput>{最终的GlobalKey _fieldKey = GlobalKey();最后的TextEditingController _textController = TextEditingController();计时器_debounceTimer;@overrideinitState(){_textController.addListener((){//我们对监听器进行去抖动,因为有时在监听器之后插入符号的位置会更新//这可以确保我们获得准确的插入符位置.如果(_debounceTimer?.isActive ?? false)_debounceTimer.cancel();_debounceTimer =计时器(常量持续时间(毫秒:100),(){如果(_fieldKey.currentContext!= null){//在字段中找到可编辑的渲染.最终的RenderObject fieldBox =_fieldKey.currentContext.findRenderObject();偏移caretPosition = getCaretPosition(fieldBox);如果(widget.onCaretMoved!= null){widget.onCaretMoved(caretPosition);}}});如果(widget.onTextChanged!= null){widget.onTextChanged(_textController.text);}});super.initState();}@override窗口小部件build(BuildContext context){返回填充(填充:const EdgeInsets.only(底部:20.0),子级:TextFormField(装饰:InputDecoration(hintText:widget.hint,labelText:widget.label,),键:_fieldKey,控制器:_textController,obscureText:widget.isObscured,验证者:(值){}),);}}void main()=>runApp(MyApp());MyApp类扩展了StatelessWidget {@override窗口小部件build(BuildContext context){返回MaterialApp(标题:"Flutter演示",主题:ThemeData(primarySwatch:Colors.blue,),主页:MyHomePage(标题:"Flutter演示首页"),);}}类MyHomePage扩展了StatefulWidget {MyHomePage({Key key,this.title}):超级(key:key);最终的字符串标题;@override_MyHomePageState createState()=>_MyHomePageState();}类_MyHomePageState扩展State< MyHomePage>{TeddyController _teddyController;@overrideinitState(){_teddyController = TeddyController();super.initState();}@override窗口小部件build(BuildContext context){EdgeInsets devicePadding = MediaQuery.of(context).padding;返回脚手架(backgroundColor:Color.fromRGBO(93,142,155,1.0),身体:容器(子代:Stack(子代:< Widget> [定位(最高:50左:0,右:0,子代:集装箱(高度:200,填充:const EdgeInsets.only(左:30.0,右:30.0),子代:FlareActor(资产/Teddy.flr",shouldClip:否,对齐方式:Alignment.bottomCenter,适合:BoxFit.contain,控制器:_teddyController,)))定位(子级:SingleChildScrollView(填充:EdgeInsets.only(左:20.0,右:20.0,顶部:devicePadding.top + 150.0),子:列(mainAxisAlignment:MainAxisAlignment.center,crossAxisAlignment:CrossAxisAlignment.center,子代:< Widget> [容器(装饰:BoxDecoration(颜色:Colors.white,borderRadius:BorderRadius.all(Radius.circular(25.0))),孩子:填充(填充:const EdgeInsets.all(30.0),子代:表格(子:列(mainAxisAlignment:MainAxisAlignment.center,crossAxisAlignment:CrossAxisAlignment.center,子代:< Widget> [TrackingTextInput(标签:电子邮件",提示:您的电子邮件地址是什么?",onCaretMoved :(偏移符号){_teddyController.lookAt(caret);}),TrackingTextInput(标签:密码",提示:尝试'熊'...",isObscured:是的,onCaretMoved :(偏移符号){_teddyController.coverEyes(caret!= null);_teddyController.lookAt(null);},onTextChanged :(字符串值){_teddyController.setPassword(value);},),登录按钮(子级:Text("Sign In",样式:TextStyle(fontFamily:"RobotoMedium",字号:16颜色:Colors.white)),onPressed:(){_teddyController.submitPassword();})],)),)),])),),],)),);}}
I just succeed flutter-flare for my flutter widgets
but I could not find a example to use it for background (Container)
is this possible?
解决方案
Use official example code https://github.com/2d-inc/Flare-Flutter/tree/stable/example/teddy/lib
with a little modification, you can use Stack
and Positioned
and change attribute value like top
code snippet
return Scaffold(
backgroundColor: Color.fromRGBO(93, 142, 155, 1.0),
body: Container(
child: Stack(
children: <Widget>[
Positioned(
top: 50,
left:0,
right: 0,
child: Container(
height: 200,
padding:
const EdgeInsets.only(left: 30.0, right: 30.0),
child: FlareActor(
"assets/Teddy.flr",
shouldClip: false,
alignment: Alignment.bottomCenter,
fit: BoxFit.contain,
controller: _teddyController,
)),),
Positioned(
child: SingleChildScrollView(
working demo
full code
import 'package:flutter/material.dart';
import 'package:flare_flutter/flare_actor.dart';
import 'package:flutter/rendering.dart';
import 'dart:async';
import 'dart:math';
import 'dart:ui';
import 'package:flare_flutter/flare.dart';
import 'package:flare_dart/math/mat2d.dart';
import 'package:flare_dart/math/vec2d.dart';
import 'package:flare_flutter/flare_controls.dart';
// Adapted these helpful functions from:
// https://github.com/flutter/flutter/blob/master/packages/flutter/test/material/text_field_test.dart
// Returns first render editable
RenderEditable findRenderEditable(RenderObject root) {
RenderEditable renderEditable;
void recursiveFinder(RenderObject child) {
if (child is RenderEditable) {
renderEditable = child;
return;
}
child.visitChildren(recursiveFinder);
}
root.visitChildren(recursiveFinder);
return renderEditable;
}
List<TextSelectionPoint> globalize(
Iterable<TextSelectionPoint> points, RenderBox box) {
return points.map<TextSelectionPoint>((TextSelectionPoint point) {
return TextSelectionPoint(
box.localToGlobal(point.point),
point.direction,
);
}).toList();
}
Offset getCaretPosition(RenderBox box) {
final RenderEditable renderEditable = findRenderEditable(box);
if (!renderEditable.hasFocus) {
return null;
}
final List<TextSelectionPoint> endpoints = globalize(
renderEditable.getEndpointsForSelection(renderEditable.selection),
renderEditable,
);
return endpoints[0].point + const Offset(0.0, -2.0);
}
class SigninButton extends StatelessWidget {
final Widget child;
final Gradient gradient;
final double width;
final double height;
final Function onPressed;
const SigninButton({
Key key,
@required this.child,
this.gradient,
this.width = double.infinity,
this.height = 50.0,
this.onPressed,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: width,
height: 50.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25.0),
gradient: LinearGradient(
colors: <Color>[
Color.fromRGBO(160, 92, 147, 1.0),
Color.fromRGBO(115, 82, 135, 1.0)
],
)),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onPressed,
child: Center(
child: child,
)),
),
);
}
}
class TeddyController extends FlareControls {
// Store a reference to our face control node (the "ctrl_look" node in Flare)
ActorNode _faceControl;
// Storage for our matrix to get global Flutter coordinates into Flare world coordinates.
Mat2D _globalToFlareWorld = Mat2D();
// Caret in Flutter global coordinates.
Vec2D _caretGlobal = Vec2D();
// Caret in Flare world coordinates.
Vec2D _caretWorld = Vec2D();
// Store the origin in both world and local transform spaces.
Vec2D _faceOrigin = Vec2D();
Vec2D _faceOriginLocal = Vec2D();
bool _hasFocus = false;
// Project gaze forward by this many pixels.
static const double _projectGaze = 60.0;
String _password;
@override
bool advance(FlutterActorArtboard artboard, double elapsed) {
super.advance(artboard, elapsed);
Vec2D targetTranslation;
if (_hasFocus) {
// Get caret in Flare world space.
Vec2D.transformMat2D(_caretWorld, _caretGlobal, _globalToFlareWorld);
// To make it more interesting, we'll also add a sinusoidal vertical offset.
_caretWorld[1] +=
sin(new DateTime.now().millisecondsSinceEpoch / 300.0) * 70.0;
// Compute direction vector.
Vec2D toCaret = Vec2D.subtract(Vec2D(), _caretWorld, _faceOrigin);
Vec2D.normalize(toCaret, toCaret);
Vec2D.scale(toCaret, toCaret, _projectGaze);
// Compute the transform that gets us in face "ctrl_face" space.
Mat2D toFaceTransform = Mat2D();
if (Mat2D.invert(toFaceTransform, _faceControl.parent.worldTransform)) {
// Put toCaret in local space, note we're using a direction vector
// not a translation so transform without translation
Vec2D.transformMat2(toCaret, toCaret, toFaceTransform);
// Our final "ctrl_face" position is the original face translation plus this direction vector
targetTranslation = Vec2D.add(Vec2D(), toCaret, _faceOriginLocal);
}
} else {
targetTranslation = Vec2D.clone(_faceOriginLocal);
}
// We could just set _faceControl.translation to targetTranslation, but we want to animate it smoothly to this target
// so we interpolate towards it by a factor of elapsed time in order to maintain speed regardless of frame rate.
Vec2D diff =
Vec2D.subtract(Vec2D(), targetTranslation, _faceControl.translation);
Vec2D frameTranslation = Vec2D.add(Vec2D(), _faceControl.translation,
Vec2D.scale(diff, diff, min(1.0, elapsed * 5.0)));
_faceControl.translation = frameTranslation;
return true;
}
// Fetch references for the `ctrl_face` node and store a copy of its original translation.
@override
void initialize(FlutterActorArtboard artboard) {
super.initialize(artboard);
_faceControl = artboard.getNode("ctrl_face");
if (_faceControl != null) {
_faceControl.getWorldTranslation(_faceOrigin);
Vec2D.copy(_faceOriginLocal, _faceControl.translation);
}
play("idle");
}
onCompleted(String name) {
play("idle");
}
// Called by [FlareActor] when the view transform changes.
// Updates the matrix that transforms Global-Flutter-coordinates into Flare-World-coordinates.
@override
void setViewTransform(Mat2D viewTransform) {
Mat2D.invert(_globalToFlareWorld, viewTransform);
}
// Transform the [Offset] into a [Vec2D].
// If no caret is provided, lower the [_hasFocus] flag.
void lookAt(Offset caret) {
if (caret == null) {
_hasFocus = false;
return;
}
_caretGlobal[0] = caret.dx;
_caretGlobal[1] = caret.dy;
_hasFocus = true;
}
void setPassword(String value) {
_password = value;
}
bool _isCoveringEyes = false;
coverEyes(cover) {
if (_isCoveringEyes == cover) {
return;
}
_isCoveringEyes = cover;
if (cover) {
play("hands_up");
} else {
play("hands_down");
}
}
void submitPassword() {
if (_password == "bears") {
play("success");
} else {
play("fail");
}
}
}
typedef void CaretMoved(Offset globalCaretPosition);
typedef void TextChanged(String text);
// Helper widget to track caret position.
class TrackingTextInput extends StatefulWidget {
TrackingTextInput(
{Key key, this.onCaretMoved, this.onTextChanged, this.hint, this.label, this.isObscured = false})
: super(key: key);
final CaretMoved onCaretMoved;
final TextChanged onTextChanged;
final String hint;
final String label;
final bool isObscured;
@override
_TrackingTextInputState createState() => _TrackingTextInputState();
}
class _TrackingTextInputState extends State<TrackingTextInput> {
final GlobalKey _fieldKey = GlobalKey();
final TextEditingController _textController = TextEditingController();
Timer _debounceTimer;
@override
initState() {
_textController.addListener(() {
// We debounce the listener as sometimes the caret position is updated after the listener
// this assures us we get an accurate caret position.
if (_debounceTimer?.isActive ?? false) _debounceTimer.cancel();
_debounceTimer = Timer(const Duration(milliseconds: 100), () {
if (_fieldKey.currentContext != null) {
// Find the render editable in the field.
final RenderObject fieldBox =
_fieldKey.currentContext.findRenderObject();
Offset caretPosition = getCaretPosition(fieldBox);
if (widget.onCaretMoved != null) {
widget.onCaretMoved(caretPosition);
}
}
});
if (widget.onTextChanged != null) {
widget.onTextChanged(_textController.text);
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: TextFormField(
decoration: InputDecoration(
hintText: widget.hint,
labelText: widget.label,
),
key: _fieldKey,
controller: _textController,
obscureText: widget.isObscured,
validator: (value) {}),
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TeddyController _teddyController;
@override
initState() {
_teddyController = TeddyController();
super.initState();
}
@override
Widget build(BuildContext context) {
EdgeInsets devicePadding = MediaQuery.of(context).padding;
return Scaffold(
backgroundColor: Color.fromRGBO(93, 142, 155, 1.0),
body: Container(
child: Stack(
children: <Widget>[
Positioned(
top: 50,
left:0,
right: 0,
child: Container(
height: 200,
padding:
const EdgeInsets.only(left: 30.0, right: 30.0),
child: FlareActor(
"assets/Teddy.flr",
shouldClip: false,
alignment: Alignment.bottomCenter,
fit: BoxFit.contain,
controller: _teddyController,
)),),
Positioned(
child: SingleChildScrollView(
padding: EdgeInsets.only(
left: 20.0, right: 20.0, top: devicePadding.top + 150.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.all(Radius.circular(25.0))),
child: Padding(
padding: const EdgeInsets.all(30.0),
child: Form(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TrackingTextInput(
label: "Email",
hint: "What's your email address?",
onCaretMoved: (Offset caret) {
_teddyController.lookAt(caret);
}),
TrackingTextInput(
label: "Password",
hint: "Try 'bears'...",
isObscured: true,
onCaretMoved: (Offset caret) {
_teddyController.coverEyes(caret != null);
_teddyController.lookAt(null);
},
onTextChanged: (String value) {
_teddyController.setPassword(value);
},
),
SigninButton(
child: Text("Sign In",
style: TextStyle(
fontFamily: "RobotoMedium",
fontSize: 16,
color: Colors.white)),
onPressed: () {
_teddyController.submitPassword();
})
],
)),
)),
])),
),
],
)),
);
}
}
这篇关于Flutter Flare,Rive,它可以用于背景吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文