Flutter ListView 与 ListTile、基于数据动态渲染 ListView 、横向列表与纵向列表嵌套
一、说明
ListView
是 Flutter App 中使用非常多的滚动列表组件,页面大多数情况都是可滚动的,列表聚合图文场景下使用 ListView 的情况非常多。
ListView
除了本身这个 Widget 之外,一般会和 ListTile
配合使用,其中 ListTile 是使用非常多的子项 Widget
基本的结构就是:
|- ListView
|- ListTile
|- ListTile
|- ListTile
下面代码实践中使用了几个静态变量,这里列出来,完整代码可以在文章最后看到
页面结构的相关代码不重复,只写 ListView 相关的代码
const TITLE = '标题标题标题标题标题标题标题';
const SUB_TITLE = '二级标题二级标题二级标题二级标题二级标题二级标题二级标题二级标题二';
const IMAGE_SRC =
'https://cdn.pixabay.com/photo/2019/05/20/13/22/portugal-4216645_1280.jpg';
二、ListView 基本用法
基本结构代码如下,各个 ListTile 子项也抽成了 Widget,直接在 ListView 的 children
参数中引入使用:
class ListViewDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
ListItemIcon(),
ListItemIcon(),
ListItemImage(),
ListItemImage(),
ListItemImage2(),
ListItemImage2(),
ListItemOnlyImage(),
ListItemOnlyImage(),
],
padding: EdgeInsets.all(12),
);
}
}
1、ListTile
标题 + Icon 用法
ListTile
支持的参数非常多,常用是 onTap
、leading
、trailing
、title
、subtitle
一个 ListTile 也由 leading
、trailing
和 title + subtitle
组成
return ListTile(
trailing: Icon(Icons.chevron_right, color: Colors.pink),
title: Text(TITLE),
subtitle: Text(
SUB_TITLE,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
);
上面代码中,创建了一个 ListTile,并且指定了 title 和 subtitle,title 和 subtitle 都是 Widget 类型,可以放 Widget,并不限制是 Text
trailing
可以放 Icon 也可以放别的 Widget
最终效果如下:
2、ListTile
图片+标题+Icon 用法
上面只是单纯的 标题+Icon,好一点的新闻列表效果会加上封面图,而封面图是放在 ListTile
的 leading
属性中的
class ListItemImage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
leading: Container(
child: Image.network(
IMAGE_SRC,
fit: BoxFit.cover,
),
color: Colors.grey,
width: 60,
height: 60),
trailing: Icon(Icons.chevron_right, color: Colors.pink),
title: Text(TITLE),
subtitle: Text(
SUB_TITLE,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
);
}
}
上面代码中。leading 放了一张网络图片,网络加载图片之前也说过这个问题,就是太突兀,所以我做了一个 Container 放在外面,然后通过 Container Widget 限制图片宽高,并且设置了一个背景色,这样加载的时候不会显得过于突兀,其他的和上面代码一致。
因为我没有设置 title
只有一行,所以看起来并没有非常水平对齐
最终效果:
3、ListTile 标题+图片 用法
上面是图片放在前面,而有些场景下,图片是放在 List 单个内容的后面,这种情况下,将图片放在 trailing
中即可:
return ListTile(
trailing: Container(
child: Image.network(
IMAGE_SRC,
fit: BoxFit.cover,
),
color: Colors.grey,
width: 60,
height: 60),
title: Text(TITLE),
subtitle: Text(
SUB_TITLE,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
);
最终效果:
4、不使用 ListTile 的列表
有些时候我们只是想构建一个列表,但是列表子项并不是 ListTile
的样式,这就需要我们自己实现一个 子项 Widget
而这种情况也非常简单,无非就是自己构造一个 Widget 然后引入使用即可
class ListItemOnlyImage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Image.network(IMAGE_SRC, fit: BoxFit.cover),
color: Colors.grey,
width: 600,
height: 400,
margin: EdgeInsets.only(bottom: 10),
);
}
}
效果:
三、基于列表数据渲染 ListView
上面都是我们写死的列表,实际过程中我们肯定是需要基于一个列表数据去渲染整个 List 的
列表数据可以通过接口请求拿到,这里我就直接用 网易新闻的结构 mock 了,数据列表可以在 https://github.com/postbird/FlutterHelloWorldDemo/blob/master/demo1/lib/mock/list.dart 拿到
children
参数需要传递一个 List<Widget>
结构的列表,列表每个子项都是一个 Widget
基于上面的基本思路,我们只需要将 ListView.children
构造出来即可:
上面已经有了 mock 数据,只要读取 mock 数据并且构造列表即可:
// 引入 mock 数据
import 'mock/list.dart' as newsList;
1、通过函数生成 List
构造 List
List<Widget> _getNewsList() {
return newsList.news.map((item) {
return ListItem(
title: '${item['title']}',
subTitle: '${item['time']}',
cover: '${item['imgurl']}',
);
}).toList();
}
方法 _getNewsList()
最终返回 List<Widget>
,其实非常简单就是使用 list.map()
返回一个 Widget 列表,需要注意的是 map 方法返回的是 Iterable,并非 List,需要 toList()
2、设计 ListItem 单个子项
ListItem 是需要我们去构造的自定义 Widget,构造也非常简单,只要传入 封面图、title 和 subtitle 即可
class ListItem extends StatelessWidget {
ListItem({this.title, this.subTitle, this.cover});
final String title;
final String subTitle;
final String cover;
@override
Widget build(BuildContext context) {
return ListTile(
leading: Container(
child: Image.network(this.cover, fit: BoxFit.cover),
width: 60,
height: 60,
color: Colors.grey),
trailing: Icon(Icons.chevron_right),
title: Text(this.title),
subtitle: Text(this.subTitle),
);
}
}
3、完整的 List 示例
构造出 ListView.children
之后就可以赋值,因此整个列表的代码如下:
class ListViewDemo extends StatelessWidget {
// 读取文件 mock 数据
List<Widget> _getNewsList() {
return newsList.news.map((item) {
return ListItem(
title: '${item['title']}',
subTitle: '${item['time']}',
cover: '${item['imgurl']}',
);
}).toList();
}
@override
Widget build(BuildContext context) {
return ListView(
// children: this._getData(20),
children: this._getNewsList(),
);
}
}
最终的效果:
四、横向列表与列表嵌套
1、横向列表
上面我们使用的都是纵向的列表滚动,而如果将其变成横向,也非常简单,只需要修改 scrollDirection
的值即可:
scrollDirection
默认值是 Axis.vertical
,横向使用 Axis.horizontal
scrollDirection: Axis.horizontal,
3、列表嵌套
ListView 的子项可以是另一个 ListView ,只不过两个嵌套的 ListView 不能是相同的方向
举个例子,ListViewVertival
是一个纵向的 ListView Widget,嵌套在了一个横向的 ListView 中
return ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
ListItem(),
ListItem(),
ListViewVertival(),
ListItem(),
ListItem(),
ListItem(),
ListItem(),
],
padding: EdgeInsets.all(12),
);
4、综合使用
将横向 ListView 和 纵向 ListView 嵌套使用,ListItem 是一个子项,不重复代码,完整代码可以在文章最后获取:
class ListViewDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
ListItem(),
ListItem(),
ListViewVertival(),
ListItem(),
ListItem(),
ListItem(),
ListItem(),
],
padding: EdgeInsets.all(12),
);
}
}
class ListViewVertival extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: ListView(
children: <Widget>[
ListItem(),
ListItem(),
ListItem(),
ListItem(),
],
),
width: 100,
height: 500,
);
}
}
实现的效果:
五、代码
基础 ListView 和 ListTile 的各种使用:
使用列表数据动态渲染 ListView:
横向列表与列表嵌套:
文章版权:Postbird-There I am , in the world more exciting!
本文链接:http://www.ptbird.cn/flutter-listview-listview-builder.html
转载请注明文章原始出处 !