Flutter动态单元格扩展插件live_cell_extension的使用
Flutter 动态单元格扩展插件 live_cell_extension 的使用
本插件提供了一个代码生成器,该生成器从 live_cells
包中扩展了 ValueCell
和 MutableCell
,为标记了 CellExtension
的类提供属性访问器。
特性
- 扩展
ValueCell
和MutableCell
,以便通过注解类访问属性。 - 可选择是否生成
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
属性并进行观察,但不能像firstName
、lastName
和age
那样直接设置其值。 - 类的属性不能与
ValueCell
或MutableCell
提供的现有属性同名。对于这样的属性不会生成访问器。
选项
CellExtension
注解还接受以下选项:
name
: 生成的ValueCell
扩展的名称。如果为null
,则生成的扩展名称为注解类的名称加上CellExtension
。mutableName
: 如果mutable
为true
,则生成的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
更多关于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行,但我们提供了一个按钮来动态地添加更多行。
解释
- LiveCellController:我们创建了一个
LiveCellController
实例,用于控制单元格的扩展。 - LiveCellExtension:我们使用
LiveCellExtension
小部件来创建表格布局,并指定列数和初始行数。 - cellBuilder:这是一个回调函数,用于构建每个单元格。在这个例子中,每个单元格显示其列和行的索引。
- 动态添加行:通过调用
_cellController.addRows(1)
,我们可以动态地添加一行。注意,我们需要在调用此方法后调用setState
来刷新UI。
请确保你已经安装了live_cell_extension
插件,并根据需要调整代码中的列数和初始行数。这个示例应该为你提供一个良好的起点,以了解如何在Flutter中使用这个插件。