我们在开发中,经常会需要显示列表,在iOS中我们使用UITableView来实现列表,那么在Flutter中,对应的部件为ListView;我们使用ListView来初步实现一个简单的列表界面;

ListView的使用

model模型

既然要显示一个列表,那么我们就需要在列表中显示一个数据模型,这里我们创建一个article.dart文件,在其中创建一个模型:

  1. class Article {
  2. const Article(this.name, this.iconUrl); // 构造函数
  3. final String name;
  4. final String iconUrl;
  5. }

模型的创建方式还有另外一种,如下:

  1. class Article {
  2. const Article({this.name, this.iconUrl}); // 构造函数 可选赋值
  3. final String? name; // ? 表示空安全
  4. final String? iconUrl;
  5. }

那么,这两种创建方式有什么区别呢?

  • const Article(this.name, this.iconUrl);是一个构造函数,初始化时nameiconUrl必须赋值;
  • const Article({this.name, this.iconUrl});也是一个构造函数,其初始化时nameiconUrl可以不进行赋值,但是此时需要注意必须满足以下两个条件中的任一个:
    • nameiconUrl必须有默认值;
    • nameiconUrl设置为空安全;

      List数据源

      为了方便,我们直接在article.dart文件中创建一个数组,来存放Article类作为列表的数据源;
      1. final List<Article> datas = [] // 具体内容没有放出来
      此时,article.dart文件如下:
      image.png

      ListView

      在使用之前,我们首先需要知道如果创建ListView,我们在使用ListView的时候一般不直接进行构造创建,而是使用其命名构造函数进行创建;创建代码如下:
      1. ListView.builder(itemBuilder: itemBuilder)
      builder就是在创建ListView的时候,我们需要一个渲染;itemBuilder是一个方法回调;根据其定义
      1. required IndexedWidgetBuilder itemBuilder,
      我们了解到其返回的是IndexedWidgetBuilder
      1. typedef IndexedWidgetBuilder = Widget Function(BuildContext context, int index);
      此函数返回了一个Widget,我们注意到,函数中有两个参数contextindex,这个时候,根据以往iOS开发经历,我们马上意识到其作用类似于UITableView中的cellForRow方法;

我们依据此函数,创建一个返回Widget的方法:

  1. Widget _itemForRow(BuildContext context, int index) {
  2. return null
  3. }
  4. @override
  5. Widget build(BuildContext context) {
  6. return Scaffold(
  7. appBar: AppBar(
  8. title: const Text('Flutter 工程'),
  9. ),
  10. body: ListView.builder(
  11. itemBuilder: _itemForRow,
  12. itemCount: datas.length,
  13. ),
  14. );
  15. }
  • _表示内部访问,文件内部!
  • _itemForRow方法返回每一个cell的视图;
  • itemCount方法传递整个列表中数据个数;

显示文章标题

我们先简单返回一个Text部件显示文章标题:

  1. Widget _itemForRow(BuildContext context, int index) {
  2. return Text(datas[index].name);
  3. }

注意,如果name属性为空安全的,那么此时需要进行强制解包:

  1. Widget _itemForRow(BuildContext context, int index) {
  2. return Text(datas[index].name!);
  3. }

运行效果:
image.png

显示封面图片

加载图片时,我们使用Image部件;

  1. Image.network(’‘)

我们将_itemForRow函数代码修改如下:

  1. Widget _itemForRow(BuildContext context, int index) {
  2. return Container(
  3. color: Colors.white,
  4. margin: const EdgeInsets.all(10),
  5. child: Image.network(datas[index].iconUrl),
  6. );
  7. }

Container是一个常用的小部件,方便我们进行布局

显示效果:
image.png

显示标题和封面

我们发现Container部件的child只能显示一个部件,这样我们就不能同时显示文章的标题和封面了;

那么,有没有一个部件,它可以存放多个部件呢?我们希望在封面的下方能显示出标题,封面和标题竖着排列,这个时候,就有Column部件能够达到这个效果,并且Column部件有children属性是个数组,能够同时存放多个部件:

  1. Widget _itemForRow(BuildContext context, int index) {
  2. return Container(
  3. color: Colors.white,
  4. margin: const EdgeInsets.all(10),
  5. child: Column(
  6. children: [
  7. Image.network(datas[index].iconUrl),
  8. Container(height: 10,),
  9. Text(
  10. datas[index].name,
  11. style: const TextStyle(
  12. fontSize: 20,
  13. ),)
  14. ],
  15. ),
  16. );
  17. }

Container(height: 10,), :封面与标题之间间隔10像素的距离,同样的效果SizedBox部件可能实现;

效果如下:
image.png
完整代码如下:

  1. class ListViewDemo extends StatelessWidget {
  2. // _的内部是指 文件内部!
  3. Widget _itemForRow(BuildContext context, int index) {
  4. return Container(
  5. color: Colors.white,
  6. margin: const EdgeInsets.all(10),
  7. child: Column(
  8. children: [
  9. Image.network(datas[index].iconUrl),
  10. const SizedBox(height: 10,),
  11. Text(
  12. datas[index].name,
  13. style: const TextStyle(
  14. fontWeight: FontWeight.w800,
  15. fontSize: 20,
  16. ),)
  17. ],
  18. ),
  19. );
  20. }
  21. @override
  22. Widget build(BuildContext context) {
  23. return Scaffold(
  24. backgroundColor: Colors.grey,
  25. appBar: AppBar(
  26. title: const Text('ListView的使用'),
  27. ),
  28. body: ListView.builder(
  29. itemBuilder: _itemForRow,
  30. itemCount: datas.length,
  31. ),
  32. );
  33. }
  34. }

Flutter小知识

部件的布局只有三种:

  • 竖着
  • 横着
  • 叠着