Flutter第八弹 构建拥有不同项的列表

目标:1)项目中,数据源可能涉及不同的模版,显示不同类型的子项,类似RecycleView的itemType, 有多种类型,列表怎么显示?

2)不同的数据源构建列表

一、创建不同的数据源

采用类似RecyclerView的思想,不同的数据源,对应不同的子项Widget进行展现;

因为需要列表项统一展现,因此最好抽取一个子项的公共基类,进行统一处理。

1.1 创建数据源基类

抽取统一基类,列表项显示主标题和副标题。

  • 一种子项只显示主标题;
  • 另一类子项显示主标题和副标题
Dart 复制代码
/// The base class for the different types of items the list can contain.
abstract class ListItem {
  /// The title line to show in a list item.
  Widget buildTitle(BuildContext context);

  /// The subtitle line, if any, to show in a list item.
  Widget buildSubtitle(BuildContext context);
}

1.2 创建不同数据源

只有主标题的数据源

Dart 复制代码
/**
 * 为什么使用implements,而不是extends?
 * 因为是ListItem的实现类,已经实现ListItem的抽象方法了。
 * 如果是extends, 意味着HeadingItem不需要实现抽象方法,自身仍然作为抽象类使用
 */
class HeadingItem implements ListItem {
  final String title;

  /**
   * 为什么需要const修饰符?
   */
  const HeadingItem(this.title);

  @override
  Widget buildSubtitle(BuildContext context) {
    // 创建主标题
    return Text(
      title,
      style: Theme.of(context).textTheme.headlineSmall,
    );
  }

  @override
  Widget buildTitle(BuildContext context) {
    return const SizedBox.shrink();
  }
}

包含主标题和副标题的数据源

Dart 复制代码
/**
 * 含有主标题和副标题的列表项
 */
class MessageItem implements ListItem {
  final String title;
  final String message;

  const MessageItem(this.title, this.message);

  @override
  Widget buildSubtitle(BuildContext context) {
    // 创建主标题
    return Text(
      title,
      style: Theme.of(context).textTheme.headlineSmall,
    );
  }

  @override
  Widget buildTitle(BuildContext context) {
    // 创建副标题
    return Text(
      message,
      style: Theme.of(context).textTheme.headlineSmall,
    );
  }
}

二、创建不同数据源的列表

我们知道ListView的子项是ListTile,因此我们需要将数据源填充到ListTile中。

Dart 复制代码
void main() {
  runApp(
    MyApp(),
  );
}

class MyApp extends StatelessWidget {

  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // TRY THIS: Try running your application with "flutter run". You'll see
        // the application has a purple toolbar. Then, without quitting the app,
        // try changing the seedColor in the colorScheme below to Colors.green
        // and then invoke "hot reload" (save your changes or press the "hot
        // reload" button in a Flutter-supported IDE, or press "r" if you used
        // the command line to start the app).
        //
        // Notice that the counter didn't reset back to zero; the application
        // state is not lost during the reload. To reset the state, use hot
        // restart instead.
        //
        // This works for code too, not just values: Most code changes can be
        // tested with just a hot reload.
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

@immutable
class MyHomePage extends StatefulWidget {
  List<ListItem> items = List<ListItem>.generate(
    1000,
    (i) => i % 6 == 0
        ? HeadingItem('Heading $i')
        : MessageItem('Sender $i', 'Message body $i'),
  );

  MyHomePage({super.key, required this.title});

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // TRY THIS: Try changing the color here to a specific color (to
        // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
        // change color while the other colors stay the same.
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      // 根据数据源创建不同项的列表
      body: ListView.builder(
        // 列表项个数
        itemCount: widget.items.length,
        // 列表项构建器
        itemBuilder: (context, index) {
          // 返回列表项的ListTile
          return ListTile(
            // 主标题(通过ListItem创建主标题)
            title: widget.items[index].buildTitle(context),
            subtitle: widget.items[index].buildSubtitle(context),
          );
        },
      ).build(context),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );

创建List Tile的时候,采用数据源ListItem进行填充,因为数据源有共同的基类,因此构建ListTile的时候就很方便。

Dart 复制代码
      // 根据数据源创建不同项的列表
      body: ListView.builder(
        // 列表项个数
        itemCount: widget.items.length,
        // 列表项构建器
        itemBuilder: (context, index) {
          // 返回列表项的ListTile
          return ListTile(
            // 主标题(通过ListItem创建主标题)
            title: widget.items[index].buildTitle(context),
            subtitle: widget.items[index].buildSubtitle(context),
          );
        },
      ).build(context),

以下是展现效果。

相关推荐
谎言西西里3 小时前
JS 高手必会:手写 new 与 instanceof
javascript
爱吃大芒果4 小时前
GitCode口袋工具的部署运行教程
flutter·华为·harmonyos·gitcode
爱吃大芒果4 小时前
Flutter基础入门与核心能力构建——Widget、State与BuildContext核心解析
flutter·华为·harmonyos
天问一5 小时前
使用 Vue Router 进行路由定制和调用的示例
前端·javascript·vue.js
飞Link6 小时前
Windows 环境下 LaTeX 的安装配置教程
windows·vscode
R.lin6 小时前
windows MySQL解压版安装教程
windows·mysql·adb
韩立学长7 小时前
【开题答辩实录分享】以《基于Vue的非遗文化知识分享平台的设计与实现》为例进行选题答辩实录分享
前端·javascript·vue.js
常家壮7 小时前
Windows隐藏账号创建完全指南:技术原理与安全实践
windows·安全·渗透测试·后门·windows隐藏账号
灵感菇_7 小时前
Flutter Riverpod 完整教程:从入门到实战
前端·flutter·ui·状态管理
前端 贾公子7 小时前
Vue响应式原理学习:基本原理
javascript·vue.js·学习