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 源码分析

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

查看所有标签

猜你喜欢:

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

Lean Analytics

Lean Analytics

Alistair Croll、Benjamin Yoskovitz / O'Reilly Media / 2013-3-18 / USD 29.99

If you're involved with a startup, analytics help you find your way to the right product and market before the money runs out. But with a flood of information available, where do you start? This book ......一起来看看 《Lean Analytics》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具