大家好,我是 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 一次。