[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 

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

相关推荐
步行cgn4 小时前
Vue 中的数据代理机制
前端·javascript·vue.js
GH小杨4 小时前
JS之Dom模型和Bom模型
前端·javascript·html
星月心城5 小时前
JS深入之从原型到原型链
前端·javascript
你的人类朋友6 小时前
🤔Token 存储方案有哪些
前端·javascript·后端
烛阴6 小时前
从零开始:使用Node.js和Cheerio进行轻量级网页数据提取
前端·javascript·后端
liuyang___6 小时前
日期的数据格式转换
前端·后端·学习·node.js·node
贩卖纯净水.7 小时前
webpack其余配置
前端·webpack·node.js
码上奶茶7 小时前
HTML 列表、表格、表单
前端·html·表格·标签·列表·文本·表单
抹茶san8 小时前
和 Trae 一起开发可视化拖拽编辑项目(1) :迈出第一步
前端·trae