[Flutter小试牛刀] 低配版signals,添加局部刷新

本文续接上篇文章: #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 

可以看出局部刷新是有效的。

相关推荐
RobinDevNotes20 分钟前
从 Electron 到 GPUI,9 个跨平台桌面方案该怎么选
前端·javascript·rust·electron·openresty·前端开发工具
jay神1 小时前
基于 YOLOv8 的轴承缺陷检测(Web 版)
前端·人工智能·yolo
jay神1 小时前
基于 YOLOv8 的森林火灾检测系统(Web 版)
前端·yolo
code2cat2 小时前
HTML基础篇之第一张网页:从index.html到浏览器里的任务清单
前端·html
miss2 小时前
AI 改完不敢上线?OpenSpec 规约驱动开发实战
前端·代码规范·前端工程化
AiMan3 小时前
腾讯ima还是不可以导出左侧所有对话,只能单条导出,方法不对吗
前端
去伪存真3 小时前
并发量一高就装死?Agent 全链路“防猝死”手册
前端·agent
Moment3 小时前
AI 编程时代,聊聊我的 Mac mini 双屏桌面
前端·后端
AiMan3 小时前
先确认能不能一次把Microsofat Copilot所有内容打包一个文件导出
前端
总要学点什么3 小时前
第97篇:三维数据加载-城市建筑白模自定义着色器——让一条光带沿高度扫上去,快慢却由帧率说了算
前端·gis·webgl·cesium·着色器·空间分析·白模