Flutter动态单元格扩展插件live_cell_extension的使用

发布于 1周前 作者 bupafengyu 来自 Flutter

Flutter 动态单元格扩展插件 live_cell_extension 的使用

本插件提供了一个代码生成器,该生成器从 live_cells 包中扩展了 ValueCellMutableCell,为标记了 CellExtension 的类提供属性访问器。

特性

  • 扩展 ValueCellMutableCell,以便通过注解类访问属性。
  • 可选择是否生成 MutableCell 访问器。
  • 允许你写以下代码:
final prop = cell.prop;

而不是:

final prop = ValueCell.computed(() => cell().prop);
  • 允许你写以下代码:
final prop = cell.prop;

而不是:

final prop = MutableCell.computed(() => cell().prop, (value) {
  cell.value = MyClass(prop: value);
});

开始使用

要使用此包,你需要在 pubspec.yaml 文件的 dependencies 中添加以下内容:

dependencies:
  live_cells_core: ^0.15.0

你也需要在 pubspec.yaml 文件的 dev_dependencies 中添加此包和 build_runner

dev_dependencies:
  build_runner:
  live_cell_extension: ^0.4.5

查看 example 目录中的示例,以了解如何设置你的项目。

使用方法

首先,使用 live_cells 包中的 CellExtension() 注解你的类:

import 'package:live_cells/live_cells.dart';

part 'person.g.dart';

[@CellExtension](/user/CellExtension)()
class Person {
  final String firstName;
  final String lastName;
  final int age;
  
  String get fullName => '$firstName $lastName';
  
  const Person({
    this.firstName,
    this.lastName,
    this.age
  });
}

然后运行以下命令:

flutter pub run build_runner build

这将为 ValueCell<Person> 生成一个名为 PersonCellExtension 的扩展,提供对 Person 类属性的访问器:

  • firstName
  • lastName
  • age
  • fullName

这样,你就可以直接访问存储在单元格中的 Person 对象的属性。例如,如果你有一个 ValueCell<Person>

final ValueCell<Person> person;

你可以通过以下方式访问 Person 对象的属性:

final firstName = person.firstName;
final lastName = person.lastName;
final fullName = person.fullName;
final age = person.age;

每个生成的属性返回一个 ValueCell,该 ValueCell 访问存储在单元格中的类实例的属性值。返回的 ValueCell 是计算单元格,当引用的属性值发生变化时,它们会更新自己的值。

person.firstName

大致等同于以下内容:

ValueCell.computed(() => person().firstName);

示例:

ValueCell.watch(() {
  print('Person ${person.fullName()} - ${person.age()}');
});

// 输出:Person John Smith - 25
person = Person(firstName: 'John', lastName: 'Smith', age: 25);

// 输出:Person Jane Doe - 30
person = Person(firstName: 'Jane', lastName: 'Doe', age: 30);

注意

  • 只有公共属性的访问器才会被生成。
  • 只有 final 属性或没有设置器的属性会被生成访问器。这是因为直接修改非 final 属性可能会导致观察者未被通知,从而造成混淆。
  • 类的属性不能与 ValueCell 提供的现有属性同名。对于这样的属性不会生成访问器。

可变属性

当一个类被注解为 CellExtension(mutable: true) 时,也会生成一个 MutableCell 的扩展。生成的扩展类似于为 ValueCell 生成的扩展,但生成的属性访问器返回的是 MutableCell 而不是 ValueCell。这允许通过 MutableCell 来单独修改属性。

使用前一个示例中的类:

import 'package:live_cells/live_cells.dart';

part 'person.g.dart';

[@CellExtension](/user/CellExtension)(mutable: true)
class Person {
  final String firstName;
  final String lastName;
  final int age;
  
  String get fullName => '$firstName $lastName';
  
  const Person({
    this.firstName,
    this.lastName,
    this.age
  });
}

生成的访问器将包括以下属性:

  • firstName
  • lastName
  • age

每个生成的访问器返回一个 MutableCell,当你设置该 MutableCell 时,它会设置属性的值。注意,这并不会直接修改单元格中存储的实例上的属性,而是创建一个新的实例,并将其赋值给单元格。

示例:

final person = MutableCell(Person(firstName: 'John', lastName: 'Smith', age: 25));

