加速开发,在 Flutter 中使用函数组件 FC
目录
Flutter 已经更新到第3个大版本,并带来更丰富的特性更接近原生的性能。然而不管是新手入门还是轻车熟路的应用搭建,我们的第一个组件都从继承 StatefulWidget 或 StatelessWidget 开始。
React 风格的开发替代方案
若经历过 Flutter 项目开发的过程,会明显感觉到,相比 React 组件开发生态,Flutter 的组件开发过程相当繁琐。
目前社区已经有部分方案提供类似 React 的函数式组件开发风格。比较出名的有 functional_widget、flutter_hooks。
functional_widget
functional_widget 能让我们直接写函数组件,并作为 Widget 构造出对象。然而这个过程需要借助 build_runner 进行代码生成,一番折腾后,和手写 Widget 并没有节省多少时间。
@widget
Widget myText(BuildContext context) {
return const Text("FC");
}
flutter_hooks
flutter_hooks 则引用 React 的钩子设计,通过单线程的特性,封装出门面基类 HookWidget 和附属钩子。继承自 HookWidget ,便可在 build 阶段使用钩子。这个库由社区活跃开发者所作,即使从未发布主版本但仍然收获 2k+ star,是较推荐的 React 风格工具库。
class MyText extends HookWidget {
Widget build(BuildContext context) {
final counter = useState(0);
// counter.value++;
return Text("counter: ${counter.value}");
}
}
可以看到 HookWidget 省去了代码生成环节也减少了相比原始开发的工作量,并提供了接近 React FC 的开发过程。
Dart 3 Records 解构语法
通过速览上方 2 个 Flutter 库,它们提供的接口仍然没有最大程度地呈现出 React 的开发风格,其中 Dart 语言的语法支持也是很重要的原因,毕竟 Dart 与 JavaScript 是不同的语言。
Dart 3 Records 语法的出现给 Flutter 函数式组件开发提供了一个机会。在之前,为了减少 DTO class 泛滥,我们一般用 Tuple 包来组建临时 DTO 实例。
// 第 1 页,取 10 条
final tuple = Tuple2<int, int>(1, 10);
tuple.item1; // 1
tuple.item2; // 10
而在 Dart 3 中,引擎提供了直接语法支持,甚至能够使用解构语法。
final tuple = (1, 10);
tuple.$1; // 1
tuple.$2; // 10
// 解构
final (page, limit) = tuple;
解构语法在 React 钩子编程很重要,一般用解构实现一行使用钩子并得到需要的部分。Dart 3 以前为了解构需要分开出多行代码赋值,大幅增加代码冗余。而现在可以这样了:
// useState 钩子,但是 in Dart 3
final (state, setState) = useState(0);
这就是 flutter_fc
的灵感,借助解构,能提供更接近 React 的开发体验。上方的例子已经在 Dart 实现了。
使用 flutter_fc 加快开发速度
flutter_fc
是一个支持函数式组件开发的工具库,它提供非常接近 React 的钩子和接口,让我们像写 React FC 那样编写 Flutter 组件,减少代码量,加速项目开发。
接下来呈现的使用方法,相信对于 React 开发者能迅速理解。
Eg1: Counter FC
一个计数器组件。
final Counter = defineFC((props) {
final (counter, setCounter) = useState(0);
return ElevatedButton(
onPressed: () => setCounter(counter + 1),
child: Text("Counter: $counter"),
);
});
void main() {
runApp(MaterialApp(home: Counter()));
}
就这么简单,不需要代码生成,不需要继承别的。只要关心输入 props 和输出组件就行了。
Eg2: 使用 Side Effects 管理受控
调用 useEffect()
部署一个副作用,依赖变化时才会触发执行。
final Counter = defineFC((int? value) {
final (counter, setCounter) = useState(0);
useEffect(() {
if (value != null) setCounter(value);
}, [value]);
return ElevatedButton(
onPressed: () => setCounter(counter + 1),
child: Text("Counter: $counter"),
);
});
Eg3: 使用 useMemo 减少重绘
使用 useMemo
确保只有需要的数据变化时才执行重计算。
final Base64Image = defineFC((String? base64String) {
assert(base64String != null, "data can not be null");
final buffer = useMemo(
() => base64Decode(base64String),
[base64String],
);
return Image.memory(buffer);
});
组合钩子
flutter_fc
存储钩子的方法和 React Fiber 类似,和 flutter_hooks
也有相同之处,所以不管在 flutter_fc
或 flutter_hooks
,都应避免在控制流程中使用钩子,避免调用混乱。
编写钩子函数,构建自己的钩子。
// useUpdate 返回一个方法,调用时请求刷新重绘
Function() useUpdate() {
final (_, setState) = useState([]);
return () => setState([]);
}
// 从 Element 树取一个 Store 锚实例
Store useStore() {
final context = useBuildContext();
return Provider.of<Store>(context);
}
FC 和 Widget 共存
flutter_fc
旨在解决 Flutter 项目中的代码冗余和解耦问题,同时提供类似 React 的接口为函数提供副作用,开发者只需关注函数内输入输出。对于功能较复杂的组件、需要使用 mixin 实现的组件,应使用组件继承方式开发。
() => Text(“请支持 flutter_fc”)
flutter_fc
借助 Dart 3 的语法支持,给函数式组件开发开辟了一条 Flutter 端组件开发的思路。如果 flutter_fc
节省了你的开发时间,或正在使用它构建项目,请支持 flutter_fc
。
-
Github Repo https://github.com/luo3house/flutter_fc
参考
- React
- Dart 3