Flutter 可滚动组件子项缓存

38 阅读2分钟

在介绍 ListView 时,有一个addAutomaticKeepAlives 属性并没有介绍,如果addAutomaticKeepAlives 为 true,则 ListView 会为每一个列表项添加一个 AutomaticKeepAlive 父组件。

虽然 PageView 的默认构造函数和 PageView.builder 构造函数中没有该参数,但它们最终都会生成一个 SliverChildDelegate 来负责列表项的按需加载,而在 SliverChildDelegate 中每当列表项构建完成后,SliverChildDelegate 都会为其添加一个 AutomaticKeepAlive 父组件。

1 AutomaticKeepAlive

AutomaticKeepAlive 的组件的主要作用是将列表项的根 RenderObject 的 keepAlive 按需自动标记 为 true 或 false。为了方便叙述,可以认为根 RenderObject 对应的组件就是列表项的根 Widget,代表整个列表项组件,同时将列表组件的 Viewport区域 + cacheExtent(预渲染区域)称为加载区域

  1. 当 keepAlive 标记为 false 时,如果列表项滑出加载区域时,列表组件将会被销毁。

  2. 当 keepAlive 标记为 true 时,当列表项滑出加载区域后,Viewport 会将列表组件缓存起来;当列表项进入加载区域时,Viewport 从先从缓存中查找是否已经缓存,如果有则直接复用,如果没有则重新创建列表项。

AutomaticKeepAlive 什么时候会将列表项的 keepAlive 标记为 true 或 false 呢?

答案是开发者说了算!

Flutter 中实现了一套类似 C/S 的机制,AutomaticKeepAlive 就类似一个 Server,它的子组件可以是 Client,这样子组件想改变是否需要缓存的状态时就向 AutomaticKeepAlive 发一个通知消息(KeepAliveNotification), AutomaticKeepAlive 收到消息后会去更改 keepAlive 的状态,如果有必要同时做一些资源清理的工作(比如 keepAlive 从 true 变为 false 时,要释放缓存)

Flutter 提供了一个 AutomaticKeepAliveClientMixin 只需要让 PageState 混入这个 mixin,且同时添加一些必要操作即可:

class _PageState extends State<Page> with AutomaticKeepAliveClientMixin {

  @override
  Widget build(BuildContext context) {
    super.build(context); // 必须调用
    return Center(child: Text("${widget.text}", textScaleFactor: 5));
  }

  @override
  bool get wantKeepAlive => true; // 是否需要缓存
}

只需要提供一个 wantKeepAlive,它会表示 AutomaticKeepAlive 是否需要缓存当前列表项;另外我们必须在 build 方法中调用一下 super.build(context),该方法实现在 AutomaticKeepAliveClientMixin 中,功能就是根据当前 wantKeepAlive 的值给 AutomaticKeepAlive 发送消息,AutomaticKeepAlive 收到消息后就会开始工作