【动画 widget】Flutter AnimatedWidget

309 阅读2分钟

大家好,我是 17,今天的每日 widget 为大家介绍 AnimatedWidget。

Flutter AnimatedWidget 是一个 StatefulWidget,它的作用是当 listenalbe 变化的时候,rebuild Widget 让 UI 也随着变化。

源码分析

构造函数

  const AnimatedWidget({
    super.key,
    required this.listenable,
  });
}

AnimatedWidget 是一个抽象类,所以即使他是一个 widget,也不能直接用。 参数里面并没有 child,所有的 UI 都在 build 方法里完成。

class _AnimatedState extends State<AnimatedWidget> {
  @override
  void initState() {
    super.initState();
    widget.listenable.addListener(_handleChange);
  }
  ... 省略 didUpdateWidget,dispose
  
  void _handleChange() {
    setState(() {
      // The listenable's state is our build state, and it changed already.
    });
  }

  @override
  Widget build(BuildContext context) => widget.build(context);
}

逻辑在 state 里面。_AnimatedState 就做了一件事:当 listenable(通常是 Animation 对象)变化的时候,rebuild Widget。

通过代码可以知道,AnimatedWidget 帮我们把监听的工作抽象出来,让我们再写动画的不时候省去了监听的代码。这也是抽象类的意义,把公共的部分抽离出来,减少子类的工作量。

使用 AnimatedWidget

使用 AnimatedWidget 很简单的,只需要给他一个 listenable 对象。

举一个简单的例子,不断放大的正方形。

class AnimatedBox extends AnimatedWidget {
  const AnimatedBox({Key? key, required Animation<double> listenalbe})
      : super(key: key, listenable: listenalbe);
  Animation get animation => listenable as Animation<double>;
  @override
  Widget build(BuildContext context) {
    return Container(
      width: animation.value * 100,
      height: animation.value * 100,
      color: Colors.blue[200],
    );
  }
}
class _MyAnimationState extends State<MyAnimation>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    _controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 1))
          ..repeat();
    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            body: Center(
                child: AnimatedBox(
        listenalbe: _controller.view,
    ))));
  }
}

要注意的是 _controller 需要及时 dispose。使用 AnimatedWidget ,让我们不光省去了监听的事,还省去了 State 类。代码省去了不少,但是使用起来还是有点麻烦,还得创建 Controller,使用 ImplicitlyAnimatedWidget 可以解决这个问题。

性能优化

每当做动画效果的时候,都要认真考虑性能的问题,因为动画的刷新频率太高了,稍有不慎就会造成卡顿。有两种办法可以优化性能

我们先加一个 MyWidget,方便测试

class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    print('build');
    return Container(
      color: Colors.blue,
      width: 50,
      height: 50,
    );
  }
}

使用 const 关键字

class AnimatedBox extends AnimatedWidget {
  const AnimatedBox({Key? key, required Animation<double> listenalbe})
      : super(key: key, listenable: listenalbe);
  Animation get animation => listenable as Animation<double>;
  @override
  Widget build(BuildContext context) {
    return Container(
      width: animation.value * 100,
      height: animation.value * 100,
      color: Colors.blue[200],
      // 新增加的代码
      child:const MyWdiget()
    );
  }
}

加上 const 关键字后,MyWdiget 只 build 一次。

去掉 const 关键字,再看下效果。

通过 child 传进来。


class AnimatedBox extends AnimatedWidget {
  const AnimatedBox({Key? key, required Animation<double> listenalbe,required this.child})
      : super(key: key, listenable: listenalbe);
  final Widget child;
  Animation get animation => listenable as Animation<double>;
  @override
  Widget build(BuildContext context) {
    return Container(
      width: animation.value * 100,
      height: animation.value * 100,
      color: Colors.blue[200],
      child: child
    );
  }
}
AnimatedBox(
      child: MyWidget(),
      listenalbe: _controller,
 );

把 MyWidget() 通过 child 的方式传到 AnimatedBox 里面,即使不加 const 关键字,MyWidget 也只 build 一次。