📜  Flutter的Listview.builder(1)

📅  最后修改于: 2023-12-03 15:30:49.883000             🧑  作者: Mango

Flutter的ListView.builder

在Flutter中,ListView是用于构建可滚动列表的一个重要组件。ListView允许我们在屏幕上展示大量的数据,并提供了诸多的用来优化性能和用户交互的选项。其中,ListView.builder是ListView中比较常用的构造方法。

ListView.builder概述

ListView.builder基于数据源的长度构建一个列表。这个列表的所有子项都由builder函数来生成,builder函数会在每次子项需要更新时调用。ListView.builder适用于当我们需要很多相似的子项时,比如从一个数组或者数据库中生成子项的列表。

ListView.builder的构造方法如下:

ListView.builder({
  Key? key,
  Axis scrollDirection = Axis.vertical,
  bool reverse = false,
  ScrollController? controller,
  bool? primary,
  ScrollPhysics? physics,
  required IndexedWidgetBuilder itemBuilder,
  int? itemCount,
  bool shrinkWrap = false,
  EdgeInsetsGeometry? padding,
  String? restorationId,
  Clip clipBehavior = Clip.hardEdge
})

参数列表:

| 参数 | 类型 | 描述 | |----------------|-------------------------|------------------------------------------------| | key | Key? | widget的标识符 | | scrollDirection| Axis | 滚动方向,可为Axis.vertical(默认)或Axis.horizontal | | reverse | bool | 是否反转滚动方向 | | controller | ScrollController? | 滚动控制器 | | primary | bool? | 当为true时,滚动将停止到边界处 (默认值为null) | | physics | ScrollPhysics? | 与此滚动视图相关联的physics | | itemBuilder | IndexedWidgetBuilder | 子项构造器,需要返回一个widget | | itemCount | int? | 列表项数量,必须指定 | | shrinkWrap | bool | 内容适合的情况下是否需要缩小 | | padding | EdgeInsetsGeometry? | ListView的内边距,如果没有指定,则默认为EdgeInsets.zero | | restorationId | String? | 存档和恢复的ID | | clipBehavior | Clip | 裁剪行为 |

ListView.builder使用方法

在使用ListView.builder之前,首先要确定数据源和itemBuilder函数。假设我们有一个包含字符串的数组,需要把它们渲染为一个ListView。

final List<String> items = ['item 1', 'item 2', 'item 3', 'item 4', 'item 5'];

然后,我们需要编写builder函数。builder函数的目的是将每个列表项渲染为widget。ListView.builder构造函数中的itemBuilder参数特别重要,它需要一个IndexedWidgetBuilder类型的函数。IndexedWidgetBuilder是一个具有以下签名的函数:

Widget Function(BuildContext context, int index)

这个函数需要两个参数:BuildContext context和int index。context是当前BuildContext,通常作为widget创建的上下文。index是列表项的索引,它从0开始递增。在builder函数中我们需要将在items中读取给定索引值的item,并将其渲染为一个Widget。

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    final item = items[index];
    return ListTile(
      title: Text(item),
    );
  },
)

上面的代码将items中的每个元素转换为一个ListTile并返回。

性能优化

ListView.builder提供了一些属性和方法来优化性能。

1. 控制滚动的位置与追加

我们可以使用ScrollController来控制滚动的位置和初始位置,同时也可以用ScrollController监听滚动事件。暴露在外的ScrollController指向ListView中的滚动位置并将其控制。

final controller = ScrollController();

@override
void initState() {
  super.initState();
  controller.jumpTo(100.0); // 控制初始位置
}

@override
Widget build(BuildContext context) {
  return ListView.builder(
    controller: controller, // 将滚动控制器指向ListView
    itemCount: items.length,
    itemBuilder: (context, index) {
      return ListTile(
        title: Text(items[index]),
      );
    },
  );
}

我们还可以在列表底部追加新的数据,这时候我们需要监听滚动到底事件,然后添加新数据到items,然后使用setState()调用来重新构建ListView。

final controller = ScrollController();

@override
void initState() {
  super.initState();
  controller.addListener(_scrollListener);
}

@override
void dispose() {
  controller.removeListener(_scrollListener);
  super.dispose();
}

void _scrollListener() {
  if (controller.offset >= controller.position.maxScrollExtent &&
      !controller.position.outOfRange) {
    setState(() {
      // 在当前items追加新数据
      items.addAll(['item 6', 'item 7', 'item 8', 'item 9', 'item 10']);
    });
  }
}

@override
Widget build(BuildContext context) {
  return ListView.builder(
    controller: controller,
    itemCount: items.length,
    itemBuilder: (context, index) {
      final item = items[index];
      return ListTile(
        title: Text(item),
      );
    },
  );
}
2. 缓存

ListView.builder支持两种缓存机制:itemExtent和addAutomaticKeepAlives。

itemExtent属性定义每个列表项的大小。它允许创建一个具有固定大小的子项,这样Flutter就不需要在每次创建列表项时都重新计算它的大小。

ListView.builder(
  itemCount: items.length,
  itemExtent: 50, // 每个子项的高度都是50
  itemBuilder: (context, index) {
    final item = items[index];
    return ListTile(
      title: Text(item),
    );
  },
)

addAutomaticKeepAlives保存了Widget状态以在滚动视图中保留它们的位置。假设我们在itemBuilder中添加RepaintBoundary,将可持续化它的状态,这可以避免在滚动过程中不必要的重绘。不过它的使用需要谨慎,因为保持项会占用更多的内存。

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    final item = items[index];
    return ListTile(
      title: Text(item),
      leading: RepaintBoundary(
        child: Icon(Icons.favorite), // 可持续化状态
      ),
    );
  },
  addAutomaticKeepAlives: true, // 开启自动保持
)
3. 预取数据

precacheExtent属性用于在滚动视图滚过页面末尾之前缓存指定数量的列表项。这可以减少滚动视图的等待时间,但需要谨慎使用。通常它会在缓存超出级别时造成无意义的列表构建,从而反而降低了性能。

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    final item = items[index];
    return ListTile(
      title: Text(item),
    );
  },
  cacheExtent: 50, // 缓存50个列表项到内存
)
总结

ListView.builder是在Flutter开发中非常重要的组件。它通过一个builder函数来根据提供的数据源动态渲染列表项,为我们的开发提供了很大的便利。当我们需要构建大量、相似的子项时,ListView.builder是最优秀的选择之一,我们还可以通过控制滚动位置、动态追加列表项、缓存和预取数据等方法对其进行性能优化。