[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 

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

相关推荐
2501_916008891 小时前
Web 前端开发常用工具推荐与团队实践分享
android·前端·ios·小程序·uni-app·iphone·webview
SkylerHu1 小时前
前端代码规范:husky+ lint-staged+pre-commit
前端·代码规范
菜鸟una2 小时前
【微信小程序 + 消息订阅 + 授权】 微信小程序实现消息订阅流程介绍,代码示例(仅前端)
前端·vue.js·微信小程序·小程序·typescript·taro·1024程序员节
Yeats_Liao2 小时前
Go Web 编程快速入门 05 - 表单处理:urlencoded 与 multipart
前端·golang·iphone
飞翔的佩奇2 小时前
【完整源码+数据集+部署教程】【运动的&足球】足球场地区域图像分割系统源码&数据集全套:改进yolo11-RFAConv
前端·python·yolo·计算机视觉·数据集·yolo11·足球场地区域图像分割系统
拉不动的猪2 小时前
h5后台切换检测利用visibilitychange的缺点分析
前端·javascript·面试
桃子不吃李子2 小时前
nextTick的使用
前端·javascript·vue.js
萌新小码农‍2 小时前
SpringBoot+alibaba的easyexcel实现前端使用excel表格批量插入
前端·spring boot·excel
冰暮流星3 小时前
css3新增背景图片样式
前端·css·css3
消失的旧时光-19433 小时前
iFlutter --> Flutter 开发者 的 IntelliJ IDEA / Android Studio 插件
flutter·android studio·intellij-idea