likes
comments
collection
share

Flutter中ListView中的使用

作者站长头像
站长
· 阅读数 39

列表的使用场景

列表是在软件开发中经常使用的一种视图控件,便于显示结构化的数据,列表显示的是排序的数据,可以根据不同的排序规则进行显示(比如说订单列表可以根据时间正序排列,也可以按时间倒序排列,同样可以按照金额大小进行排序)。数据量小的时候,可能不用占满屏幕。如果数据量巨大的时候,可以占满屏幕,同时支持滚动条显示数据。列表的应用广泛,比如说显示朋友列表,商品列表,聊天记录列表,订单列表等等。Flutter中的ListView Widget可以帮助你实现一个列表。

列表的两种排列布局

ListView显示排列布局可以分为横向布局和纵向布局,也就是滚动方向是按水平滚动还是垂直滚动。ListView中还有一个属性是否逆向排序,示例代码如下所示:

  body: ListView(
          scrollDirection: Axis.horizontal,
          reverse: false,
          children: <Widget>[
            for (int i = 0; i < 10; i++)
              SizedBox(
                width: 100,
                child: ListTile(
                  title: Text('item $i'),
                ),
              )
          ],
        ),
  1. scrollDirection代表滚动方向,Axis.horizontal代表水平方向,Axis.vertical代表垂直方向(默认)。
  2. reverse代表是否逆向排序,false代表正向排序(默认),true代表逆向排序。

不同类型的元素列表

我们经常需要创建展示不同类型内容的列表。比方说,我们可能在开发一个列表,它显示一个标题,后跟一些与标题相关的项目,然后是另一个标题,依此类推。比方说,我们可能开发一个和朋友的单独聊天页面,朋友的聊天内容靠左显示,显示用户名称和用户头像;自己的聊天内容靠右显示,仅仅显示用户头像。 效果图如下所示:

Flutter中ListView中的使用 完整代码如下:

import 'package:flutter/material.dart';

class Message {
  final bool isMine;
  final String name;
  final String avaUrl;
  final String message;
  Message(this.isMine, this.name, this.avaUrl, this.message);
}

abstract class MessageItem {
  Widget build(BuildContext context);
}

class MyMessage extends MessageItem {
  final Message message;
  MyMessage(this.message);
  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.end,
      children: <Widget>[
        Text(message.message),
        const SizedBox(
          width: 10,
        ),
        Image.asset(
          message.avaUrl,
          width: 40,
          height: 40,
        ),
        const SizedBox(
          width: 10,
        ),
      ],
    );
    ;
  }
}

class FriendMessage extends MessageItem {
  final Message message;
  FriendMessage(this.message);
  @override
  Widget build(BuildContext context) {
    return Row(
      children: <Widget>[
        Image.asset(
          message.avaUrl,
          width: 40,
          height: 40,
        ),
        const SizedBox(
          width: 10,
        ),
        Column(
          children: <Widget>[
            Text(message.name),
            Text(message.message),
          ],
        ),
      ],
    );
  }
}

void main() {
  var messages = <Message>[];
  messages.add(Message(false, "老李", "images/friend.png", "干什么呢"));
  messages.add(Message(true, "技术蔡蔡", "images/my.jpg", "吃饭呢"));
  messages.add(Message(false, "老李", "images/friend.png", "下午去干什么?"));
  messages.add(Message(true, "技术蔡蔡", "images/my.jpg", "下午去打球"));
  runApp(
    MyApp(
      items: messages,
    ),
  );
}

class MyApp extends StatelessWidget {
  final List<Message> items;

  const MyApp({super.key, required this.items});

  @override
  Widget build(BuildContext context) {
    final title = items.firstWhere((element) => !element.isMine).name;

    return MaterialApp(
      title: title,
      home: Scaffold(
        appBar: AppBar(
          title: Text(title),
        ),
        body: ListView.builder(
          itemCount: items.length,
          itemBuilder: (context, index) {
            final item = items[index];
            return item.isMine
                ? MyMessage(item).build(context)
                : FriendMessage(item).build(context);
          },
        ),
      ),
    );
  }
}

  1. 首先通过拉取和某个人(老李)的聊天记录(这里mock的数据)。
  2. 根据相同的数据源(Message)是否是自己的来创建不同的MessageItem,MessageItem子类分为FriendMessage和MyMessage,他们两者直接会创建不同的Widget,显示不同的页面元素。
  3. ListView.builder中具体显示构建不同的Widget的业务逻辑。

这个聊天记录页面还有需要优化的部分,比如聊天时间片段有间隔的时候需要显示时间,还有可以发送聊天的输入框,以及发送消息过程中的动画等效果。这里仅仅为了展示不同类型的列表,所以就没有完善。

ListView的两种构建方式的对比

标准的ListView构造函数适用于短列表,对于具有大量列表项的长列表,需要用ListView.builder构造函数来创建。

与标准的ListView构造函数需要一次性创建所有列表项不同的是,ListView.builder构造函数只在列表项从屏幕外滑入屏幕时才去创建列表项。

两种构建方式的不同导致,标准的ListView构造函数内部有大量数据的时候,会导致内存使用超过内存分配的问题,从而导致应用崩溃。

Flutter中ListView中的使用 代码如下:

 body: ListView(
          // This next line does the trick.
          scrollDirection: Axis.vertical,
          reverse: false,
          children: <Widget>[
            for (int i = 0; i < 10000000000; i++)
              SizedBox(
                width: 100,
                child: ListTile(
                  title: Text('item $i'),
                ),
              )
          ],
        ),

当遇到加载大量数据的时候,可以考虑下面这几种方式:

  1. 我们可以用ListView.builder构建。
  2. 我们可以用分页的方式来分批次请求数据。

ListView当需要用分隔符的时候可以使用ListView.separated构建,separatorBuilder这个属性就是设置分隔符的。

ListView还有很多其他的属性,比如prototypeItem,physics,addAutomaticKeepAlives,cacheExtent等等,我就不一一介绍了,大家可以多查查文档看看源码,多多练习就可以了。

总结

大量结构化的数据如何安全高效地显示,一直是一个很有意思的课题。ListView是Flutter中经常使用的一个Widget,希望文章对您有帮助,如果文中有问题,希望您不吝指教。

转载自:https://juejin.cn/post/7390957334418751540
评论
请登录