[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 

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

相关推荐
布兰妮甜11 分钟前
单例模式在前端(JavaScript)中的实现与应用
前端·javascript·单例模式
Mintopia11 分钟前
Three.js 加载模型文件:从二进制到像素的奇幻漂流
前端·javascript·three.js
前端小巷子30 分钟前
跨域问题解决方案:JSONP
前端·javascript·面试
eric*168838 分钟前
尚硅谷张天禹老师课程配套笔记
前端·vue.js·笔记·vue·尚硅谷·张天禹·尚硅谷张天禹
程序员爱钓鱼1 小时前
Go语言中的反射机制 — 元编程技巧与注意事项
前端·后端·go
GIS之路1 小时前
GeoTools 结合 OpenLayers 实现属性查询(二)
前端·信息可视化
烛阴1 小时前
一文搞懂 Python 闭包:让你的代码瞬间“高级”起来!
前端·python
AA-代码批发V哥1 小时前
HTML之表单结构全解析
前端·html
瓜子三百克2 小时前
十、高级概念
flutter
聪聪的学习笔记2 小时前
【1】确认安装 Node.js 和 npm版本号
前端·npm·node.js