signals有多简单
signals到底有多简单。
- 可以通过signal发送一个信号。
- 可以通过computed组合计算多个signal。
- 可以通过effect监听数据变化。 示例代码如下:
dart
import 'package:signals/signals.dart';
void main() {
final name = signal("Jane");
final surname = signal("Doe");
final fullName = computed(() => name.value + " " + surname.value);
// Logs: "Jane Doe"
effect(() {
print("name is ${name.value}");
print("fullName is ${fullName.value}");
});
// Updating one of its dependencies will automatically trigger
// the effect above, and will print "John Doe" to the console.
name.value = "John";
}
运行结果如下图所示:

分配两个Signal:name和surname,一个Computed(fullName)是name和surname计算结果,effect内部监听数据变化打印name和fullName的值。当name的值被重新设置的时候,fullName的值是name和surname的组合计算,也会跟随重新计算。
Computed图解
Computed会注册监听多个Signal,当其中的任何一个Signal值变化的时候,都会把值通知Computed从而引起Computed值的变换。注意上面图的箭头方向代表数据流向。
Effect
你也可以从effect中返回一个清理函数。当effect被销毁时,该函数会被调用。清理函数以阻止订阅更新回调。
scss
final s = signal(0);
final dispose1 = effect(() {
print(s.value);
return () => print('Effect destroyed');
});
// Destroy effect and subscriptions
dispose1();
s.value = 2;
运行结果如下图所示:只打印了一次s的值,dispose1函数执行以后,就不再监听s的数据变化。

需要注意effect使用过程中防止发生无限循环问题:不能在effect函数体中改变signal的值,否则会无限循环导致不可处理异常。
Batch
批处理功能允许您将多个信号写入组合成一个更新,该更新在回调完成时触发。
scss
final counter = signal(0);
final _double = computed(() => counter.value * 2);
final _triple = computed(() => counter.value * 3);
effect(() {
print(_double.value);
print(_triple.value);
});
batch(() {
counter.value = 1;
// Logs: 2, despite being inside batch, but `triple`
// will only update once the callback is complete
print(_double.value);
});
// Now we reached the end of the batch and call the effect
batch执行完成后才会执行effect。batch可以嵌套,并且当最外层批次调用完成时,更新将被刷新。运行结果如下图所示:
Flutter中的应用
在Flutter中,如果你想创建一个signal,当Widget从Widget树中移除时,该signal会自动释放,并在signal发生变化时重建Widget,你可以在有状态Widget中使用createComputed。示例代码如下:
scala
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
void main() async {
runApp(CounterWidget());
}
class CounterWidget extends StatefulWidget {
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> with SignalsMixin {
late final counter = createSignal(0);
late final isEven = createComputed(() => counter.value.isEven);
late final isOdd = createComputed(() => counter.value.isOdd);
@override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Counter: even=$isEven, odd=$isOdd'),
ElevatedButton(
onPressed: () => counter.value++,
child: Text('Increment'),
),
],
),
),
),);
}
}
无需Watch Widget或扩展程序,当Widget从Widget树中移除时,signal将自动处置。SignalsMixin是一个混合类,当Widget从Widget树中移除时,它会自动处置在该状态下创建的signal。
总结
signals是一种存储状态的方式,当值发生变化时自动通知监听器,而不需要setState、Provider或其他重量级解决方案。可以通过signal发送一个信号,可以通过computed组合计算多个signal,可以通过effect监听数据变化,可以通过Batch批量处理多个signal的值。signals使用起来还是挺简单的,但是signals的原理需要下篇来讲解。希望文章对您有帮助,祝大家编码愉快。
参考资料
dartsignals.dev/ pub.dev/packages/si... dartsignals.dev/reference/o...