如何在flutter中把几个动画放到一个小部件中?

问题描述 投票:0回答:1

我想把几个动画放到一个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中.

因此,这里的问题是:

  1. 这是唯一的方法来执行几个动画到1个widget吗?
  2. 如果第一个问题中没有,什么将是更好的(或最好的)方式来执行这个?
flutter flutter-animation
1个回答
1
投票

好吧,我觉得没有办法改善这种情况,因为...。Flutter还不像Android Studio那么流行和稳定,但我就是不知怎么了 改善 这种情况下,通过手动添加我自己的类,名为 AnimationManager. 我知道代码还是很复杂的,而且当我为每个方向放不同的动画时,就会变得更加复杂(这让我很沮丧...)。但是 至少比在一个widget上放很多bool要好,而且更好看。我还是希望有人能帮我改进,但我还是要举个例子。

与其为每个widget添加一堆bools,不如用这个 AnimationManager 可以通过以下方式设置动画 int 变量。让我们假设我们想把动画放在关于 Duration, TransformOpacity. 我没有测试为许多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 是可以调用的。

Multiple animation

© www.soinside.com 2019 - 2024. All rights reserved.