刚看到这个 widget 的时候,有点奇怪,已经有了 AnimatedWidget 和 ImplicitlyAnimatedWidget,感觉已经无所不能了啊,怎么还有个 AnimatedSize。存在肯定有他的价值,让我们来一探究竟吧。
可以这样给 AnimatedSize 下定义:一个有动画能力的布局 widget。AnimatedSize 和 AnimatedWidget、 ImplicitlyAnimatedWidget 相比,是完全不同的 widget。AnimatedSize 的动画是在 renderObject 实现的,效率更高。动画是由 child 驱动的,而不是由外界通过参数指定。
动画能力是附加的,本质上 AnimateSize 是一个布局 widget,所以第一步,按布局三板斧来研究。我们通过查看源码来研究下 child 的 constrains,AnimatedSize 的 size 和摆放 child
child 的 constrains
void performLayout() { _lastValue = _controller.value; _hasVisualOverflow = false; final BoxConstraints constraints = this.constraints; if (child == null || constraints.isTight) { _controller.stop(); size = _sizeTween.begin = _sizeTween.end = constraints.smallest; _state = RenderAnimatedSizeState.start; child?.layout(constraints); return; } child!.layout(constraints, parentUsesSize: true); ... 后面省略 复制代码
在把 constrains 赋值给child 之前, AnimatedSize 并没有对 constrains 做修改,直接透传给了chid
AnimatedSize 的 size
- constrains 是 tight,size = constraints.smallest。
- constrains 是 loose, size 在 constrains 范围内取 childSize 的大小。
如果 childSize 发生变化引发了动画, size 在 constrains 的范围取 _animatedSize
。
_animatedSize
也就是动画过程中某一时间点上的值。
Size? get _animatedSize { return _sizeTween.evaluate(_animation); } 复制代码
摆放 child
只有 size 和 childSize 不一样的时候才能摆放 child。
只有一个情况下 size 比 childSize 大。当 child size 从大变小的时候,child 是直接没有过渡的直接变小,AnimatedSize 要执行动画,所以是慢慢变小,这个时候就会出现 size 比 childSize 大的情况。
如何摆放 child 是 alignment 参数控制的。
使用 AnimatedSize
百说不如一练。我们看一个例子。就是一个简单的 box,点击它会变大变小。
double _size = 250.0; bool _large = false; void _updateSize() { setState(() { _size = _large ? 250.0 : 100.0; _large = !_large; }); } 复制代码
Center( child: GestureDetector( onTap: () => _updateSize(), child: Container( color: Colors.amberAccent, child: AnimatedSize( alignment: Alignment.center, curve: Curves.easeIn, duration: const Duration(seconds: 1), child: Container( width: _size, height: _size, color: Colors.green, )), ))) 复制代码
黄色代表 AnimatedSize size,绿色代表 childSize。你可能会有疑问,为什么变大的时候,child 和 parent 一起变大,而变小的时候,child 直接变小,而 parent 慢慢变小呢。其实这只是假象,真实情况都是 child 直接变大变小,parent 慢慢变大变小。变大的时候因为 AnimatedSize clip child ,把 child 裁剪了,所以感觉上好像和 AnimatedSize 一样大。
做个实验 加上 clipBehavior: Clip.none
看看效果,你会发现,变在的时候也是突然变大了。
AnimatedSize( // 加上这句 clipBehavior: Clip.none, ... 复制代码
再试着修改下 alignemnt alignment: Alignment.center 改为alignment: Alignment.bottomRight,
,效果如下
总之, AnimatedWidget 和 ImplicitlyAnimatedWidget 的动画是由外面传参进行控制,而 AnimatedSize 是由内部分的 child 的 size 的变化引发动画过程,AnimatedSize 的动画的效率更高。