Flutter可滚动Widgets-ListView

个人博客

ListView

先看下如下截图

enter image description here

以上效果图的代码,是从flutter官方demoflutter_gallery内copy的部分代码。
首先,首先定义一个列表,代码如下

List<String> items = <String>[
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
];

然后,通过上面的定义的列表数据,现在构建ListView的子Widget数据,代码如下

Iterable<Widget> listTiles = items
    .map<Widget>((String item) => buildListTile(context, item));

Widget buildListTile(BuildContext context, String item) {
    Widget secondary = const Text(
      'Even more additional list item information appears on line three.',
    );
    return ListTile(
      isThreeLine: true,
      leading: ExcludeSemantics(child: CircleAvatar(child: Text(item))),
      title: Text('This item represents $item.'),
      subtitle: secondary,
      trailing: Icon(Icons.info, color: Theme.of(context).disabledColor),
    );
}

最后,将生成的子Widget数据填充到ListView内,代码如下

ListView(
    children: listTiles.toList(),
)

以上代码,就能完成最上面截图的效果。下面主要对ListTile做一下介绍

ListTile

ListTileFlutter给我们准备好的widget提供非常常见的构造和定义方式,包括文字,icon,点击事件,一般是能够满足基本列表需求。

构造函数

ListTile({
    Key key,
    this.leading,
    this.title,
    this.subtitle,
    this.trailing,
    this.isThreeLine = false,
    this.dense,
    this.contentPadding,
    this.enabled = true,
    this.onTap,
    this.onLongPress,
    this.selected = false,
 })

属性

enter image description here

使用

ListTile(
    //展示三行
    isThreeLine: true,
    //前置图标
    leading: ExcludeSemantics(child: CircleAvatar(child: Text(item))),
    //标题
    title: Text('This item represents $item.'),
    //副标题
    subtitle: secondary,
    //后置图标
    trailing: Icon(Icons.info, color: Theme.of(context).disabledColor),
)

效果

enter image description here

ListView.builder

ListView.builder适合列表项比较多(或者无限)的情况,因为只有当子Widget真正显示的时候才会被创建。
将上面列表填充的代码修改为ListView.builder,代码如下所示

ListView.builder(
    itemCount: items.length,
    itemBuilder: (BuildContext context, int index) {
        return buildListTile(context, items[index]);
})

运行结果如下图所示


enter image description here

ListView.separated

ListView.separated可以生成列表项之间的分割器,它比ListView.builder多了一个separatorBuilder参数,该参数是一个分割器生成器。
将上面列表填充的代码修改为ListView.separated,代码如下所示

ListView.separated(
    itemBuilder: (BuildContext context, int index) {
        return buildListTile(context, items[index]);
    },
    separatorBuilder: (BuildContext context, int index) {
        return index % 2 == 0 ? divider1 : divider2;
    },
    itemCount: items.length
)

运行结果如下图所示


enter image description here

实例:Listview下拉刷新 上拉加载更多

下面实现首次进入页面,加载数据,下拉能刷新页面数据,上拉能加载更多数据。

下拉刷新

下拉刷新,用到的是Flutter自带的RefreshIndicatorWidget,ListView主要用ListView.builder进行实现。代码如下所示

RefreshIndicator(
    key: refreshIndicatorKey,
    child: ListView.builder(
        itemCount: list.length,
        itemBuilder: (context, index) {
            return buildListTile(context, list[index]);
        },
    ),
    onRefresh: onRefresh)

实现下拉刷新,主要需要实现RefreshIndicatoronRefresh属性,代码如下所示

Future<Null> onRefresh() async {
    return Future.delayed(Duration(seconds: 2)).then((e) {
      list.addAll(items);
      setState(() {
        //重新构建列表
      });
    });
}

主要实现延迟2s加载数据,在重新刷新列表。
首次进入页面,Loading状态的实现实现如下面代码所示

void showRefreshLoading() async {
    await Future.delayed(const Duration(seconds: 0), () {
      refreshIndicatorKey.currentState.show().then((e) {});
      return true;
    });
}

Loading完之后会触发RefreshIndicatoronRefresh属性,到此,下拉刷新已经实现完毕。
运行效果如下图所示

enter image description here

上拉加载更多

上拉加载需要监听ListView的滚动事件,当滚动事件与底部小于50并且有更多数据加载时,才会触发加载更多的逻辑,如下面代码所示

scrollController.addListener(() {
    var position = scrollController.position;
    // 小于50px时,触发上拉加载;
    if (position.maxScrollExtent - position.pixels < 50 &&
        !isNoMore) {
        loadMore();
    }
});

void loadMore() async {
    if (!isLoading) {
      //刷新加载状态
      isLoading = true;
      setState(() {});

      Future.delayed(Duration(seconds: 2)).then((e) {
        list.addAll(items);
        //取消加载状态,并提示暂无更多数据
        isLoading = false;
        isNoMore = true;
        setState(() {
          //重新构建列表
        });
      });
    }
}

视图层的代码,当需要处理加载更多的逻辑时,ListViewitemCount属性需要进行加1,用来填充加载更多的视图。如下面代码所示

int getListItemCount() {
    int count = list.length;
    if (isLoading || isNoMore) {
      count += 1;
    }
    return count;
}

ListViewitemBuilder属性,加载更多的视图代码如下所示

Widget builderMore() {
    return Center(
      child: Padding(
        padding: EdgeInsets.all(10.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            isNoMore
                ? Text("")
                : SizedBox(
                    width: 20.0,
                    height: 20.0,
                    child: CircularProgressIndicator(
                        strokeWidth: 4.0,
                        valueColor: AlwaysStoppedAnimation(Colors.black)),
                  ),
            Padding(
              padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 15.0),
              child: Text(
                isNoMore ? "没有更多数据" : "加载中...",
                style: TextStyle(fontSize: 16.0),
              ),
            ),
          ],
        ),
      ),
    );
  }

RefreshIndicator代码做如下修改

RefreshIndicator(
    key: refreshIndicatorKey,
    child: ListView.builder(
        controller: scrollController,
        itemCount: getListItemCount(),
        itemBuilder: (context, index) {
              return builderItem(context, index);
        },
    ),
    onRefresh: onRefresh)
          
Widget builderItem(BuildContext context, int index) {
    if (index < list.length) {
      return buildListTile(context, list[index]);
    }
    return builderMore();
}

运行代码
加载中的效果如下图所示


enter image description here

没有更多数据的效果如下图所示


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

推荐阅读更多精彩内容