Flutter AnimatedList 源码分析

栏目: 编程工具 · 发布时间: 5年前

内容简介:由于近期某些不可抗拒的原因,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 的代码:

  1. 首先判断 index 和 duration 都不能为 null
  2. 判断 index 不能小于0 或者大于整个列表的 length
  3. 把所有在当前 index 以后的 item 下标全部 +1
  4. 给当前 item 设置上动画的 controller
  5. 启动动画并在动画完结后把当前动画的 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/…

Flutter AnimatedList 源码分析

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

网络机器人Java编程指南

网络机器人Java编程指南

美 Heaton J. / 电子工业出版社 / 2002-7 / 44.00元

这是一本研究如何实现具有Web访问能力的网络机器人的书。该书从Internet编程的基本原理出发,深入浅出、循序渐进地阐述了网络机器人程序Spider、Bot、Aggregator的实现技术,并分析了每种程序的优点及适用场合。本书提供了大量的有效源代码,并对这些代码进行了详细的分析。通过本书的介绍,你可以很方便地利用这些技术,设计并实现网络蜘蛛或网络信息搜索器等机器人程序。 适合于具有一起来看看 《网络机器人Java编程指南》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具