本文续接上篇文章: #[Flutter小试牛刀] 低配版signals,添加多层监听链
在上篇文章里,已经写了一个减配版的signals,且添加了多层监听链,但它在页面刷新上粒度不够,如果每次值改变都去刷新整个页面,对性能肯定是不友好的。本次更新是添加局部更新的功能。
为了实现这个功能,我们可以先定义一个UserBuilder,它是一个Widget,传入参数只有一个WidgetBuilder,只要是WidgetBuilder创建的Widget树里有value的引用则添加到这个UseBuilder的监听列表中,只要这些值改变则重新刷新这个UseBuilder,达到局部刷新的效果。
dart
class UseBuilder extends StatefulWidget {
final WidgetBuilder builder;
const UseBuilder(this.builder, {super.key});
@override
State<UseBuilder> createState() => _UseBuilderState();
}
class _UseBuilderState extends MixinUseState<UseBuilder> {
late final result = useCompute(() {
return widget.builder(context);
}, autoNotify: true);
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return result.value ?? const SizedBox.shrink();
}
@override
void reassemble() {
super.reassemble();
WidgetsBinding.instance.addPostFrameCallback((_) {
result.notifyListeners();
if (mounted) setState(() {});
result.value;
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
result.notifyListeners();
}
@override
void didUpdateWidget(covariant UseBuilder oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.builder != widget.builder) {
result.notifyListeners();
}
}
}
以上为代码的实现,实现的核心思路是通过useCompute来创建UseBuilder的Widget,在这里autoNotify: true表示将这个compute和该Widget绑定,只要compute的值发生变化时,会调用setState重新刷新UI。
又因为useCompute实际上返回的是WidgetBuilder的回调,因此在回调函数中的所有useValue值都会绑定到这个compute上,这些value值改变会触发compute的改变从而触发UseBuilder的重绘。
当然一些其它的情况也会导致UseBuilder的重绘,因此我们需要重写didChangeDependencies 和 didUpdateWidget方法。
再写一段测试代码测试它的可行性:
dart
class Page2 extends StatefulWidget {
const Page2({super.key});
@override
State<Page2> createState() => _Page2State();
}
class _Page2State extends State<Page2> with MixinUseState {
late final c1 = use(0)
..onDispose = (value) {
debugPrint("Page2 c1 dispose $value");
}
..onChange = (value) {
debugPrint("Page2 c1 onChange $value");
};
late final c2 = use(2)
..onDispose = (value) {
debugPrint("Page2 c2 dispose $value");
}
..onChange = (value) {
debugPrint("Page2 c2 onChange $value");
};
late final c3 = useCompute(() {
return c1.value + c2.value;
})
..onDispose = (value) {
debugPrint("Page2 c3 dispose $value");
}
..onChange = (value) {
debugPrint("Page2 c3 onChange $value");
};
late final counter = useCompute(() {
return c1.value + c3.value;
})
..onDispose = (value) {
debugPrint("Page2 counter dispose $value");
}
..onChange = (value) {
debugPrint("Page2 counter onChange $value");
};
@override
Widget build(BuildContext context) {
debugPrint("Page2 build ");
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Page2 You have pushed the button this many times:',
),
TextButton(
onPressed: () {
c2.value++;
},
child: const Text("add")
),
UseBuilder((context) {
debugPrint("RefWidget c3 build ");
return Text(
"${c3.value}",
style: Theme.of(context).textTheme.headlineMedium,
);
}),
UseBuilder((context) {
debugPrint("RefWidget counter build ");
return Text(
'${counter.value}',
style: Theme.of(context).textTheme.headlineMedium,
);
}),
],),
);
}
}
从上面代码中,点击按钮会改变c1的值,从而触发改变c3和counter的值,每次点击都会输出:
dart
RefWidget c3 build
RefWidget counter build
可以看出局部刷新是有效的。