Flutter入门进阶之旅(十四)ListView&GridView

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

1.ListView

1.1 ListView简单列表

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

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

ListView简单列表

上述效果图样例代码

import 'package:flutter/material.dart';

void main() {
  runApp(new MaterialApp(
    title: "ListView",
    home: new MyApp(),
  ));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("ListView"),
        ),
        body: ListView(
          scrollDirection: Axis.vertical, //控制列表方向
          children: <Widget>[
            new Container(
              width: 100.0,
              height: 100.0,
              color: Colors.lightBlue,
            ),
            new Container(
              width: 100.0,
              height: 100.0,
              color: Colors.red,
            ),
            new Container(
              width: 100.0,
              height: 100.0,
              color: Colors.deepPurple,
            ),
            new Container(
              width: 100.0,
              height: 100.0,
              color: Colors.yellow,
            ),
            new Container(
              width: 100.0,
              height: 100.0,
              color: Colors.lightBlue,
            ),
            new Container(
              width: 100.0,
              height: 100.0,
              color: Colors.red,
            ),
            new Container(
              width: 100.0,
              height: 100.0,
              color: Colors.deepPurple,
            ),
            new Container(
              width: 100.0,
              height: 100.0,
              color: Colors.lightBlue,
            ),
            new Container(
              width: 100.0,
              height: 100.0,
              color: Colors.red,
            ),
            new Container(
              width: 100.0,
              height: 100.0,
              color: Colors.deepPurple,
            ),
            new Container(
              width: 100.0,
              height: 100.0,
              color: Colors.lightBlue,
            ),
          ],
        ));
  }
}

上述代码中我们利用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自行测试。

ListView.builder({
    Key key,
    Axis scrollDirection: Axis.vertical,
    bool reverse: false,
    ScrollController controller,
    bool primary,
    ScrollPhysics physics,
    bool shrinkWrap: false,
    EdgeInsetsGeometry padding,
    this.itemExtent,
    @required IndexedWidgetBuilder itemBuilder,//item构建者
    int itemCount,//item数量
    bool addAutomaticKeepAlives: true,
    bool addRepaintBoundaries: true,
  })

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

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

下面我们来看下效果图:

长列表

样例代码

import 'package:flutter/material.dart';

void main() {
  runApp(new MaterialApp(
    title: "ListView",
    home: new MyApp(),
  ));
}

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => MyState();
}

class MyState extends State {
  List<ItemEntity> entityList = [];

  @override
  void initState() {
    super.initState();
    for (int i = 0; i < 30; i++) {
      entityList.add(ItemEntity("Item  $i", Icons.accessibility));
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("ListView"),
        ),
        body: ListView.builder(
          itemBuilder: (BuildContext context, int index) {
            return ItemView(entityList[index]);
          },
          itemCount: entityList.length,
        ));
  }
}

/**
 * 渲染Item的实体类
 */
class ItemEntity {
  String title;
  IconData iconData;

  ItemEntity(this.title, this.iconData);
}

/**
 * ListView builder生成的Item布局,读者可类比成原生Android的Adapter的角色
 */
class ItemView extends StatelessWidget {
  ItemEntity itemEntity;

  ItemView(this.itemEntity);

  @override
  Widget build(BuildContext context) {
    return Flex(
      direction: Axis.vertical,
      children: <Widget>[
        ListTile(
            leading: Icon(itemEntity.iconData), title: Text(itemEntity.title),subtitle: Text('长列表')),
        SizedBox(
          height: 0.2,
          child: Container(
            color: Colors.black,
          ),
        )
      ],
    );
  }
}

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

2.GridView

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

构造方法

GridView({
    Key key,
    Axis scrollDirection: Axis.vertical,
    bool reverse: false,
    ScrollController controller,
    bool primary,
    ScrollPhysics physics,
    bool shrinkWrap: false,
    EdgeInsetsGeometry padding,
    @required this.gridDelegate,   //控制GridView显示方式
    bool addAutomaticKeepAlives: true,
    bool addRepaintBoundaries: true,
    List<Widget> children: const <Widget>[],
  })

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

gridDelegate参数说明

gridDelegate可接收两种参数类型:

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

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

