前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter AnimatedList 源码分析

Flutter AnimatedList 源码分析

作者头像
Flutter笔记
发布2019-07-25 14:30:30
5430
发布2019-07-25 14:30:30
举报
文章被收录于专栏:Flutter笔记

现在的UI页面已经离不开动画了,如果没有动画,页面看起来就会很突兀。

对于我们使用最多的Listview,Flutter 当然也给我们封装好了。

AnimatedListView

由于近期某些不可抗拒的原因,Flutter官网我们是打不开了。

所以我们直接点开源码看吧,在 AnimatedList 类中的第一句话是:

Creates a scrolling container that animates items when they are inserted or removed. 创建一个滚动容器,在插入或删除项目时为其设置动画。

再来看一下构造函数:

代码语言:javascript
复制
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 行代码,所以我们很快就能找到:

代码语言:javascript
复制
/// 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 这个类,我们去看一下是什么:

代码语言:javascript
复制
// 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 方法:

代码语言:javascript
复制
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 方法:

代码语言:javascript
复制
_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

点开看一下:

代码语言:javascript
复制
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:https://github.com/wanglu1209/WFlutterDemo

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Flutter笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • AnimatedListView
  • Insert/Remove 方法
  • Build 方法
  • 总结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档