在之前讲 Layout Widget的文章中,我们掌握了基于不同的场景适当的选择不同的Widget来完成我们的布局要求,但是关于长列表的数据展示我们并没有做展开介绍,而长列表的身影几乎出现在日常生活中的任意一款APP中,鉴于它的重要性,所以我想单独作为一个章节来讲解长列表Widget—ListView&GirdView

1.ListView

1.1 ListView简单列表

看下ListView的构造方法,然后我们用listview来完成一个简单列表

  1. ListView({
  2. Key key,
  3. Axis scrollDirection: Axis.vertical,//滚动方向
  4. bool reverse: false,//是否反向显示数据
  5. ScrollController controller,
  6. bool primary,
  7. ScrollPhysics physics,//物理滚动
  8. bool shrinkWrap: false,
  9. EdgeInsetsGeometry padding,
  10. this.itemExtent,//item有效范围
  11. bool addAutomaticKeepAlives: true,//自动保存视图缓存
  12. bool addRepaintBoundaries: true,//添加重绘边界
  13. List<Widget> children: const <Widget>[],
  14. })

14-Flutter入门进阶之ListViewGridView - 图1
上述效果图样例代码

  1. import 'package:flutter/material.dart';
  2. void main() {
  3. runApp(new MaterialApp(
  4. title: "ListView",
  5. home: new MyApp(),
  6. ));
  7. }
  8. class MyApp extends StatelessWidget {
  9. @override
  10. Widget build(BuildContext context) {
  11. return new Scaffold(
  12. appBar: new AppBar(
  13. title: new Text("ListView"),
  14. ),
  15. body: ListView(
  16. scrollDirection: Axis.vertical, //控制列表方向
  17. children: <Widget>[
  18. new Container(
  19. width: 100.0,
  20. height: 100.0,
  21. color: Colors.lightBlue,
  22. ),
  23. new Container(
  24. width: 100.0,
  25. height: 100.0,
  26. color: Colors.red,
  27. ),
  28. new Container(
  29. width: 100.0,
  30. height: 100.0,
  31. color: Colors.deepPurple,
  32. ),
  33. new Container(
  34. width: 100.0,
  35. height: 100.0,
  36. color: Colors.yellow,
  37. ),
  38. new Container(
  39. width: 100.0,
  40. height: 100.0,
  41. color: Colors.lightBlue,
  42. ),
  43. new Container(
  44. width: 100.0,
  45. height: 100.0,
  46. color: Colors.red,
  47. ),
  48. new Container(
  49. width: 100.0,
  50. height: 100.0,
  51. color: Colors.deepPurple,
  52. ),
  53. new Container(
  54. width: 100.0,
  55. height: 100.0,
  56. color: Colors.lightBlue,
  57. ),
  58. new Container(
  59. width: 100.0,
  60. height: 100.0,
  61. color: Colors.red,
  62. ),
  63. new Container(
  64. width: 100.0,
  65. height: 100.0,
  66. color: Colors.deepPurple,
  67. ),
  68. new Container(
  69. width: 100.0,
  70. height: 100.0,
  71. color: Colors.lightBlue,
  72. ),
  73. ],
  74. ));
  75. }
  76. }

上述代码中我们利用ListView做了一个简单的长列表布局,通过给children: <Widget>[]传入我们要展示的子Widget来完成列表渲染,理论上我们可以传入任意多的子Widget,但是这点使用起来跟Column没有太大差别,这显然是不符合我们的预期的,因为我们在使用ListView展示长列表布局的时候,大多数情况下我们是不知道长列表一共有多少个元素,即使是能知道,我们重复性的把每个相似的布局都写一遍,也几乎让人崩溃。

没错,Flutter肯定给我们提供了像原生Android写Adapter一样的方式,我们可以提前写好公共的Item布局,然后通过ListView.builder()或者ListView.custom()方式自动生成长列表,ListView.builder()ListView.custom()的用法基本相同,只不过custom可以根据自己的需要控制Item显示方式,如Item显示大小。

1.2 可复用的ListView长列表

下面我带大家一起看下ListView.builder()的构造方法,然后使用ListView.builder写个简单的样式代码,至于ListView.custom的用法我会在下面介绍GridView的时候讲到,读者可参考Listview.bulider自行测试。

  1. ListView.builder({
  2. Key key,
  3. Axis scrollDirection: Axis.vertical,
  4. bool reverse: false,
  5. ScrollController controller,
  6. bool primary,
  7. ScrollPhysics physics,
  8. bool shrinkWrap: false,
  9. EdgeInsetsGeometry padding,
  10. this.itemExtent,
  11. @required IndexedWidgetBuilder itemBuilder,//item构建者
  12. int itemCount,//item数量
  13. bool addAutomaticKeepAlives: true,
  14. bool addRepaintBoundaries: true,
  15. })

