ListView是最常用的可滚动组件之一,它可以沿一个方向线性排布所有子组件,并且它也支持基于Sliver的延迟构建模型。Flutter中的ListView与Android中的ListView具有同等功能。
Sliver的延迟构建模型
请参见本系列文章《Flutter SingleChildScrollView 滚动控件》中的基本概念
处。
本文示例效果图(全)
1、默认构造函数
我们看看ListView的默认构造函数定义:
代码语言:javascript复制ListView({
...
//可滚动widget公共参数
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
EdgeInsetsGeometry padding,
//ListView各个构造函数的共同参数
double itemExtent,
bool shrinkWrap = false,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
double cacheExtent,
//子widget列表
List<Widget> children = const <Widget>[],
})
上面参数分为两组:第一组是可滚动组件的公共参数,本章第一节中已经介绍过,不再赘述;第二组是ListView各个构造函数(ListView有多个构造函数)的共同参数,我们重点来看看这些参数。
ListView通用属性值 | 含义 |
---|---|
itemExtent | 每个子控件的高度。指定itemExtent的值比不指定(自适应高度)会更高效。 |
shrinkWrap | 是否根据子组件的总高度来设置ListView的高度,默认为false 。当ListView在一个无边界(滚动方向上)的容器中时,shrinkWrap必须为true。 |
addAutomaticKeepAlives | 是否将列表项(子控件)包裹在AutomaticKeepAlive组件中,包含之后列表项滑出视口时它也不会被GC。如果列表项自己维护其KeepAlive状态,那么此参数必须置为false。 |
addRepaintBoundaries | 是否将列表项(子控件)包裹在RepaintBoundary组件中,包含之后可以避免列表项重绘。如果列表项自己维护其KeepAlive状态,那么此参数必须置为false。 |
cacheExtent | 预加载的区域。 |
不支持Sliver懒加载模型。
默认构造函数有一个children
参数,它接受一个Widget列表。这种方式适合只有少量的子组件的情况,因为这种方式需要将所有children
都提前创建好(这需要做大量工作),而不是等到子widget真正显示的时候再创建,也就是说通过默认构造函数构建的ListView没有应用基于Sliver的懒加载模型。实际上通过此方式创建的ListView
和使用SingleChildScrollView
Column
的方式没有本质的区别。
下面是一个例子:
代码语言:javascript复制 ListView(
padding: EdgeInsets.all(20),
children: <Widget>[
new Text("one"),
new Text("two"),
new Text("three"),
new Text("four"),
new Text("five"),
new Text("six"),
],
),
2、ListView.builder
支持Sliver懒加载模型。
ListView.builder
适合列表项比较多(或者无限)的情况,因为只有当子组件真正显示的时候才会被创建,也就说通过该构造函数创建的ListView
是支持基于Sliver的懒加载模型的。
下面看一下ListView.builder
的核心参数列表:
ListView.builder({
// ListView公共参数已省略
...
@required IndexedWidgetBuilder itemBuilder,
int itemCount,
...
})
ListView.builder属性值 | 含义 |
---|---|
itemBuilder | 它是列表项的构建器,类型为IndexedWidgetBuilder,返回值为一个widget。当列表滚动到具体的index位置时,会调用该构建器构建列表项。 |
itemCount | 列表项的数量,如果为null,则为无限列表。 |
可滚动组件的构造函数如果需要一个列表项Builder
,那么通过该构造函数构建的可滚动组件通常就是支持基于Sliver的懒加载模型
的,反之则不支持,其他可滚动组件亦是如此。
下面看一个例子:
代码语言:javascript复制ListView.builder(
itemCount: 100,
itemExtent: 50.0, //强制高度为50.0,可提升效率
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text("$index"));
}
);
3、ListView.separated
支持Sliver懒加载模型。
ListView.separated
可以在生成的列表项之间添加一个分割组件,它比ListView.builder
多了一个separatorBuilder
参数,该参数是一个分割组件生成器,可生成分割线。
下面我们看一个例子:奇数行添加一条蓝色下划线,偶数行添加一条绿色下划线。
代码语言:javascript复制class ListView3 extends StatelessWidget {
@override
Widget build(BuildContext context) {
//下划线widget预定义以供复用。
Widget divider1=Divider(color: Colors.blue,);
Widget divider2=Divider(color: Colors.green);
return ListView.separated(
itemCount: 100,
//列表项构造器
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text("$index"));
},
//分割器构造器
separatorBuilder: (BuildContext context, int index) {
return index%2==0?divider1:divider2;
},
);
}
}
4、ListView.custom
支持Sliver懒加载模型。
ListView.custom
可以自定义ListView的Item,对于复杂ListView(比如不同index对应不同布局时)需要用到它。ListView.builder
和ListView.separated
是ListView.custom
的简化版。因为 ListView 内部是靠这个 childrenDelegate 属性动态初始化子元素的。
构造方法:
代码语言:javascript复制ListView.builder({
// ListView公共参数已省略
...
@required this.childrenDelegate,
...
})
- childrenDelegate:
为ListView提供子委托,类型为SliverChildDelegate
。可传入SliverChildDelegate的子类SliverChildBuilderDelegate
实例。该实例可以获取到ListView的index,并根据index返回对应的Widget。适用于高度自定义ListView的情况下使用。
举个例子:
代码语言:javascript复制 String datas =
"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ";
Expanded(
child: ListView.custom(
itemExtent: 40.0,
childrenDelegate: SliverChildBuilderDelegate(
(BuildContext context, int i) {
Widget result;
if (i < 5) {
result = new Text(
"cus${datas.split("")[i]}",
style: new TextStyle(fontSize: 18.0, color: Colors.green),
);
} else if (i < 10) {
result = new Text(
"cus${datas.split("")[i]}",
style: new TextStyle(fontSize: 18.0, color: Colors.red),
);
} else {
result = new Text(
"cus${datas.split("")[i]}",
style: new TextStyle(fontSize: 18.0, color: Colors.blue),
);
}
return result;
},
childCount: datas.length,
),
cacheExtent: 0.0,
)),
效果图见文章开篇处。