[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 

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

相关推荐
F2E_Zhangmo7 分钟前
基于cornerstone3D的dicom影像浏览器 第三章 拖拽seriesItem至displayer上显示第一张dicom
前端·javascript·cornerstone·cornerstone3d·cornerstonejs
gnip5 小时前
Jst执行上下文栈和变量对象
前端·javascript
excel5 小时前
🐣 最简单的卷积与激活函数指南(带示例)
前端
醉方休6 小时前
npm/pnpm软链接的优点和使用场景
前端·npm·node.js
拉不动的猪6 小时前
简单回顾下Weakmap在vue中为何不能去作为循环数据源,以及替代方案
前端·javascript·vue.js
How_doyou_do6 小时前
数据传输优化-异步不阻塞处理增强首屏体验
开发语言·前端·javascript
奇舞精选6 小时前
超越Siri的耳朵:ASR与Whisper零代码部署实战指南
前端·人工智能·aigc
奇舞精选6 小时前
Nano Banana 如何为前端注入 AI 控制力
前端·aigc
一支鱼6 小时前
基于 Node.js 的短视频制作神器 ——FFCreator
前端·node.js·音视频开发
DT——6 小时前
前端登录鉴权详解
前端·javascript