Flutter可滚动组件(2):ListView基本使用

 


ListView 是一个滚动列表组件,可以在垂直方向上(或水平方向,默认是垂直方向)展示一系列的子组件。一种最简单的使用方式是直接将所有需要排列的子 Widget 放在 ListView 的 children 属性中即可。

一、ListView的构造方法

ListView 有下面三种构造方法:

  • 常规方法,即直接使用 ListView 的构造函数
  • ListView.builder方法
  • ListView.separated方法

1.1 常规方法

ListView 的常规方法就是直接使用 ListView 的构造函数来构造 ListView 中的各个 item。其中 ListView 有一个 children 属性,它接收一个 widget 的 list,这个 list 就是 ListView 中要呈现的对象。

我们来构造一个拥有 100 个 item 的 ListView 对象:

import 'package:flutter/material.dart';

main(List<String> args) {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: MyHomeBody(),
      ),
    );
  }
}

class MyHomeBody extends StatelessWidget {
  const MyHomeBody({super.key});
  final TextStyle textStyle = const TextStyle(fontSize: 20, color: Colors.redAccent);

  @override
  Widget build(BuildContext context) {
    return  ListView(
      children: List<Widget>.generate(100, (i) => Text('列表 $i')),
    );
  }
}

效果图如下所示:

Flutter_view_O.png


1.2 ListView.builder方法

上面的例子中,我们简单的使用List.generate方法生成了 100 个对象。通过构造函数中的 children 传入所有的 item 有一个问题:默认会创建出所有的子 item。

在 item 数目比较少的情况下是没有任何问题的,如果 item 数目比较多的情况下,就会产生性能问题,而且会增加首屏的渲染时间。幸好,ListView 还提供了一个ListView.builder的方法,适用于 item 比较多的场景,该构造函数将创建 item 交给了一个抽象的方法,交给 ListView 进行管理,ListView 会在真正需要的时候去创建子 Widget,而不是一开始就全部初始化好。。

首先,我们构建一个 items list,并将其传入 MyApp 的 StatelessWidget 中:

MyApp(
	items: List<String>.generate(10000, (i) => '列表 $i'),
)

然后就可以在 MyApp 的 body 中使用ListView.builder来构建 item 了:

body: ListView.builder(
          itemCount: items.length,
          prototypeItem: ListTile(
            title: Text(items.first),
          ),
          itemBuilder: (context, index) {
            return ListTile(
              title: Text(items[index]),
            );
          },
        )

ListView.builder是推荐用来创建 ListView 的方式,上面的完整代码如下:

import 'package:flutter/material.dart';

void main() {
  runApp(
    MyApp(
      items: List<String>.generate(10000, (i) => '列表 $i'),
    ),
  );
}

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

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

  @override
  Widget build(BuildContext context) {
    const title = 'ListView的使用';

    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: title,
      home: Scaffold(
        appBar: AppBar(
          title: const Text(title),
        ),
        body: ListView.builder(
          itemCount: items.length,
          prototypeItem: ListTile(
            title: Text(items.first),
          ),
          itemBuilder: (context, index) {
            return ListTile(
              title: Text(items[index]),
            );
          },
        ),
      ),
    );
  }
}

效果图如下所示:

Flutter_view_P.png


1.3 ListView.separated方法

ListView.separatedListView.builder的一个变种,它在每个项之间插入了一个分隔符。你可以使用separatorBuilder属性来定义分隔符的样式和构建方法。下面是一个例子:

import 'package:flutter/material.dart';

void main() {
  runApp(
    MyApp(
      items: List<String>.generate(10000, (i) => '列表 $i'),
    ),
  );
}

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

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

  @override
  Widget build(BuildContext context) {
    const title = 'ListView的使用';

    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: title,
      home: Scaffold(
        appBar: AppBar(
          title: const Text(title),
        ),
        body: ListView.separated(
          itemCount: items.length,
          separatorBuilder: (context, index) => const Divider(),
          itemBuilder: (context, index) {
            return ListTile(
              title: Text(items[index]),
            );
          },
        ),
      ),
    );
  }
}

效果图如下所示:

Flutter_view_V.png


