我想把几个动画放到一个widget里,但由于我是Flutter的初学者,我不知道我必须使用什么功能来执行这个。当一个widget只有一个动画的时候,就可以了。只要在字段中添加一个bool,用AnimatedContainer覆盖widget就可以了。像这样。
bool init = false; //Bool about animation
//Somewhere of codes...
child: AnimatedContainer(
transform: init ? matrixA : matrixB,
child: FlatButton(
child: Text("Press Me"),
onPressed: () {
//Change init to true
setState(() {
init = true;
});
}
) //FlatButton
) //AnimatedContainer
我可以用Flare来执行图标动画,也可以用Future.delayed()来执行懒惰动画(对不起,如果这种行为是不好的做法,请告诉我是否我做错了)。问题是一个widget一定要有几个动画,这样的widget大概会有3个。我曾经尝试过为一个widget先放两个动画。在我的应用中,我放了一个喇叭图标动画。在这个动画结束后,我让按钮显示出来,当用户点击这个按钮时,按钮就会出现。当用户点击这个按钮时,按钮将上升到特定的位置。由于按钮一开始必须在屏幕下方,我需要3个Matrix4变量。我试着用同样的方法把两个动画放到一个widget上,结果是这样的。
bool init = false; //This will make button show up
bool pressed = false; //This will make button go up
//Somewhere of codes...
children: [
FlareActor("assets/animation.flr",
animation: "anim",
callback: (name) {
//Notify flare animation is ended
//and make button show up by calling setState
if(name == "anim") {
this.setState(() {
init = true;
});
}
}), //FlareActor
AnimatedContainer(
transform: init ? pressed ? matrixA : matrixB : matrixC, //This is problem
duration: pressed ? Duration(milliseconds: 300) : Duration(millisecons: 150),
child:FlatButton(
child: Text("Press Me"),
onPressed: () {
//Change pressed to true to make it move up
setState(() {
pressed = true;
});
}
) //FlatButton
) //AnimatedContainer
]
我知道这些代码并不包含完整的信息(如矩阵A,矩阵B,矩阵C),但我想这已经足够让你理解我想要做的事情了。如果你需要更多关于代码的信息,你可以在评论中问我。我只是在字段中加入了2个bool,然后......它已经开始看起来很复杂了,尤其是AnimatedContainer中的变换参数。想象一下,每3个widgets会有4个bools,总共会有12个bools,这会让代码变得很乱,看起来很复杂。
你可能会问我为什么要在1个widget上放几个动画,其实,只是因为我想让app更有活力。活着而不是简单的换屏、对话或无聊的动画。当然,我搜索了一下,找到的是Stream和AnimatedBuilder。我不知道Stream是否可以帮助我,因为即使我使用了Stream,我也不能直接设置widgets的属性(位置,比例,不透明度)。只需要发送布尔值到监听器,然后调用setState(),就会变成和上面一样。AnimatedBuilder看起来不错,但我找到的所有帖子都是关于 为多个小部件添加动画,不 将多个动画放到一个widget中.
因此,这里的问题是:
好吧,我觉得没有办法改善这种情况,因为...。Flutter还不像Android Studio那么流行和稳定,但我就是不知怎么了 改善 这种情况下,通过手动添加我自己的类,名为 AnimationManager
. 我知道代码还是很复杂的,而且当我为每个方向放不同的动画时,就会变得更加复杂(这让我很沮丧...)。但是 至少比在一个widget上放很多bool要好,而且更好看。我还是希望有人能帮我改进,但我还是要举个例子。
与其为每个widget添加一堆bools,不如用这个 AnimationManager
可以通过以下方式设置动画 int
变量。让我们假设我们想把动画放在关于 Duration
, Transform
和 Opacity
. 我没有测试为许多widget添加许多动画,所以我真的还不知道它的性能。
class AnimationManager {
static const int DURATION = 0;
static const int TRANSFORM = 1;
static const int OPACITY = 2;
Map map = HashMap<int, List<int>>();
List du = new List<Duration>();
List tr = new List<Matrix4>();
List o = new List<double>();
void createIndex({@required int index}) {
List<int> data = map[index]; //Check if index is already created
if(data != null) {
throw Exception("Index $index already exists!");
}
data = [-1, -1, -1]; // Duration, Transform, Opacity
map[index] = data;
}
void addDuration({@required Duration duration}) {
du.add(duration); //Add Duration info to du
}
void addTransform({@required Matrix4 transform}) {
tr.add(transform); //Add Transform info to tr
}
void addOpacity({@required double opacity}) {
o.add(opacity); //Add Opacity info to o
}
void setDuration({@required int index, @required int mode}) {
//Set specific duration mode for specific index data
List data = map[index];
data[DURATION] = mode;
map[index] = data;
}
void setTransform({@required int index, @required int mode}) {
//Do same thing like Duration
}
void setOpacity({@required int index, @required int mode}) {
//Do same thing like Duration
}
Duration getDuration({@required int mode}) {
return mode < 0 ? Duration(milliseconds: 300) : du[mode]; //NOT SAFE
}
Matrix4 getTransform({@required int mode}) {
//Do same thing like getDuration
}
double getOpacity({@required int mode}) {
//Do same thing like getDuration
}
Widget build({@required int index, @required Widget child}) {
//Put child into AnimatedContainer with specific index data
List data = map[index];
return AnimatedContainer(
duration: getDuration(data[DURATION]),
transform: getTransform(data[TRANSFORM]),
child: child
);
}
Widget buildWithOpacity({@required int index, @required Widget child}) {
List data = map[index];
return AnimatedContainer(
duration: getDuration(data[DURATION]),
transform: getTransform(data[TRANSFORM]),
child: AnimatedOpacity(
duration: getDuration(data[DURATION]),
opacity: getOpacity(data[OPACITY]),
child: child
));
}
}
这段代码缺乏安全检查,但由于我只是给出概念,这就足够了。这个例子只能包含持续时间,变换,不透明度数据,但实际上我还添加了曲线,高度,宽度数据。它可以像这样使用
AnimationManager am = AnimationManager();
bool initialized = false;
@override Widget build(BuildContext context) {
if(!initialized) {
//Initialize data
am.addDuration(duration: Duration(seconds: 1));
am.addTransform(transform: matrixA);
am.addTransform(transform: matrixB);
am.addTransform(transform: matrixC);
am.addOpacity(opacity: 0);
am.addOpacity(opacity: 1);
am.createIndex(index: 0);
am.setDuration(index: 0, mode: 0);
am.setTransform(index: 0, mode: 0);
am.setOpacity(index: 0, mode: 1);
initialized = true;
}
//Somewhere of codes...
children: <Widget> [
am.buildWithOpacity(
index: 0 //Build AnimatedContainer with specified index data
child: FlatButton(
child: Text("Press me"),
onPressed: () {
setState() {
am.setTransform(index: 0, mode: 1);
am.setOpacity(index:0, mode: 0);
}
}
)
),
//Add more Widget which needs animation
]
}
如果里面有很多小部件,代码还是会很复杂,但我更喜欢这种方式,而不是把bool和做长长的1行代码,如 init ? pressed ? done ? matrixA : matrixB : matrixC : matrixD
等。这里唯一的问题是内存处理,因为如果我们使用了bool,那么这些 Matrix4
, Duration
、等会暂时由GC创建并收集,但现在 AnimationManager
保存所有动画数据。但还是比较好的,因为这种方式可以处理方向的变化,或者有不同方向布局的动画时的情况。更容易 比放bools更合适。下面的Gif图片是测试结果,使用 AnimationManager
在任何地方都可以控制它,如果 setState
是可以调用的。