简介: ListView是最常用的可滚动组件之一
有三种构建方式:
- ListView
- ListView.builder
- ListView.separated
主要参数说明:
scrollDirection: Axis.horizontal 水平列表Axis.vertical 垂直列表
padding: 内边距
resolve: 组件反向排序
children: 列表元素
itemBuilder:(ListView.builder、ListView.separated固有的) 它是列表项的构建器,类型为IndexedWidgetBuilder,(ListView.builder、ListView.separated固有的) 返回值为一个widget。当列表滚动到具体的index位置时,会调用该构建器构建列表项。
itemCount:列表项的数量,如果为null,则为无限列表。
一、ListView
适合场景: 适合只有少量的子组件的情况,需要将所有children都提前创建好,即通过默认构造函数构建的ListView没有应用基于Sliver的懒加载模型
示例代码:
ListView(shrinkWrap: true, padding: const EdgeInsets.all(20.0),children: <Widget>[const Text('I\'m dedicating every day to you'),const Text('Domestic life was never quite my style'),const Text('When you smile, you knock me out, I fall apart'),const Text('And I thought I was so smart'),],
);
二、ListView.builder
适合场景: 适合列表项比较多(或者无限)的情况,因为只有当子组件真正显示的时候才会被创建,也就说通过该构造函数创建的ListView是支持基于Sliver的懒加载模型的。
示例代码:
ListView.builder(itemCount: 100,itemExtent: 50.0, //强制高度为50.0itemBuilder: (BuildContext context, int index) {return ListTile(title: Text("$index"));}
);
运行效果如下图:
三、ListView.separated
适合场景: 和ListView.builder适用场景一样的,它比ListView.builder多了一个separatorBuilder参数,该参数是一个分割组件生成器,可用在生成的列表项之间。
下面我们看一个例子:奇数行添加一条蓝色下划线,偶数行添加一条绿色下划线。
class ListView3 extends StatelessWidget {@overrideWidget 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;},);}
}
效果图如下:
四、实战一:添加固定列表头的处理方式
在弹性布局中,可以使用Expanded自动拉伸组件大小,并且Column是继承自Flex的,所以可以直接使用Column+Expanded来实现
@override
Widget build(BuildContext context) {return Column(children: <Widget>[ListTile(title:Text("商品列表")),Expanded(child: ListView.builder(itemBuilder: (BuildContext context, int index) {return ListTile(title: Text("$index"));}),),]);
}
五、实战二:无限加载列表
假设我们要从数据源异步分批拉取一些数据,然后用ListView展示,当我们滑动到列表末尾时,判断是否需要再去拉取数据,如果是,则去拉取,拉取过程中在表尾显示一个loading,拉取成功后将数据插入列表;如果不需要再去拉取,则在表尾提示"没有更多"。代码如下:
class InfiniteListView extends StatefulWidget {@override_InfiniteListViewState createState() => new _InfiniteListViewState();
}class _InfiniteListViewState extends State<InfiniteListView> {static const loadingTag = "##loading##"; //表尾标记var _words = <String>[loadingTag];@overridevoid initState() {super.initState();_retrieveData();}@overrideWidget build(BuildContext context) {return ListView.separated(itemCount: _words.length,itemBuilder: (context, index) {//如果到了表尾if (_words[index] == loadingTag) {//不足100条,继续获取数据if (_words.length - 1 < 100) {//获取数据_retrieveData();//加载时显示loadingreturn Container(padding: const EdgeInsets.all(16.0),alignment: Alignment.center,child: SizedBox(width: 24.0,height: 24.0,child: CircularProgressIndicator(strokeWidth: 2.0)),);} else {//已经加载了100条数据,不再获取数据。return Container(alignment: Alignment.center,padding: EdgeInsets.all(16.0),child: Text("没有更多了", style: TextStyle(color: Colors.grey),));}}//显示单词列表项return ListTile(title: Text(_words[index]));},separatorBuilder: (context, index) => Divider(height: .0),);}void _retrieveData() {Future.delayed(Duration(seconds: 2)).then((e) {_words.insertAll(_words.length - 1,//每次生成20个单词generateWordPairs().take(20).map((e) => e.asPascalCase).toList());setState(() {//重新构建列表});});}}