从ListView.bulider的构造方法我们可以看出,它只比上述我们直接使用的ListView的构造方法多了两个参数,也正是这两个参数简化了我们对长列表的操作

itemCount::被展示的Item的数量
itemBuilder:被展示的Item的构造者(这里读者可以它类比成原生Android的Adapter)

下面我们来看下效果图:
14-Flutter入门进阶之ListViewGridView - 图2
样例代码

  1. import 'package:flutter/material.dart';
  2. void main() {
  3. runApp(new MaterialApp(
  4. title: "ListView",
  5. home: new MyApp(),
  6. ));
  7. }
  8. class MyApp extends StatefulWidget {
  9. @override
  10. State<StatefulWidget> createState() => MyState();
  11. }
  12. class MyState extends State {
  13. List<ItemEntity> entityList = [];
  14. @override
  15. void initState() {
  16. super.initState();
  17. for (int i = 0; i < 30; i++) {
  18. entityList.add(ItemEntity("Item $i", Icons.accessibility));
  19. }
  20. }
  21. @override
  22. Widget build(BuildContext context) {
  23. return new Scaffold(
  24. appBar: new AppBar(
  25. title: new Text("ListView"),
  26. ),
  27. body: ListView.builder(
  28. itemBuilder: (BuildContext context, int index) {
  29. return ItemView(entityList[index]);
  30. },
  31. itemCount: entityList.length,
  32. ));
  33. }
  34. }
  35. /**
  36. * 渲染Item的实体类
  37. */
  38. class ItemEntity {
  39. String title;
  40. IconData iconData;
  41. ItemEntity(this.title, this.iconData);
  42. }
  43. /**
  44. * ListView builder生成的Item布局,读者可类比成原生Android的Adapter的角色
  45. */
  46. class ItemView extends StatelessWidget {
  47. ItemEntity itemEntity;
  48. ItemView(this.itemEntity);
  49. @override
  50. Widget build(BuildContext context) {
  51. return Flex(
  52. direction: Axis.vertical,
  53. children: <Widget>[
  54. ListTile(
  55. leading: Icon(itemEntity.iconData), title: Text(itemEntity.title),subtitle: Text('长列表')),
  56. SizedBox(
  57. height: 0.2,
  58. child: Container(
  59. color: Colors.black,
  60. ),
  61. )
  62. ],
  63. );
  64. }
  65. }

上述代码中的逻辑读者大部分都可以自解的,我就不多做赘述了,下面我们进入本篇分享的第二部分,关于GridView部分的讲解。

2.GridView

跟原生Android一样, GridView满足了我们所有使用表格布局的场景,当然如果GridView在每行或者每列都只显示一个Item的时候又相当于ListView的使用场景。但是回到Flutter上 GridView的用法又跟ListView使用类似,可以直接new对象的方式生成简单的表格布局,也可以像ListView那样通过 builder&#xFF08;&#xFF09;&#x548C;custom&#xFF08;&#xFF09;方法来创建可复用的对象

构造方法

  1. GridView({
  2. Key key,
  3. Axis scrollDirection: Axis.vertical,
  4. bool reverse: false,
  5. ScrollController controller,
  6. bool primary,
  7. ScrollPhysics physics,
  8. bool shrinkWrap: false,
  9. EdgeInsetsGeometry padding,
  10. @required this.gridDelegate, //控制GridView显示方式
  11. bool addAutomaticKeepAlives: true,
  12. bool addRepaintBoundaries: true,
  13. List<Widget> children: const <Widget>[],
  14. })

上面提到GridView跟ListView使用方法类似,但是GridView不同于ListView的是它可以在一行或者一列显示多个Item,所以GridView的构造方法对比ListView多了一个 gridDelegate参数,来配置一行(列)有几个Item和Item的间隔。

gridDelegate参数说明

gridDelegate可接收两种参数类型:

  • SliverGridDelegateWithFixedCrossAxisCount可以直接指定每行(列)显示多少个Item
  • SliverGridDelegateWithMaxCrossAxisExtent会根据GridView的宽度和你设置的每个的宽度来自动计算没行显示多少个Item

先来看下GridView的效果图,关于 gridDelegate的区别我在代码里做了注释讲解,这里就不在多赘述了读者可结合代码自行理解。

