内容简介:由于近期某些不可抗拒的原因,Flutter官网我们是打不开了。所以我们直接点开源码看吧,在 AnimatedList 类中的第一句话是:Creates a scrolling container that animates items when they are inserted or removed.
由于近期某些不可抗拒的原因,Flutter官网我们是打不开了。
所以我们直接点开源码看吧,在 AnimatedList 类中的第一句话是:
Creates a scrolling container that animates items when they are inserted or removed.
创建一个滚动容器,在插入或删除项目时为其设置动画。
再来看一下构造函数:
const AnimatedList({ Key key, @required this.itemBuilder, this.initialItemCount = 0, this.scrollDirection = Axis.vertical, this.reverse = false, this.controller, this.primary, this.physics, this.shrinkWrap = false, this.padding, }) : assert(itemBuilder != null), assert(initialItemCount != null && initialItemCount >= 0), super(key: key); 复制代码
可以看到和普通的没什么区别,那我们再来找一下怎么添加/删除item以及添加/删除时是如何设置动画的。
Insert/Remove 方法
animated_list.dart
这个文件一共才380 行代码,所以我们很快就能找到:
/// Insert an item at [index] and start an animation that will be passed /// to [AnimatedList.itemBuilder] when the item is visible. /// /// This method's semantics are the same as Dart's [List.insert] method: /// it increases the length of the list by one and shifts all items at or /// after [index] towards the end of the list. void insertItem(int index, { Duration duration = _kDuration }) { assert(index != null && index >= 0); assert(duration != null); final int itemIndex = _indexToItemIndex(index); assert(itemIndex >= 0 && itemIndex <= _itemsCount); // Increment the incoming and outgoing item indices to account // for the insertion. for (_ActiveItem item in _incomingItems) { if (item.itemIndex >= itemIndex) item.itemIndex += 1; } for (_ActiveItem item in _outgoingItems) { if (item.itemIndex >= itemIndex) item.itemIndex += 1; } final AnimationController controller = AnimationController(duration: duration, vsync: this); final _ActiveItem incomingItem = _ActiveItem.incoming(controller, itemIndex); setState(() { _incomingItems ..add(incomingItem) ..sort(); _itemsCount += 1; }); controller.forward().then<void>((_) { _removeActiveItemAt(_incomingItems, incomingItem.itemIndex).controller.dispose(); }); } 复制代码
首先我们看到这里用了一个 _ActiveItem 这个类,我们去看一下是什么:
// Incoming and outgoing AnimatedList items. class _ActiveItem implements Comparable<_ActiveItem> { _ActiveItem.incoming(this.controller, this.itemIndex) : removedItemBuilder = null; _ActiveItem.outgoing(this.controller, this.itemIndex, this.removedItemBuilder); _ActiveItem.index(this.itemIndex) : controller = null, removedItemBuilder = null; final AnimationController controller; final AnimatedListRemovedItemBuilder removedItemBuilder; int itemIndex; @override int compareTo(_ActiveItem other) => itemIndex - other.itemIndex; } 复制代码
可以看得出来,这其实就是一个包装类,封装了 AnimatedList 中常用的参数。
接下来分析一下上面添加 item 的代码:
- 首先判断 index 和 duration 都不能为 null
- 判断 index 不能小于0 或者大于整个列表的 length
- 把所有在当前 index 以后的 item 下标全部 +1
- 给当前 item 设置上动画的 controller
- 启动动画并在动画完结后把当前动画的 controller dispose 掉
Build 方法
删除item的同理,就不讲了,下面再来看一下 build 方法:
Widget _itemBuilder(BuildContext context, int itemIndex) { final _ActiveItem outgoingItem = _activeItemAt(_outgoingItems, itemIndex); if (outgoingItem != null) return outgoingItem.removedItemBuilder(context, outgoingItem.controller.view); final _ActiveItem incomingItem = _activeItemAt(_incomingItems, itemIndex); final Animation<double> animation = incomingItem?.controller?.view ?? kAlwaysCompleteAnimation; return widget.itemBuilder(context, _itemIndexToIndex(itemIndex), animation); } @override Widget build(BuildContext context) { return ListView.builder( itemBuilder: _itemBuilder, itemCount: _itemsCount, scrollDirection: widget.scrollDirection, reverse: widget.reverse, controller: widget.controller, primary: widget.primary, physics: widget.physics, shrinkWrap: widget.shrinkWrap, padding: widget.padding, ); } 复制代码
可以看到其他的参数都是用 widget 里的,唯独itemBuilder 是自己写的,那我们就可以主要来看一下他。
还是一步一步来。
首先看到他是去找 outgoingItem 也就是删除的 item,我们查看一下 _activeItemAt
方法:
_ActiveItem _activeItemAt(List<_ActiveItem> items, int itemIndex) { final int i = binarySearch(items, _ActiveItem.index(itemIndex)); return i == -1 ? null : items[i]; } 复制代码
可以看到是用了二分查找来找需要删除的items列表里是否存在该 index。
如果存在,那么直接返回 outgoingItem.removedItemBuilder
,这个 itemBuilder 是需要我们自己写的。
目的是在做动画的时候显示,而 insertItem 就不需要。
因为我们插入的 widget 肯定也是原有的widget,所以在写AnimatedList 时就已经写好了。
接下来就是判断添加的动画是否存在。
如果不存在,就默认一个永远都是完成的动画,也就是没有动画的动画 -> kAlwaysCompleteAnimation
。
点开看一下:
class _AlwaysCompleteAnimation extends Animation<double> { const _AlwaysCompleteAnimation(); @override void addListener(VoidCallback listener) { } @override void removeListener(VoidCallback listener) { } @override void addStatusListener(AnimationStatusListener listener) { } @override void removeStatusListener(AnimationStatusListener listener) { } @override AnimationStatus get status => AnimationStatus.completed; @override double get value => 1.0; @override String toString() => 'kAlwaysCompleteAnimation'; } 复制代码
可以看到 value 和 status 永远都是完成的状态。
所以这就是我们初始的列表没有动画的原因,而在调用 insertItem 的时候默认传入了一个 controller。
所以我们了解到, 如果我们在定义 itemWidget 的时候,如果不给动画的插值器,那么动画就会是一个 kAlwaysCompleteAnimation
。
最后把这个widget 返回就完成了这一个 itemBuilder。
总结
所以,综上所述,我们在定义一个 AnimatedList 时必须传入一个带动画的 Widget,不然我们用这个控件的意义何在?
关注我,每天更新 Flutter & Dart 知识。
完整代码已经传至GitHub: github.com/wanglu1209/…
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 以太坊源码分析(36)ethdb源码分析
- [源码分析] kubelet源码分析(一)之 NewKubeletCommand
- libmodbus源码分析(3)从机(服务端)功能源码分析
- [源码分析] nfs-client-provisioner源码分析
- [源码分析] kubelet源码分析(三)之 Pod的创建
- Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
网络机器人Java编程指南
美 Heaton J. / 电子工业出版社 / 2002-7 / 44.00元
这是一本研究如何实现具有Web访问能力的网络机器人的书。该书从Internet编程的基本原理出发,深入浅出、循序渐进地阐述了网络机器人程序Spider、Bot、Aggregator的实现技术,并分析了每种程序的优点及适用场合。本书提供了大量的有效源代码,并对这些代码进行了详细的分析。通过本书的介绍,你可以很方便地利用这些技术,设计并实现网络蜘蛛或网络信息搜索器等机器人程序。 适合于具有一起来看看 《网络机器人Java编程指南》 这本书的介绍吧!