ListView是最常用的可滚动组件之一,它可以沿一个方向线性排布所有子组件,并且它也支持基于Sliver的延迟构建模型。
属性:
itemExtent:
该参数如果不为null,则会强制children的“长度”为itemExtent的值;这里的“长度”是指滚动方向上子组件的长度,也就是说如果滚动方向是垂直方向,则itemExtent代表子组件的高度;如果滚动方向为水平方向,则itemExtent就代表子组件的宽度。
shrinkWrap:
该属性表示是否根据子组件的总长度来设置ListView的长度,默认值为false 。默认情况下,ListView的会在滚动方向尽可能多的占用空间。
addAutomaticKeepAlives:
该属性表示是否将列表项(子组件)包裹在AutomaticKeepAlive 组件中;如在一个懒加载列表中,将列表项包裹在AutomaticKeepAlive中,在该列表项滑出视口时它也不会被回收,它会使用KeepAliveNotification来保存其状态。
addRepaintBoundaries:
该属性表示是否将列表项(子组件)包裹在RepaintBoundary组件中。当可滚动组件滚动时,将列表项包裹在RepaintBoundary中可以避免列表项重绘。
默认构造函数
默认构造函数有一个children参数,它接受一个Widget列表(List)。这种方式适合只有少量的子组件的情况,因为要将所有children都提前创建好,而不是等到子widget真正显示的时候再创建。
重要:可滚动组件通过一个List来作为其children属性时,只适用于子组件较少的情况。
ListView(
shrinkWrap: true,
padding: const EdgeInsets.all(10),
children: <Widget>[
Text("佛山无影脚"),
Text("南拳北腿"),
Text("少林武当"),
Text("峨眉倥侗"),
Text("武林盟主"),
],
),
ListView.builder
ListView.builder适合列表项比较多(或者无限)的情况,因为只有当子组件真正显示的时候才会被创建,也就说通过该构造函数创建的ListView是支持基于Sliver的懒加载模型的。
属性
itemBuilder:它是列表项的构建器,类型为IndexedWidgetBuilder,返回值为一个widget。
当列表滚动到具体的index位置时,会调用该构建器构建列表项。
itemCount:列表项的数量,如果为null,则为无限列表。
ListView.builder(
itemCount: 80,
scrollDirection: Axis.vertical,
itemExtent: 50,//限制高度为50
itemBuilder: (BuildContext context,int index){
return ListTile(title: Text("$index",),minLeadingWidth: double.infinity,);
},
),
如图
ListView.separated
ListView.separated可以在生成的列表项之间添加一个分割组件,它比ListView.builder多了一个separatorBuilder参数,该参数是一个分割组件生成器。
Widget blueDivider = Divider(color: Colors.blue,);
Widget greenDivider = Divider(color: Colors.green,);
Widget separated = ListView.separated(
itemBuilder: (BuildContext context,int index){
return ListTile(title: Text("$index",),minLeadingWidth: double.infinity,);
},
separatorBuilder: (BuildContext context,int index){
return index%2 == 0?blueDivider:greenDivider;
},
itemCount: 80,
);
效果
实现无限加载列表
从数据源异步分批拉取一些数据,然后用ListView展示,当我们滑动到列表末尾时,判断是否需要再去拉取数据,如果是,则去拉取,拉取过程中在表尾显示一个loading,拉取成功后将数据插入列表;如果不需要再去拉取,则在表尾提示"没有更多"。
代码
class InfiniteListView extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return InfiniteListViewState();
}
}
class InfiniteListViewState extends State<InfiniteListView>{
static const loadingTag = "##loading##"; //表尾标记
var words = <String>[loadingTag];
@override
void initState() {
super.initState();
getData();
}
@override
Widget build(BuildContext context) {
return ListView.separated(
itemBuilder: (BuildContext context,int index){
if(words[index] == loadingTag){
if(words.length-1 < 100){
//小于100 继续添加数据
getData();
//加载是显示loading
return Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(10),
child: SizedBox(
width: 36,
height: 36,
child: CircularProgressIndicator(strokeWidth: 2,),
),
);
}else{
//大于等于100,不再添加数据
return Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(10),
child: Text("没有更多了",style: TextStyle(color: Colors.grey),),
);
}
}
return ListTile(title: Text(words[index]),);
},
separatorBuilder: (BuildContext context,int index){
return Divider(height: 1,);
},
itemCount: words.length,
);
}
//生成数据
void getData() {
Future.delayed(Duration(seconds: 2)).then((value){
setState(() {
//重新加列表
words.insertAll(words.length-1,
//每次生成10个单词
generateWordPairs().take(10).map((e) => e.asPascalCase).toList()
);
});
});
}
}
单词生成包的引入
1,pubspec.yaml中 添加 english_words: ^4.0.0,
2,导包import ‘package:english_words/english_words.dart’;
效果
添加固定列表头
class StudyListView extends StatelessWidget{
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ListView"),
),
body: Scrollbar(//显示滚动条
child: Column(
children: <Widget>[
ListTile(title:Text("单词表")),
Expanded(child: InfiniteListView()),
],
),
),
);
}
}
如图