我们经常需要创建显示不同类型内容的列表。例如,我们可能正在制作一个列表,其中显示一个标题,后面跟着与该标题相关的几个子项,再后面是另一个标题,等等。

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

步骤

  1. 使用不同类型的数据创建数据源
  2. 将数据源转换为Widgets列表

1. 使用不同类型的数据创建数据源

条目(子项)类型

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

在这个例子中,我们将在一个应用程序上显示一个标题,后面跟着五条消息。因此,我们将创建三个类:ListItemHeadingItem、和MessageItem

  1. // The base class for the different types of items the List can contain
  2. abstract class ListItem {}
  3. // A ListItem that contains data to display a heading
  4. class HeadingItem implements ListItem {
  5. final String heading;
  6. HeadingItem(this.heading);
  7. }
  8. // 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. }

创建Item列表

大多数时候,我们会从互联网或本地数据库中读取数据,并将该数据转换成item的列表。

对于这个例子,我们将生成一个Item列表来处理。该列表将包含一个标题、后跟五条消息,然后重复。

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

2. 将数据源转换为Widgets列表

为了将每个item转换为Widget,我们将使用ListView.builder构造函数。

通常,我们需要提供一个builder函数来检查我们正在处理的item类型,并返回该item类型对应的Widget。

在这个例子中,使用is关键字来检查我们正在处理的item的类型,这个速度很快,并会自动将每个item转换为适当的类型。 但是,如果您更喜欢另一种模式,也有不同的方法可以解决这个问题!

  1. new ListView.builder(
  2. // Let the ListView know how many items it needs to build
  3. itemCount: items.length,
  4. // Provide a builder function. This is where the magic happens! We'll
  5. // 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 new ListTile(
  10. title: new Text(
  11. item.heading,
  12. style: Theme.of(context).textTheme.headline,
  13. ),
  14. );
  15. } else if (item is MessageItem) {
  16. return new ListTile(
  17. title: new Text(item.sender),
  18. subtitle: new Text(item.body),
  19. );
  20. }
  21. },
  22. );

完整的例子

  1. import 'package:flutter/foundation.dart';
  2. import 'package:flutter/material.dart';
  3. void main() {
  4. runApp(new MyApp(
  5. items: new List<ListItem>.generate(
  6. 1000,
  7. (i) => i % 6 == 0
  8. ? new HeadingItem("Heading $i")
  9. : new 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 new MaterialApp(
  20. title: title,
  21. home: new Scaffold(
  22. appBar: new AppBar(
  23. title: new Text(title),
  24. ),
  25. body: new ListView.builder(
  26. // Let the ListView know how many items it needs to build
  27. itemCount: items.length,
  28. // Provide a builder function. This is where the magic happens! We'll
  29. // 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 new ListTile(
  34. title: new Text(
  35. item.heading,
  36. style: Theme.of(context).textTheme.headline,
  37. ),
  38. );
  39. } else if (item is MessageItem) {
  40. return new ListTile(
  41. title: new Text(item.sender),
  42. subtitle: new Text(item.body),
  43. );
  44. }
  45. },
  46. ),
  47. ),
  48. );
  49. }
  50. }
  51. // The base class for the different types of items the List can contain
  52. abstract class ListItem {}
  53. // A ListItem that contains data to display a heading
  54. class HeadingItem implements ListItem {
  55. final String heading;
  56. HeadingItem(this.heading);
  57. }
  58. // 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. }