No more than code.
Flutter ListView & GridView
| ListView
- 默认构造函数 数据较少时,可以直接使用如下方式(一次加载所有的组件,没有“懒加载”,和使用 SingleChildScrollView+Column 的方式没有本质的区别):
ListView(
children: <Widget>[
_ListItem(title: '1',),
_ListItem(title: '2',),
_ListItem(title: '3',),
_ListItem(title: '4',),
_ListItem(title: '5',),
_ListItem(title: '6',),
],
)
// _ListItem 定义如下:
class _ListItem extends StatelessWidget {
final String title;
const _ListItem({Key key, this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
child: Container(
height: 45,
alignment: Alignment.center,
child: Text('$title'),
),
);
}
}
- ListView.builder 当有大量数据时,使用动态创建列表的方式(当子组件真正显示的时候才会被创建,也就说通过该构造函数创建的 ListView 是支持基于 Sliver 的懒加载模型的):
ListView.builder(
// 列表项的构建器,类型为IndexedWidgetBuilder,返回值为一个widget。当列表滚动到具体的index位置时,会调用该构建器构建列表项
itemBuilder: (BuildContext context, int index) {
return _ListItem(
title: '$index',
);
},
itemExtent: 50, // 每一个Item的高度
itemCount: 30, // 个数
scrollDirection: Axis.horizontal, // 滚动方向,默认是垂直方向,可以设置为水平方向。
reverse: false, //是否反转滚动方向,比如当前滚动方向是垂直方向,reverse : true,滚动方向为从上倒下,reverse:false,滚动方向为从下倒上。
shrinkWrap: true,
)
在 ListView 中,指定 itemExtent 比让子组件自己决定自身长度会更高效,这是因为指定 itemExtent 后,滚动系统可以提前知道列表的长度,而无需每次构建子组件时都去再计算一下,尤其是在滚动位置频繁变化时(滚动系统需要频繁去计算列表高度)
shrinkWrap:该属性表示是否根据子组件的总长度来设置 ListView 的长度,默认值为 false 。默认情况下,ListView 的会在滚动方向尽可能多的占用空间。当 ListView 在一个无边界(滚动方向上)的容器中时,shrinkWrap 必须为 true。
ScrollController 是 ListView 组件的控制器,通过 ScrollController 可以获取当前滚动的位置,也可以滚动到指定的位置。用法如下:
class _ListViewDemoState extends State<ListViewDemo> {
ScrollController _controller;
@override
void initState() {
_controller = ScrollController()
..addListener(() {
print('${_controller.position}');
});
super.initState();
}
@override
Widget build(BuildContext context) {
return ListView.builder(
controller: _controller,
reverse: false,
itemBuilder: (BuildContext context, int index) {
return _ListItem(
title: '$index',
);
},
itemCount: 30,
itemExtent: 50,
);
}
}
// 指定跳转到指定位置:
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Container(
child: RaisedButton(
child: Text('滚动到指定位置'),
onPressed: () {
_controller.animateTo(200,
duration: Duration(milliseconds: 300), curve: Curves.linear);
},
),
),
Expanded(
child: ListView.builder(
controller: _controller,
reverse: false,
itemBuilder: (BuildContext context, int index) {
return _ListItem(
title: '$index',
);
},
itemCount: 30,
itemExtent: 50,
),
)
],
);
}
physics 参数表示当滚动到顶部或者底部时滚动的物理特性。比如设置为不可滚动:
系统提供的 ScrollPhysics 有:
- AlwaysScrollableScrollPhysics:总是可以滑动
- NeverScrollableScrollPhysics:禁止滚动
- BouncingScrollPhysics :内容超过一屏 上拉有回弹效果
- ClampingScrollPhysics :包裹内容 不会有回弹
ListView(
physics: NeverScrollableScrollPhysics(),
···
)
- ListView.separated 可以在生成的列表项之间添加一个分割组件,它比 ListView.builder 多了一个 separatorBuilder 参数,该参数是一个分割组件生成器
ListView.separated(
itemBuilder: (BuildContext context, int index) {
return Container(
height: 45,
alignment: Alignment.center,
child: Text('$index'),
);
},
separatorBuilder: (BuildContext context, int index){
return Divider();
},
itemCount: 30,
)
列表上方添加标题:
@override
Widget build(BuildContext context) {
return Column(children: <Widget>[
ListTile(title:Text("商品列表")),
Expanded( // 解决ListView高度边界无法确定
child: ListView.builder(itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text("$index"));
}),
),
]);
}
和 ListTile 配合使用
ListTile 支持的参数非常多,常用是 onTap、leading、trailing、title、subtitle
一个 ListTile 也由 leading、trailing 和 title + subtitle 组成
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,
),
);
通过函数生成 List
方法 _getNewsList() 最终返回 List
List<Widget> _getNewsList() {
return newsList.news.map((item) {
return ListItem(
title: '${item['title']}',
subTitle: '${item['time']}',
cover: '${item['imgurl']}',
);
}).toList();
}
| GridView
基本用法:
GridView(
scrollDirection: Axis.horizontal, // 同listview
reverse: true, // // 同listview
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, //横轴三个子 widget
childAspectRatio: 1.0 //宽高比为 1 时,子 widget
),
children:<Widget>[
Icon(Icons.ac_unit),
Icon(Icons.airport_shuttle),
Icon(Icons.all_inclusive),
Icon(Icons.beach_access),
Icon(Icons.cake),
Icon(Icons.free_breakfast)
]
);
gridDelegate 类型是 SliverGridDelegate,它的作用是控制 GridView 子组件如何排列(layout),有 2 个选择:
- SliverGridDelegateWithFixedCrossAxisCount:交叉轴方向上固定数量,对于垂直方向的 GridView 来说交叉轴方向指的是水平方向。
- SliverGridDelegateWithMaxCrossAxisExtent:交叉轴方向上尽量大,比如水平方上有 500 空间,指定此值为 150,那么可以放 3 个,剩余一些空间,此时 GridView 将会缩小每一个 Item,放置 4 个。
SliverGridDelegateWithFixedCrossAxisCount 有属性介绍如下:
- crossAxisCount:交叉轴方向上个数。
- mainAxisSpacing:主轴方向上 2 行之间的间隔。
- crossAxisSpacing:交叉轴方向上之间的间隔。
- childAspectRatio:子控件宽高比。
controller 和 physics 属性用法同 ListView 中一样
GridView 提供了一些快速构建的方法
- GridView.builder
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
// 构建子控件
itemBuilder: (context, index) {
return Container(
height: 80,
color: Colors.primaries[index % Colors.primaries.length],
);
},
itemCount: 50, // 指定数据个数
)
- GridView.custom
GridView.custom(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
childrenDelegate: SliverChildBuilderDelegate((context, index) {
return Container(
height: 80,
color: Colors.primaries[index % Colors.primaries.length]);
}, childCount: 50),
)
- GridView.count
body: new GridView.count(
crossAxisCount: 4, // 交叉轴方向上个数
childAspectRatio: 0.7, // 子控件宽高比。
mainAxisSpacing: 5, // 主轴方向上2行之间的间隔。
crossAxisSpacing: 36, // 交叉轴方向上之间的间隔。
children: List.generate(100, (index){
return new Center(
child: new Text(
'Item ${index}',
style: Theme.of(context).textTheme.headline,
),
);
})
)
- GridView.extent
GridView.extent(
maxCrossAxisExtent: 100,
children: List.generate(50, (i) {
return Container(
height: 80,
color: Colors.primaries[i % Colors.primaries.length],
);
}),
)