二、自定义ListView的样式和布局

你可以使用ListView.builderitemBuilder属性来自定义列表项的样式和布局。下面是一个例子,展示如何自定义列表项:

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

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

  @override
  Widget build(BuildContext context) {
    const title = 'ListView的使用';

    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: title,
      home: Scaffold(
        appBar: AppBar(
          title: const Text(title),
        ),
        body: ListView.builder(
          itemCount: items.length,
          itemBuilder: (context, index) {
            return Container(
              color: index % 2 == 0 ? Colors.grey[200] : Colors.white,
              padding: const EdgeInsets.all(16),
              child: Text(items[index]),
            );
          },
        ),
      ),
    );
  }
}

效果图如下所示:

Flutter_view_N.png


三、ListView的性能优化

当使用ListView显示大量数据时,为了提高性能,你可以考虑以下几种优化方式:

  • 使用ListView.builderListView.separated来按需构建列表项,避免一次性构建所有的项。
  • 使用ListView.separated并提供合适的分隔符构建方法,避免不必要的分隔符构建。
  • 如果列表项有固定高度,请使用itemExtent属性来指定项的高度,以避免动态计算高度带来的性能开销。
  • 如果列表项是不可滚动的,可以将physics属性设置为NeverScrollableScrollPhysics来禁用滚动。
  • 使用ScrollController来控制滚动,并使用addPostFrameCallback在构建完成后延迟加载数据。

四、ListView的常见问题和解决方法

在使用ListView时,你可能会遇到一些常见的问题。以下是其中一些问题和相应的解决方法:

  • ListView无法滚动:确保包含ListView的容器(如Scaffold)具有足够的空间来进行滚动,或者尝试将ListView的父级包装在一个可滚动的容器(如SingleChildScrollView)中。
  • ListView不显示或空白:检查是否正确设置了itemCount属性,并且itemBuilder方法返回了有效的项。
  • ListView滚动不流畅:可能是由于列表项过多或构建过程中的性能问题导致的。尝试使用按需构建的ListView.builder或ListView.separated,并使用性能优化技术来提高滚动性能。

五、示例

9.1 垂直列表

import 'package:flutter/material.dart';

main(List<String> args) {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: MyHomeBody(),
      ),
    );
  }
}

class MyHomeBody extends StatelessWidget {
  const MyHomeBody({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: const [
        ListTile(
          leading: Icon(
            Icons.assignment,
            color: Colors.red,
            // size: 40,
          ),
          title: Text("全部订单"),
        ),
        Divider(),
        ListTile(
          leading: Icon(
            Icons.payment,
            color: Colors.green,
            // size: 40,
          ),
          title: Text("待付款"),
        ),
        Divider(),
        ListTile(
          leading: Icon(
            Icons.local_car_wash,
            color: Colors.orange,
            // size: 40,
          ),
          title: Text("待收货"),
        ),
        Divider(),
        ListTile(
          leading: Icon(
            Icons.favorite,
            color: Colors.lightGreen,
            // size: 40,
          ),
          title: Text("我的收藏"),
        ),
        Divider(),
        ListTile(
          leading: Icon(
            Icons.people,
            color: Colors.black54,
            // size: 40,
          ),
          title: Text("在线客服"),
        ),
        Divider(),
      ],
    );
  }
}

效果图如下所示:

Flutter_view_Z.png


9.2 垂直图文列表

class MyHomeBody extends StatelessWidget {
  const MyHomeBody({super.key});
  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        ListTile(
          leading: Image.network("https://www.itying.com/images/flutter/1.png"),
          title: const Text("华北黄淮高温雨今起强势登场"),
          subtitle: const Text("中国天气网讯 21日开始,华北黄淮高温雨今起强势登场"),
        ),
        const Divider(),
        ListTile(
          trailing: Image.network("https://www.itying.com/images/flutter/1.png"),
          title: const Text("华北黄淮高温雨今起强势登场"),
          subtitle: const Text("中国天气网讯 21日开始,华北黄淮高温雨今起强势登场"),
        ),
        const Divider(),
        ListTile(
          leading: Image.network("https://www.itying.com/images/flutter/1.png"),
          title: const Text("华北黄淮高温雨今起强势登场"),
          // subtitle: const Text("中国天气网讯 21日开始,华北黄淮高温雨今起强势登场"),
        ),
        const Divider(),
      ],
    );
  }
}

