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),

以下是展现效果。

相关推荐
熊的猫13 分钟前
webpack 核心模块 — loader & plugins
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
我不是李.杨34 分钟前
Windows10/11开启卓越性能模式 windows开启卓越性能电源模式 工作电脑开启卓越性能模式 电脑开启性能模式
windows·电脑
四喜花露水1 小时前
Vue 自定义icon组件封装SVG图标
前端·javascript·vue.js
前端Hardy1 小时前
HTML&CSS: 实现可爱的冰墩墩
前端·javascript·css·html·css3
Zender Han1 小时前
Flutter自定义矩形进度条实现详解
android·flutter·ios
hello world smile2 小时前
关于Flutter空安全升级方案整理
flutter·移动端
web Rookie2 小时前
JS类型检测大全:从零基础到高级应用
开发语言·前端·javascript
工业甲酰苯胺2 小时前
C# 单例模式的多种实现
javascript·单例模式·c#
Cachel wood2 小时前
Github配置ssh key原理及操作步骤
运维·开发语言·数据库·windows·postgresql·ssh·github
汪小敏同学2 小时前
【Django进阶】django-rest-framework中文文档——快速入门
网络·windows·oracle