ValueCell.watch(() {
  print('Person ${person.fullName()} - ${person.age()}');
});

// 输出:Person John Smith 30
person.age.value = 30;

// 输出:Person Jane Doe 30
MutableCell.batch(() {
  person.firstName.value = 'Jane';
  person.lastName.value = 'Doe';
});

注意

  • 属性必须是 final 或没有设置器的这一限制仍然适用。
  • mutable: true 允许通过生成扩展的属性返回的 MutableCell 来设置属性值,但直接修改实例上的属性不会通知观察者,即使在注解中提供了 mutable: true
  • MutableCell 访问器仅针对具有类无命名构造函数形式参数的属性生成。这就是为什么没有为 fullName 生成访问器。
  • 即使提供了 mutable: true,仍然会生成 ValueCell 扩展。因此,你仍然可以直接访问单元格上的 fullName 属性并进行观察,但不能像 firstNamelastNameage 那样直接设置其值。
  • 类的属性不能与 ValueCellMutableCell 提供的现有属性同名。对于这样的属性不会生成访问器。

选项

CellExtension 注解还接受以下选项:

  • name: 生成的 ValueCell 扩展的名称。如果为 null,则生成的扩展名称为注解类的名称加上 CellExtension
  • mutableName: 如果 mutabletrue,则生成的 MutableCell 扩展的名称。如果为 null,则生成的扩展名称为注解类的名称加上 MutableCellExtension

额外信息

查看 example/ 目录中的示例,从设置项目的依赖项到演示生成的属性访问器的完整示例。

如果您发现任何问题或有任何功能请求,请在包的 GitHub 存储库中打开一个问题。


示例代码

import 'package:flutter/material.dart';
import 'package:live_cells/live_cell_widgets.dart';
import 'package:live_cells/live_cells.dart';

part 'main.g.dart';

/// 一个自定义类
///
/// `CellExtension` 注解告诉 live_cell_extension 生成一个 `ValueCell<Person>` 的扩展,提供类的属性访问器。
///
/// `mutable: true` 告诉 live_cell_extension 还生成一个 `MutableCell<Person>` 的扩展,提供类的属性访问器,这些访问器返回 `MutableCell`。
[@CellExtension](/user/CellExtension)(mutable: true)
class Person {
  final String firstName;
  final String lastName;
  final int age;

  const Person({
    required this.firstName,
    required this.lastName,
    required this.age
  });

  String get fullName => '$firstName $lastName';
}