效果图
14-Flutter入门进阶之ListViewGridView - 图3
样例代码

  1. import 'package:flutter/material.dart';
  2. void main() {
  3. runApp(new MaterialApp(
  4. title: "ListView",
  5. home: new MyApp(),
  6. ));
  7. }
  8. class MyApp extends StatefulWidget {
  9. @override
  10. State<StatefulWidget> createState() => MyState();
  11. }
  12. class MyState extends State {
  13. @override
  14. Widget build(BuildContext context) {
  15. return new Scaffold(
  16. appBar: new AppBar(
  17. title: new Text("ListView"),
  18. ),
  19. body: new GridView(
  20. // gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  21. // crossAxisCount: 3, //固定显示三个
  22. // mainAxisSpacing: 10,
  23. // crossAxisSpacing: 10),
  24. gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
  25. maxCrossAxisExtent: 100, //根据maxCrossAxisExtent的值对比屏幕的真实宽度,决定一行或者一列显示多少个Item
  26. mainAxisSpacing: 10,
  27. crossAxisSpacing: 10),
  28. children: <Widget>[
  29. Container(
  30. child: Icon(Icons.adb, size: 60),
  31. color: Colors.deepOrangeAccent),
  32. Container(
  33. child: Icon(Icons.adb, size: 60),
  34. color: Colors.deepOrangeAccent),
  35. Container(
  36. child: Icon(Icons.adb, size: 60),
  37. color: Colors.deepOrangeAccent),
  38. Container(
  39. child: Icon(Icons.adb, size: 60),
  40. color: Colors.deepOrangeAccent),
  41. Container(
  42. child: Icon(Icons.adb, size: 60),
  43. color: Colors.deepOrangeAccent),
  44. Container(
  45. child: Icon(Icons.adb, size: 60),
  46. color: Colors.deepOrangeAccent),
  47. Container(
  48. child: Icon(Icons.adb, size: 60),
  49. color: Colors.deepOrangeAccent),
  50. Container(
  51. child: Icon(Icons.adb, size: 60),
  52. color: Colors.deepOrangeAccent),
  53. ],
  54. ));
  55. }
  56. }

在本篇分享的第一部分我们介绍ListView的时候提到,为了复用Item布局,我们可以通过builder()或者custom来生成Item布局,在ListView中我们使用的是 builder()的方式生成的可复用的布局,在gridView中我带大家使用下 custom(),其实listView跟GridView使用类似,无非就是布局排列不同而已。

效果图

14-Flutter入门进阶之ListViewGridView - 图4

样例代码

  1. import 'package:flutter/material.dart';
  2. void main() {
  3. runApp(new MaterialApp(
  4. title: "ListView",
  5. home: new MyApp(),
  6. ));
  7. }
  8. class MyApp extends StatefulWidget {
  9. @override
  10. State<StatefulWidget> createState() => MyState();
  11. }
  12. class MyState extends State {
  13. List<ItemEntity> entityList = [];
  14. @override
  15. void initState() {
  16. super.initState();
  17. for (int i = 0; i < 30; i++) {
  18. entityList.add(ItemEntity("Item $i", Icons.accessibility));
  19. }
  20. }
  21. @override
  22. Widget build(BuildContext context) {
  23. return new Scaffold(
  24. appBar: new AppBar(
  25. title: new Text("GridView"),
  26. ),
  27. body: GridView.custom(
  28. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  29. crossAxisCount: 3,
  30. crossAxisSpacing: 10,
  31. mainAxisSpacing: 10,
  32. ),
  33. childrenDelegate: SliverChildBuilderDelegate(
  34. (BuildContext context, int index) {
  35. return ItemView(entityList[index]);
  36. },
  37. childCount: entityList.length,
  38. )));
  39. }
  40. }
  41. /**
  42. * 渲染Item的实体类
  43. */
  44. class ItemEntity {
  45. String title;
  46. IconData iconData;
  47. ItemEntity(this.title, this.iconData);
  48. }
  49. /**
  50. * GridView builder生成的Item布局,读者可类比成原生Android的Adapter的角色
  51. */
  52. class ItemView extends StatelessWidget {
  53. ItemEntity itemEntity;
  54. ItemView(this.itemEntity);
  55. @override
  56. Widget build(BuildContext context) {
  57. return GestureDetector(
  58. child: Flex(
  59. direction: Axis.vertical,
  60. children: <Widget>[
  61. Icon(
  62. itemEntity.iconData,
  63. size: 60,
  64. ),
  65. Text(itemEntity.title),
  66. ],
  67. ),
  68. onTap: () {
  69. Scaffold.of(context).showSnackBar(
  70. new SnackBar(content: Text("点击了${itemEntity.title}")));
  71. },
  72. );
  73. }
  74. }

总结

通过本篇博文我们了解到ListView跟GridView都可以直接用new对象的方式生成列表布局,一个用于表格布局,另一个用于长列表布局,也可以使用new 或者builder()和custom()方法来创建可复用对象,从而扩展了ListView跟GridView的灵活性,就像我们原生Android一样,把可复用的布局抽象成Adapter的形式,但是或许你总觉得关于列表还少点什么,下拉刷新?加载更多?