20210704从InheritedWidget容器与布局管理容器所引出的对“包裹”与“对接”的探究 - ziyouzy/2021blog GitHub Wiki

首先明确结论:
包裹往往是用来描述布局容器的,对接则是用来描述InheritedWidget的
相同点是两者都需要包裹,不同点是InheritedWidget是分两步实现的自身功能,那就是需要先完成包裹后才能进行对接

InheritedWidget往往会作为业务逻辑所需的某个“状态域”的顶层Widget,这个Widget内部会包含需要共享的所有数据
而其子widget,以及子子,甚至子子孙孙,都可以直接读写这个其所在的“状态域”Widget内所准备好共享的数据(如实时显示某个或某几个共享的数据)

接下来继续细说:
读与写是很不同的
优先考虑“读”需要被“写”所触发的设计模式,而不是通过某个时钟刻意触发的设计模式,这样才能最大程度节省资源开销,设计模式也是最为合理
设想一个典型的例子:
比如说有一个变量的值为1,同时有个timer每隔2秒决定一次变量的值是否需要发生改变
虽然表面上逻辑是每两秒就会变化一次,但是如果大部分的“2秒后”系统判定变量的值依然为1,那就不需要系统去做任何改变,也就是说ui显示树不会发生改变
最直观的现实中的例子是,如果某两次连续接收从后端tcpserver所发来的值都是1那么就不用去触发任何工序了 然而假如某一次是1,紧跟着接受到了1.1那么就需要触发写的操作了,或者说是改变InheritedWidget内部某个状态值的操作,这也代表着一次**“对接操作”**的真正开始
当某个值被改变后,用到这个值的某个子子孙孙内部由于拥有着方法didChangeDependencies(),因此他会立刻感受到某个他所“依赖”的某个状态发生改变了
如是他就会紧随着这个状态改变事件进行后续的响应逻辑

ps:这些响应逻辑都是通过重写didChangeDependencies()来实现的,比如切换背景图,修改字体颜色等等:

class _TestWidget extends StatefulWidget {
    @override
    __TestWidgetState createState() => new __TestWidgetState();
}

class __TestWidgetState extends State<_TestWidget> {
    @override
    Widget build(BuildContext context) {
    //使用InheritedWidget中的共享数据,或者说唯一的目的是拿到某一个数据,这里是int data
    return Text(ShareDataWidget
        .of(context)
        .data
        .toString());
    }

    @override
    void didChangeDependencies() {
        super.didChangeDependencies();
        //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
        //如果build中没有依赖InheritedWidget,则此回调不会被调用。
        print("Dependencies change");
    }
}

可以把这个_TestWidget想象成我即将去设计的仪表盘ui控件,data字段就是温度或始终以及“开与关”的具体值,这些具体值都将属于 YHInheritedWidget这个状态管理控件,同时也可以让YHInheritedWidget属于package yh
同时package yh也会包含着YHyibiaopanWidget(等同于这里的 _TestWidget与__TestWidgetState)
而在__YHyibiaopanWidgetState内部通过这句:

return Text(YHInheritedWidget
    .of(context)
    .data
    .toString());
}

实现一种**“类与类间解耦式数据共享”**的数据通信方式
而实现这种方式所必须用到的东西就被叫做“建立依赖”
需要用当两个内置关键词,那就是state和of,将在下篇文章里介绍

现在还有最后的收尾工序需要探讨一下
那就式改变仪表盘颜色和字体颜色
首先一定要在脑子里将“字体颜色”和“数据内容”明确的区分开,数据内容其实就是“状态管理的状态”,换句话说数据内容在逻辑上根本就不是属于这个子widget的
而字体颜色则是属于这个子widget的,同时仪表盘颜色其实是和背景图片一样,也是属于这个子widget的
正常与异常的判定逻辑是子widget需要实现的逻辑,也就是说是YHyibiaopanWidget需要我自定义设计出的判定逻辑

如在内部添加一个isNormal布尔值
然后背景图路径,文字颜色,红/蓝仪表盘背景路径之类的都是通过这个isNormal所设计的条件表达式完成,如:
isNoamal?“/blue.png”:“/red.png”
来实现逻辑分支

但是判定逻辑仅仅是逻辑本身,或者说是一个自定义函数,如果需要去触发这个逻辑,你就需要用到didChangeDependencies()了
数值发生变化后会触发didChangeDependencies(),你需要在里边比较一下值,比如温度是否超过了阈值
如果超限了就需要让isNormal=false
但是触发后的一切都是属于子widget内部的自管理机制,因此需要遵守自管理规则于是乎应该是这样的:

didChangeDependencies(){
    if isNormal ==true&&(data<15||date>50){
        setState(() {
            isNormal =false;
        })
    }else if isNormal ==false{
        setState(() {
            isNormal =true;
        })
    }   
}

还不完善,总之就是类似这样的逻辑

接下来再举一个children的例子,来强化理解一些“地位上的逻辑”

children: <Widget>[
    Padding(
        padding: const EdgeInsets.only(bottom: 20.0),
        child: _TestWidget(),//子widget中依赖ShareDataWidget
    ),

    RaisedButton(
        child: Text("Increment"),
        //每点击一次,将count自增,然后重新build,ShareDataWidget的data将被更新  
        onPressed: () => setState(() => ++count),
    )
],

其实RaisedButton和_TestWidget()是同一个层面上的东西,也就是说RaisedButton和我即将去设计的YHyibiaopanWidget是同一个层面的东西
区别只不过是YHyibiaopanWidget初始化时不需要传入参数
或者说参数表只是进行数据“对接”的方式之一,这里的YHyibiaopanWidget没有通过参数表进行数据对接,而是通过上边所说的“建立依赖关系”(隐式)实现的数据“对接”

而onPressed()和didChangeDependencies()也是地位一致的,他们都是上面所描述的触发器
而区别只是在于onPressed是通过人的手指头触发的,didChangeDependencies则是通过其所依赖的(如YHInheritedWidget)内的数据发生了状态改变而触发的

以上~

⚠️ **GitHub.com Fallback** ⚠️