void main() {
  runApp(const MyApp());
}

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

  // 此小部件是您的应用程序的根
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // 这是您的应用程序的主题。
        //
        // 尝试运行您的应用程序。你会看到应用有一个蓝色工具栏。然后,不退出应用,尝试更改以下主色调为绿色并重新加载。
        // 注意计数器并没有重置回零;应用程序没有重新启动。
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends CellWidget with CellInitializer {
  [@override](/user/override)
  Widget build(BuildContext context) {
    // 一个用于存储个人信息的单元格
    final person = cell(() => MutableCell(const Person(
        firstName: '',
        lastName: '',
        age: 25
    )));

    watch(() {
      // 这是为了证明当属性改变时,所有观察者都会被通知
      print('Person changed: ${person().fullName}');
    });

    return Scaffold(
      appBar: AppBar(
        title: const Text('CellExtension Demo'),
      ),
      body: Center(
        child: Column(
          children: [
            Flexible(
              child: SingleChildScrollView(
                child: Padding(
                  padding: const EdgeInsets.all(10.0),
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      const Text(
                        'Person Details:',
                      ),
                      const SizedBox(height: 10),

                      // 注意,没有必要用 cell(...) 包装属性单元格或将其存储在局部变量中。

                      CellTextField(
                        content: person.firstName,
                        decoration: const InputDecoration(
                          label: Text('First Name')
                        ).cell,
                      ),
                      CellTextField(
                        content: person.lastName,
                        decoration: const InputDecoration(
                            label: Text('Last Name')
                        ).cell,
                      ),
                      numField(person.age, 'Age'),
                      const SizedBox(height: 20),
                      const Text(
                        'The following uses the cell holding the entire Person, person():',
                        style: TextStyle(
                          fontWeight: FontWeight.bold
                        ),
                      ),
                      const SizedBox(height: 10),
                      CellWidget.builder((context) => Text('${person().fullName} is ${person().age} years old.')),
                      const SizedBox(height: 20),
                      ElevatedButton(
                        child: const Text('Reset'),
                        onPressed: () {
                          person.value = const Person(
                              firstName: '',
                              lastName: '',
                              age: 25
                          );
                        },
                      ),
                      const SizedBox(height: 20),
                      const Text(
                        'The following uses the generated accessors:',
                        style: TextStyle(
                            fontWeight: FontWeight.bold
                        ),
                      ),
                      const SizedBox(height: 10),
                      Row(
                        mainAxisSize: MainAxisSize.max,
                        children: [
                          const Text('person.firstName():'),
                          const Spacer(),
                          Text(person.firstName()),
                        ],
                      ),
                      const SizedBox(height: 5),
                      Row(
                        mainAxisSize: MainAxisSize.max,
                        children: [
                          const Text('person.lastName():'),
                          const Spacer(),
                          Text(person.lastName()),
                        ],
                      ),
                      const SizedBox(height: 5),
                      Row(
                        mainAxisSize: MainAxisSize.max,
                        children: [
                          const Text('person.age():'),
                          const Spacer(),
                          Text('${person.age()}'),
                        ],
                      ),
                      const SizedBox(height: 10),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  static Widget numField(MutableCell<int> cell, String label) =>
      StaticWidget.builder((context) {
        final maybe = cell.maybe();
        final content = maybe.mutableString();
        final error = maybe.error;

        return CellTextField(
          content: content,
          keyboardType: TextInputType.number.cell,
          decoration: ValueCell.computed(() => InputDecoration(
              errorText: error() != null
                  ? 'Not a valid integer'
                  : null
          )),
        );
      });
}

更多关于Flutter动态单元格扩展插件live_cell_extension的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter动态单元格扩展插件live_cell_extension的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用live_cell_extension插件的一个简单示例。这个插件允许你动态地扩展单元格,这在构建类似表格布局的应用时非常有用。

首先,你需要在你的pubspec.yaml文件中添加依赖项:

dependencies:
  flutter:
    sdk: flutter
  live_cell_extension: ^最新版本号  # 请替换为实际的最新版本号

然后运行flutter pub get来安装依赖。

下面是一个完整的示例代码,展示了如何使用live_cell_extension来创建一个可以动态扩展的单元格:

import 'package:flutter/material.dart';
import 'package:live_cell_extension/live_cell_extension.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Live Cell Extension Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final LiveCellController _cellController = LiveCellController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Live Cell Extension Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('Dynamic Cell Expansion Demo', style: TextStyle(fontSize: 24)),
            SizedBox(height: 16),
            Expanded(
              child: LiveCellExtension(
                controller: _cellController,
                columns: 3,  // 假设我们有3列
                rows: 5,     // 假设我们有5行,但我们会动态添加更多
                cellBuilder: (context, index) {
                  final column = index % 3;
                  final row = index ~/ 3;
                  return GestureDetector(
                    onTap: () {
                      // 你可以在这里处理单元格点击事件
                      print("Cell tapped at (${column}, ${row})");
                    },
                    child: Container(
                      color: Colors.grey[300],
                      alignment: Alignment.center,
                      child: Text("${column}, ${row}"),
                    ),
                  );
                },
              ),
            ),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                // 动态添加一行
                setState(() {
                  _cellController.addRows(1);
                });
              },
              child: Text('Add Row'),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个使用LiveCellExtension的表格布局。表格初始时有3列和5行,但我们提供了一个按钮来动态地添加更多行。

解释

  1. LiveCellController:我们创建了一个LiveCellController实例,用于控制单元格的扩展。
  2. LiveCellExtension:我们使用LiveCellExtension小部件来创建表格布局,并指定列数和初始行数。
  3. cellBuilder:这是一个回调函数,用于构建每个单元格。在这个例子中,每个单元格显示其列和行的索引。
  4. 动态添加行:通过调用_cellController.addRows(1),我们可以动态地添加一行。注意,我们需要在调用此方法后调用setState来刷新UI。

请确保你已经安装了live_cell_extension插件,并根据需要调整代码中的列数和初始行数。这个示例应该为你提供一个良好的起点,以了解如何在Flutter中使用这个插件。

回到顶部