我们经常需要创建展示不同类型内容的列表。比方说,我们可能在开发一个列表,它显示一个标题,后跟一些与标题相关的项目,然后是另一个标题,依此类推。

我们该如何用 Flutter 创建这样的结构呢?

路线

  • 创建一个拥有不同类型项目的数据源

  • 把数据源转换成一个包含 Widget 的 List

1. 创建一个具有不同类型项目的数据源

项目的类型

为了表示 List 中不同类型的项,我们需要为每一个类型的项目定义一个类。

在这个例子中,我们将制作一个展示了标题,后面有五条消息的应用。因此,我们将创建三个类:ListItemHeadingItemMessageItem

  1. // 一个 List 可以包含的不同类型项的基类(The base class for the different types of items the List can contain)
  2. abstract class ListItem {}
  3. // 包含标题数据的一种 ListItem(A ListItem that contains data to display a heading)
  4. class HeadingItem implements ListItem {
  5. final String heading;
  6. HeadingItem(this.heading);
  7. }
  8. // 包含消息数据的一种 ListItem(A ListItem that contains data to display a message)
  9. class MessageItem implements ListItem {
  10. final String sender;
  11. final String body;
  12. MessageItem(this.sender, this.body);
  13. }

创建项目的 List

大部分时候,我们从网络或本地数据库获取数据,并将数据转换成一个项目列表。

对于这个例子来说,我们将生成一个要使用的项目列表。这个列表将包含一个标题,后跟五条消息。如此往复。

  1. final items = List<ListItem>.generate(
  2. 1200,
  3. (i) => i % 6 == 0
  4. ? HeadingItem("Heading $i")
  5. : MessageItem("Sender $i", "Message body $i"),
  6. );

2. 把数据源转换成 Widget 的 List

为了把每一个项目转换成 Widget,我们将采用 ListView.builder 构造方法。

通常,我们需要提供一个 builder 函数来确定我们正在处理的项目类型,并返回该类型项目的相应 Widget。

在这个例子中,我们使用 is 关键字来检查我们正在处理的项目类型。这样做速度很快,并会自动将每个项目转换为适当的类型。不过,如果你更喜欢其他模式,也能通过其他方式解决此问题!

  1. ListView.builder(
  2. // 让 ListView 知道有多少项目需要被构建(Let the ListView know how many items it needs to build)
  3. itemCount: items.length,
  4. // 提供一个 builder 方法。这正是魔力所在!(Provide a builder function. This is where the magic happens! We'll)
  5. // 我们将基于各个项目本身的类型,转化成 Widget。(convert each item into a Widget based on the type of item it is.)
  6. itemBuilder: (context, index) {
  7. final item = items[index];
  8. if (item is HeadingItem) {
  9. return ListTile(
  10. title: Text(
  11. item.heading,
  12. style: Theme.of(context).textTheme.headline,
  13. ),
  14. );
  15. } else if (item is MessageItem) {
  16. return ListTile(
  17. title: Text(item.sender),
  18. subtitle: Text(item.body),
  19. );
  20. }
  21. },
  22. );

完整示例

  1. import 'package:flutter/foundation.dart';
  2. import 'package:flutter/material.dart';
  3. void main() {
  4. runApp(MyApp(
  5. items: List<ListItem>.generate(
  6. 1000,
  7. (i) => i % 6 == 0
  8. ? HeadingItem("Heading $i")
  9. : MessageItem("Sender $i", "Message body $i"),
  10. ),
  11. ));
  12. }
  13. class MyApp extends StatelessWidget {
  14. final List<ListItem> items;
  15. MyApp({Key key, @required this.items}) : super(key: key);
  16. @override
  17. Widget build(BuildContext context) {
  18. final title = 'Mixed List';
  19. return MaterialApp(
  20. title: title,
  21. home: Scaffold(
  22. appBar: AppBar(
  23. title: Text(title),
  24. ),
  25. body: ListView.builder(
  26. // 让 ListView 知道有多少项目需要被构建(Let the ListView know how many items it needs to build)
  27. itemCount: items.length,
  28. // 提供一个 builder 方法。这正是魔力所在!(Provide a builder function. This is where the magic happens! We'll)
  29. // 我们将基于各个项目本身的类型,转化成 Widget。(convert each item into a Widget based on the type of item it is.)
  30. itemBuilder: (context, index) {
  31. final item = items[index];
  32. if (item is HeadingItem) {
  33. return ListTile(
  34. title: Text(
  35. item.heading,
  36. style: Theme.of(context).textTheme.headline,
  37. ),
  38. );
  39. } else if (item is MessageItem) {
  40. return ListTile(
  41. title: Text(item.sender),
  42. subtitle: Text(item.body),
  43. );
  44. }
  45. },
  46. ),
  47. ),
  48. );
  49. }
  50. }
  51. // List 能容纳的不同类型项目的基类(The base class for the different types of items the List can contain)
  52. abstract class ListItem {}
  53. // 一种包含展示标题数据的 ListItem(A ListItem that contains data to display a heading)
  54. class HeadingItem implements ListItem {
  55. final String heading;
  56. HeadingItem(this.heading);
  57. }
  58. // 一种包含展示消息数据的 ListItem(A ListItem that contains data to display a message)
  59. class MessageItem implements ListItem {
  60. final String sender;
  61. final String body;
  62. MessageItem(this.sender, this.body);
  63. }

Mixed List Demo