深入学习Flutter之ListView和ScrollPhysics

英文地址:Flutter ListView and ScrollPhysics: A Detailed Look

探究ListView的类型

我们将先查看listview的类型,然后查看它的其他特性和整洁性修改。

列表视图的类型:

  1. ListView
  2. ListView.builder
  3. ListView.separated
  4. ListView.custom
ListView

这个是ListView类的默认构造函数,仅仅接收children列表就能够实现滚动效果。

代码一般格式如下:

1
2
3
4
5
6
7
ListView(
children: <Widget>[
ItemOne(),
ItemTwo(),
ItemThree(),
],
),

通常,这适用于少量子组件的使用,因为列表也将会构建不可见的元素,而渲染大量的元素会影响性能效率。

ListView.builder()

这个builder()构造函数来构造重复性的列表项,这个构造函数有两个主要的参数:itemCount(列表项个数),itemBuilder用于构建具体每个列表项目。

代码一般格式如下:

1
2
3
4
5
6
ListView.builder(
itemCount: itemCount,
itemBuilder: (context, position) {
return listItem();
},
),

这些列表项是惰性构造的,意味着只会构造特定数量的列表项,当用户向前滚动时,之前的列表项就会被销毁。

(巧妙的技巧)Neat trick:因为这些元素是惰性载入的,仅有被需要数量的元素被载入进去。我们可以不必需要itemCount作为一个强制参数,这样列表将会是无限的。

1
2
3
4
5
6
7
8
9
10
ListView.builder(
itemBuilder: (context, position) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(position.toString(), style: TextStyle(fontSize: 22.0),),
),
);
},
),

ListView.separated()

separated()构造函数中,我将会生成一个列表,并且我们可以指定列表项之间的分隔符。

本质上,我们构造了两个交织的列表,一个作为主列表,另一个作为分割列表。

注意,上面的构造函数谈及的无限数量的列表项不能在这里使用,这个构造函数强制需要一个itemCount参数。

代码的一般格式如下:

1
2
3
4
5
6
7
8
9
ListView.separated(
itemBuilder: (context,position) {
return ListItem();
}
separatorBuilder: (context,position) {
return SeparationItem();
},
itemCount: itemCount,
)

这种类型的列表允许你动态构造分割符,不同类型的列表项,可拥有不同的分隔符,按需添加/移除分割符等等。

此实现可以轻松插入其他类型的元素(例如:广告),而无需修改这些列表项中间的主列表。

注意:分隔符列表长度比这些列表项少1个,因为在最后一个元素之后不存在分隔符。

ListView.custom()

这个custom()构造函数如其名,允许你使用定义功能(这些列表中的子组件是如何构建的)来构建ListView。为此,所必须的主要参数是一个SliverChildDelegate,用于构建这些列表项目。

SliverChildDelegate的类型如下:

  1. SliverChildListDelegate
  2. SliverChildBuilderDelegate

SliverChildListDelegate直接接收一个children列表,而SliverChildBuilderDelegate接收一个IndexedWidgetBuilder(我们使用的builder函数)。

您可以使用或继承它们来构建自己的委托。

ListView.builder本质上是ListView.custom使用一个SilverChildBuilderDelegate

ListView默认构造函数的行为类似于ListView.custom使用一个SliverChildListDelegate

探究滚动原理

为了控制滚动的发生方式,我们在ListView构造函数设置physics参数,类型如下:

NeverScrollablePhysics

使用这个,将会完全禁用滚动。

BouncingScrollPhysics

在列表结束的时候会有一个反弹效果,iOS上也是使用了类似的效果。

ClampingScrollPhysics

这是Android上使用的默认scrolling physics。列表在末尾停止,并给出一个指示它的效果。

FixedExtentScrollPhysics

这个稍微与其他的不同,这个仅对FixedExtendScrollControllers和使用它们的列表有效。例如,我们将以ListWheelScrollView为例,它生成一个具有滚轮效果的列表。

FixedExtentScrollPhysics只会滚动到项,而不会是其之间的任何偏移量。

这个例子的代码非常简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
FixedExtentScrollController fixedExtentScrollController =
new FixedExtentScrollController();
ListWheelScrollView(
controller: fixedExtentScrollController,
physics: FixedExtentScrollPhysics(),
children: monthsOfTheYear.map((month) {
return Card(
child: Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
month,
style: TextStyle(fontSize: 18.0),
),
)),
],
));
}).toList(),
itemExtent: 60.0,
),

更多

如何使列表中被销毁的元素保持活动状态?

Flutter提供了一个KeepAlive()小部件,该小部件使一个本来会被销毁的项保持活跃状态。在列表中,元素默认被包装在AutomaticKeepAlive小部件中。

可以通过将addAutomaticKeepAlives字段设置为false来禁用AutomaticKeepAlives。这在不需要保持元素活跃或自定义KeepAlive实现的情况下非常有用。

为什么我的列表视图在列表和外部小部件之间存在空隙??

默认情况下,ListView与外部小部件之间有填充,要移除它,将填充设置为EdgeInset.all(0.0)