阅读 4516

Flutter | 求求你们了,切换 Widget 的时候加上动画吧

平时我们在切换 Widget 的时候是怎样的呢?

有没有动画效果?是不是直接改变了一个 Widget?

类似于这样的:

如果是的话,那么今天所说的 Widget,绝对符合你的口味。

那如何在 Flutter 当中切换 Widget 的时候加上特效?完成这样的效果?

AnimatedSwitcher 了解一下。

AnimatedSwitcher

官方介绍

话不多说,功能我们已经了解,再来看一下官方的介绍:

A widget that by default does a FadeTransition between a new widget and the widget previously set on the AnimatedSwitcher as a child.

If they are swapped fast enough (i.e. before duration elapses), more than one previous child can exist and be transitioning out while the newest one is transitioning in.

If the "new" child is the same widget type and key as the "old" child, but with different parameters, then AnimatedSwitcher will not do a transition between them, since as far as the framework is concerned, they are the same widget and the existing widget can be updated with the new parameters. To force the transition to occur, set a Key on each child widget that you wish to be considered unique (typically a ValueKey on the widget data that distinguishes this child from the others).

大致意思就是:

默认情况下是执行透明度的动画。

如果交换速度足够快,则存在多个子级,但是在新子级传入的时候将它移除。

如果新 Widget 和 旧 Widget 的类型和键相同,但是参数不同,那么也不会进行转换。如果想要进行转换,那么要添加一个 Key。

构造函数

再来看构造函数,来确定如何使用:

const AnimatedSwitcher({
  Key key,
  this.child,
  @required this.duration,
  this.reverseDuration,
  this.switchInCurve = Curves.linear,
  this.switchOutCurve = Curves.linear,
  this.transitionBuilder = AnimatedSwitcher.defaultTransitionBuilder,
  this.layoutBuilder = AnimatedSwitcher.defaultLayoutBuilder,
}) : assert(duration != null),
assert(switchInCurve != null),
assert(switchOutCurve != null),
assert(transitionBuilder != null),
assert(layoutBuilder != null),
super(key: key);
复制代码

来解释一下每个参数:

  1. child:不用多说
  2. duration:动画持续时间
  3. reverseDuration:从新的 Widget 到旧的 Widget 动画持续时间,如果不设置则为 duration 的值
  4. switchInCurve:动画效果
  5. switchOutCurve:同上
  6. transitionBuilder:设置一个新的转换动画
  7. layoutBuilder:包装新旧 Widget 的组件,默认是一个 Stack

其中必要参数就是一个 duration,那既然知道如何使用了,那就开撸。

简单例子

前面我们看的图,就是在对 AppBar上的 actions 进行操作,

其实这个例子在实际开发当中经常存在,肯定要删除一些东西的嘛,然后选中了以后批量删除。

那这里也不多说,直接上代码,然后解释:

class _AnimatedSwitcherPageState extends State<AnimatedSwitcherPage> {
  IconData _actionIcon = Icons.delete;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('AnimatedSwitcherPage'),
          actions: <Widget>[
            AnimatedSwitcher(
              transitionBuilder: (child, anim){
                return ScaleTransition(child: child,scale: anim);
              },
              duration: Duration(milliseconds: 300),
              child: IconButton(
                  key: ValueKey(_actionIcon),
                  icon: Icon(_actionIcon),
                  onPressed: () {
                    setState(() {
                      if (_actionIcon == Icons.delete)
                        _actionIcon = Icons.done;
                      else
                        _actionIcon = Icons.delete;
                    });
                  }),
            )
          ],
        ),
        body: Container());
  }
}
复制代码

我们定义的是一个 StatefulWidget,因为在切换 Widget 的时候要调用 setState()

下面来说一下整个流程:

  1. 首先定义好我们初始化的 Icon的数据为 Icons.delete
  2. AppBaractions 里面加入 AnimatedSwitcher
  3. 设置 transitionBuilder 为 缩放动画 ScaleTransition
  4. AnimatedSwitcher 的 child 为 IconButton
  5. 因为前面官方文档说过,如果 Widget 类型一样,只是数据不一样,那么想要动画,就必须添加 Key。
  6. 所以我们给 IconButton 添加了一个 ValueKey,值就为定义好的 IconData
  7. 最后在点击事件中切换两个 Icon 就完成了

最后再看一下效果:

总结

使用该控件最应该注意的点就是 Key 的问题,一定要记住:

如果新 Widget 和 旧 Widget 的类型和键相同,但是参数不同,那么也不会进行转换。如果想要进行转换,那么要添加一个 Key。

完整代码已经传至GitHub:github.com/wanglu1209/…