效果图

GridView

样例代码

import 'package:flutter/material.dart';

void main() {
  runApp(new MaterialApp(
    title: "ListView",
    home: new MyApp(),
  ));
}

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => MyState();
}
class MyState extends State {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("ListView"),
        ),
        body: new GridView(
//          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
//              crossAxisCount: 3, //固定显示三个
//              mainAxisSpacing: 10,
//              crossAxisSpacing: 10),
          gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
              maxCrossAxisExtent: 100, //根据maxCrossAxisExtent的值对比屏幕的真实宽度,决定一行或者一列显示多少个Item
              mainAxisSpacing: 10,
              crossAxisSpacing: 10),
          children: <Widget>[
            Container(
                child: Icon(Icons.adb, size: 60),
                color: Colors.deepOrangeAccent),
            Container(
                child: Icon(Icons.adb, size: 60),
                color: Colors.deepOrangeAccent),
            Container(
                child: Icon(Icons.adb, size: 60),
                color: Colors.deepOrangeAccent),
            Container(
                child: Icon(Icons.adb, size: 60),
                color: Colors.deepOrangeAccent),
            Container(
                child: Icon(Icons.adb, size: 60),
                color: Colors.deepOrangeAccent),
            Container(
                child: Icon(Icons.adb, size: 60),
                color: Colors.deepOrangeAccent),
            Container(
                child: Icon(Icons.adb, size: 60),
                color: Colors.deepOrangeAccent),
            Container(
                child: Icon(Icons.adb, size: 60),
                color: Colors.deepOrangeAccent),
          ],
        ));
  }
}

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

效果图

GridView

样例代码

import 'package:flutter/material.dart';

void main() {
  runApp(new MaterialApp(
    title: "ListView",
    home: new MyApp(),
  ));
}

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => MyState();
}

class MyState extends State {
  List<ItemEntity> entityList = [];

  @override
  void initState() {
    super.initState();
    for (int i = 0; i < 30; i++) {
      entityList.add(ItemEntity("Item  $i", Icons.accessibility));
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("GridView"),
        ),
        body: GridView.custom(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 3,
              crossAxisSpacing: 10,
              mainAxisSpacing: 10,
            ),
            childrenDelegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                return ItemView(entityList[index]);
              },
              childCount: entityList.length,
            )));
  }
}

/**
 * 渲染Item的实体类
 */
class ItemEntity {
  String title;
  IconData iconData;

  ItemEntity(this.title, this.iconData);
}

/**
 * GridView builder生成的Item布局,读者可类比成原生Android的Adapter的角色
 */
class ItemView extends StatelessWidget {
  ItemEntity itemEntity;

  ItemView(this.itemEntity);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Flex(
        direction: Axis.vertical,
        children: <Widget>[
          Icon(
            itemEntity.iconData,
            size: 60,
          ),
          Text(itemEntity.title),
        ],
      ),
      onTap: () {
        Scaffold.of(context).showSnackBar(
            new SnackBar(content: Text("点击了${itemEntity.title}")));
      },
    );
  }
}

总结

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

下一篇:Flutter入门进阶之旅(十五)ListView下拉刷新&上拉加载更多

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 一、适用场景 ListViewListview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用...
    Geeks_Liu阅读 10,503评论 1 28
  • 国庆后面两天在家学习整理了一波flutter,基本把能撸过能看到的代码都过了一遍,此文篇幅较长,建议保存(star...
    Nealyang阅读 4,314评论 1 17
  • 对于持之以恒的做一件事儿,一周应该是最小的计数单位吧!当然,那些爱情上头,一见钟情,以秒来计算时间的除外,细数自己...
    灰玫瑰汪然阅读 461评论 8 11
  • 我最近看到了一句话,他说:“不管将来你的身边有谁,也不可能存在一个人完完全全明白你的所思所想,哪怕是你未来的伴侣也...
    暖心怪人阅读 128评论 0 0
  • 昨天傍晚吃饭,奶奶跟我说暖小姐在家说的那些的话,笑到不行。奶奶记录了部分,边读她在一边边笑。大体是因为她自己...
    小猪天堂阅读 104评论 2 1