【Flutter 组件集录】AnimatedContainer | 8 月更文挑战

1,628 阅读4分钟
前言:

这是我参与8月更文挑战的第 15 天,活动详情查看:8月更文挑战。为应掘金的八月更文挑战,我准备在本月挑选 31 个以前没有介绍过的组件,进行全面分析和属性介绍。这些文章将来会作为 Flutter 组件集录 的重要素材。希望可以坚持下去,你的支持将是我最大的动力~

本系列组件文章列表
1.NotificationListener2.Dismissible3.Switch
4.Scrollbar5.ClipPath6.CupertinoActivityIndicator
7.Opacity8.FadeTransition9. AnimatedOpacity
10. FadeInImage11. Offstage12. TickerMode
13. Visibility14. Padding15. AnimatedContainer
16.CircleAvatar17.PhysicalShape18.Divider
19.Flexible、Expanded 和 Spacer 20.Card

一、认识 AnimatedContainer 组件

Container 组件 一文中,我们知道它存在的价值是:整合如下 8 个单子组件,这样通过一个 Container 完成多种功能。可以避免在使用是层层嵌套,简化代码结构。而 AnimatedContainer 就相当于一个加强版的 Container ,可以让其中的各个属性在变化时具有动画效果


1.AnimatedContainer 基本信息

AnimatedContainer 是一个隐式动画组件,和之前介绍过的 AnimatedOpacity 是同类,这种动画组件,只需要更改属性 + 重新构建 就可以实现动画。这类的动画组件,都有一些共同的属性,如 duration 表示动画时长、curve 表示动画曲线、onEnd 表示动画结束的回调。

下面可以对比一下 AnimatedContainerContainer 的属性,可以看出基本是一模一样的。也就是说了解 Container 属性的用法之后,其实 AnimatedContainer 自然也就会了。两者的区别只在于:Container 在属性变化重构后,是直接变换,而 AnimatedContainer 是动画渐变。


2. AnimatedContainer 组件的使用

我们先通过一个简单的例子看一下 AnimatedContainer 的使用方式。如下,当点击按钮时,通过 _changeSize 方法改变宽高数值,并重建组件。可以看出 AnimatedContainer 尺寸变化会有动画效果。想一下,如果这里使用 Container 组件,那么将会直接改变到对应数值,就比较生硬。

class AnimatedContainerDemo extends StatefulWidget {
  @override
  _AnimatedContainerDemoState createState() => _AnimatedContainerDemoState();
}

class _AnimatedContainerDemoState extends State<AnimatedContainerDemo> {
  double _width = 180;
  double _height = 120;

  @override
  Widget build(BuildContext context) {
    return Wrap(
      direction: Axis.vertical,
      crossAxisAlignment: WrapCrossAlignment.center,
      children: <Widget>[
        ElevatedButton(
          child: const Text('更新宽高'),
          onPressed: _changeSize,
        ),
        const SizedBox(height: 10,),
        buildAnimatedContainer()
      ],
    );
  }

  Widget buildAnimatedContainer() => AnimatedContainer(
        duration: const Duration(seconds: 1),
        curve: Curves.fastOutSlowIn,
        height: _height,
        color: Colors.black12,
        width: _width,
        onEnd: onEnd,
      );

  void onEnd() {
    print('End');
  }
  
  void _changeSize() {
    setState(() {
      _width = _width == 100 ? 180 : 100;
      _height = _height == 100 ? 120 : 100;
    });
  }
}

3.AnimatedContainer 组件的其他属性

AnimatedContainer 的其他某些属性也可以进行动画,比如下面的 alignmentcolor 。和宽高一样,只需要改变属性值重构组件,就能进行动画。不需要处理动画器,这就是隐式动画的方便之处。

Alignment _alignment = Alignment.topLeft;
Color _color = Colors.orange;

Widget buildAnimatedContainer() => AnimatedContainer(
      duration: const Duration(seconds: 1),
      curve: Curves.fastOutSlowIn,
      height: 100,
      color: _color.withOpacity(0.2),
      width: 100,
      alignment: _alignment,
      onEnd: onEnd,
      child: FlutterLogo(),
    );

void _change() {
  setState(() {
    _alignment =
        _alignment == Alignment.center ? 
      Alignment.topLeft : Alignment.center;
    
    _color = _color == Colors.orange ? 
      Colors.purple : Colors.orange;
  });
}

我们也可以对 decoration 属性进行动画,如下的圆角和阴影装饰,也是只需要改变属性值重构组件 即可。

总的来说 AnimatedContainer 的属性意义和 Container 一模一样,只是前者在属性变化重构时会进行动画过渡到该值。这样会使变化显得不那么突兀,视觉上更好些。其他的属性参考 Container 组件 一文,大家可以自己试试改变这些属性的效果。


二、AnimatedContainer 的源码实现

AnimatedContainer 继承自 ImplicitlyAnimatedWidget ,这也就说明它和 AnimatedOpacity 是同族的,所以他们在源码实现上是类似的。


不同点在于AnimatedOpacity 是通过 FadeTransition 组件实现的动画渐变功能,该组件天生可以监听 Listenable 对象触发重绘。而 _AnimatedContainerState 继承自 AnimatedWidgetBaseState

AnimatedWidgetBaseState 中会监听动画控制器 controller ,通过 setState 对子组件进行 局部更新。其原理和 AnimatedBuilder 是一致的。


从状态类中维护的 XXXTween 可以看出能够进行动画的属性有哪些。从组件的构建中可以看出,本身还是使用了 Container 实现的。由于之前监听了动画,并执行setState ,那么在动画的每一帧,都会更新 Container 的属性配置信息,进行局部更新


最后,动画的开启和 AnimatedOpacity 一文中介绍的一样,比较他们都是 ImplicitlyAnimatedWidget 。动画器的维护都封装在 ImplicitlyAnimatedWidgetState 中:

StatefulWidget重新构建时,状态类是不会重新初始化的。而是触发 State#didUpdateWidget 来通知 Widget 配置的变更,在此可以处理一下组件更新的逻辑。可以看出,如下在 didUpdateWidget 中,会对配置进行对比,发生变化将会更新。最后 _controller 会执行 forward 进行动画。

AnimatedContainer 的使用方式到这里就介绍完毕,那本文到这里就结束了,谢谢观看,明天见~