前言:
这是我参与8月更文挑战的第 15 天,活动详情查看:8月更文挑战。为应掘金的八月更文挑战,我准备在本月挑选 31 个以前没有介绍过的组件,进行全面分析和属性介绍。这些文章将来会作为 Flutter 组件集录 的重要素材。希望可以坚持下去,你的支持将是我最大的动力~
一、认识 AnimatedContainer 组件
在 Container 组件 一文中,我们知道它存在的价值是:整合如下 8 个单子组件,这样通过一个 Container 完成多种功能。可以避免在使用是层层嵌套,简化代码结构。而 AnimatedContainer 就相当于一个加强版的 Container ,可以让其中的各个属性在变化时具有动画效果。
1.AnimatedContainer 基本信息
AnimatedContainer 是一个隐式动画组件,和之前介绍过的 AnimatedOpacity 是同类,这种动画组件,只需要更改属性 + 重新构建 就可以实现动画。这类的动画组件,都有一些共同的属性,如 duration 表示动画时长、curve 表示动画曲线、onEnd 表示动画结束的回调。
下面可以对比一下 AnimatedContainer 和 Container 的属性,可以看出基本是一模一样的。也就是说了解 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 的其他某些属性也可以进行动画,比如下面的 alignment 和 color 。和宽高一样,只需要改变属性值和重构组件,就能进行动画。不需要处理动画器,这就是隐式动画的方便之处。
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 的使用方式到这里就介绍完毕,那本文到这里就结束了,谢谢观看,明天见~