效果图如下所示:

Flutter_view_X.png


9.3 水平列表

class MyHomeBody extends StatelessWidget {
  const MyHomeBody({super.key});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 180,
      child: ListView(
        scrollDirection: Axis.horizontal,
        children: [
          ClipOval(
            //圆角
            child: Container(
              width: 180,
              color: Colors.red,
            ),
          ),
          Container(
              width: 180,
              color: Colors.green,
              child: ClipRRect(
                borderRadius: BorderRadius.circular(50),
                child: Column(
                  children: [
                    Image.network(
                        "https://www.itying.com/images/flutter/1.png"),
                    const Text("第一个文本"),
                  ],
                ),
              )),
          Container(
            color: Colors.orange,
            width: 180,
          ),
        ],
      ),
    );
  }
}

效果图如下所示:

Flutter_listView_A.png


9.4 动态列表

class MyHomeBody extends StatelessWidget {
  const MyHomeBody({super.key});

  List<Widget> _getData() {
    List<Widget> list = [];
    for (var i = 0; i < 10; i++) {
      list.add(const ListTile(
        title: Text("列表"),
      ));
    }
    return list;
  }

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: _getData(),
    );
  }
}

效果图如下所示:

Flutter_listView_B.png


9.5 builder 实现动态列表

// ignore: must_be_immutable
class MyHomeBody extends StatelessWidget {
  List<String> list = [];
  MyHomeBody({super.key}) {
    for (var i = 0; i < 10; i++) {
      list.add("列表 -- $i");
    }
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: list.length,//数量
      itemBuilder: (BuildContext context, int index) {
        return ListTile(
          title: Text(list[index]),
        );
      },
    );
  }
}

效果图如下所示:

Flutter_listView_C.png


9.6 builder 实现动态列表 -- 获取 json 数据

// listData 中测试数据,使用时需要导入头文件
List listData=[
      {
          "title": 'Candy Shop',
          "author": 'Mohamed Chahin',
          "imageUrl": 'https://www.itying.com/images/flutter/1.png',
      },
       {
          "title": 'Childhood in a picture',
          "author": 'Google',
          "imageUrl": 'https://www.itying.com/images/flutter/2.png',
      },
      {
          "title": 'Alibaba Shop',
          "author": 'Alibaba',
          "imageUrl": 'https://www.itying.com/images/flutter/3.png',
      },
      {
          "title": 'Candy Shop',
          "author": 'Mohamed Chahin',
          "imageUrl": 'https://www.itying.com/images/flutter/4.png',
      },
       {
          "title": 'Tornado',
          "author": 'Mohamed Chahin',
          "imageUrl": 'https://www.itying.com/images/flutter/5.png',
      },
      {
          "title": 'Undo',
          "author": 'Mohamed Chahin',
          "imageUrl": 'https://www.itying.com/images/flutter/6.png',
      },
      {
          "title": 'white-dragon',
          "author": 'Mohamed Chahin',
          "imageUrl": 'https://www.itying.com/images/flutter/7.png',
      }      

];

class MyHomeBody extends StatelessWidget {
  const MyHomeBody({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: listData.length,
      itemBuilder: (BuildContext context, int index) {
        return ListTile(
          leading: Image.network(listData[index]["imageUrl"]),
          title: Text(listData[index]["title"]),
          subtitle: Text(listData[index]["author"]),
        );
      },
    );
  }
}

效果图如下所示:

Flutter_listView_D.png


参考:

flutter系列之:flutter中listview的高级用法-腾讯云开发者社区-腾讯云

Flutter速来系列23-1、ListView和GridView,躲不掉避不开的列表一、引言 在Flutter中,Li - 掘金

Flutter 组件之 ListView、GridView - 简书


posted @   fengMisaka  阅读(437)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 【.NET】调用本地 Deepseek 模型
点击右上角即可分享
微信分享提示