Flutter InheritedWidget与setState及局部刷新

数据共享(InheritedWidget)学习笔记:

  1. InheritedWidget可以跨组件(同一个InheritedWidget祖先)获取数据,同时InheritedWidget重新build时,会触发所有使用inheritFromWidgetOfExactType获取数据的Widget进行build,原理是在该函数中将这些Widget添加到依赖列表中,build时触发其didChangeDependencies函数,该函数会调用markNeedsBuild触发build
  2. ancestorInheritedElementForWidgetOfExactType如果使用该函数获取数据,则不会将context添加到依赖列表,不会触发didChangeDependencies

setState相关

setState()会调用markNeedsBuild触发context即本Element重绘——rebuild,而rebuild会调用performRebuild,这个函数不同类型的Widget有不同的实现,如StatefulWidget对应的ComponentElementCenter对应的SingleChildRenderObjectElementRow对应的MultiChildRenderObjectElement

  • 大原则:尽量减少重新生成Element,因为这是个复杂对象,基类ElementupdateChild实现如下:
/// flutter的注释特别多,基本就是一份官方教程,这里解释了Widget的更新操作
/// The following table summarizes the above:
///
/// |                     | **newWidget == null**  | **newWidget != null**   |
/// | :-----------------: | :--------------------- | :---------------------- |
/// |  **child == null**  |  Returns null.         |  Returns new [Element]. |
/// |  **child != null**  |  Old child is removed, returns null. | Old child updated if possible, returns child or new [Element]. |
  @protected
  Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
    ...
    if (newWidget == null) {
      if (child != null)
      2
        deactivateChild(child);
      return null;
    }
    if (child != null) {
      if (child.widget == newWidget) {
        if (child.slot != newSlot)
          updateSlotForChild(child, newSlot);
        return child;
      }
      if (Widget.canUpdate(child.widget, newWidget)) {
        if (child.slot != newSlot)
          updateSlotForChild(child, newSlot);
        child.update(newWidget);
        ...
        return child;
      }
      deactivateChild(child);
    }
    return inflateWidget(newWidget, newSlot);
  }

不是一刀切的直接调用inflateWidget新建Element,而是分情况处理,尽量减少新建操作

  1. oldWidget==nullnewWidget==null,无操作;
  2. oldWidget!=nullnewWidget==null,删除节点;
  3. oldWidget==nullnewWidget!=null,新增节点;
  4. oldWidget!=nullnewWidget!=null,前3种都是常规操作,这种才是大多数会遇到的情况,这里又分类进行处理
    • 返回Widget一致,一般情况下我们写的代码Widget都是new出来的,如Center(),这种不符合,必须是完全一个对象才可以,这种直接返回就可以;
    • 符合Widget.canUpdate,大多数的情况是这种,canUpdate有两个条件,类型与key,一般用Widget时不会使用key,只要类型一致即可满足条件,此时,执行其update函数,这个函数也是因类型而异,如StatelessElement只是将自己标记为需要重绘,同时调用rebuild,也就是说StatelessElement只是将rebuild的指令传递了下去,给了孩子节点。
    • 实在没办法了,只能重建了

举一个简单例子:

import 'package:flutter/material.dart';

class TestWidget extends StatefulWidget {
  @override
  _TestWidgetState createState() => _TestWidgetState();
}

class _TestWidgetState extends State<TestWidget> {
  int count= 0;
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Text('$count'),
        RaisedButton(
          onPressed: (){
            setState(() {
              count+=1;
            });
          },
          child: Text('Add'),
        )
      ],
    );
  }
}

说一下点击按钮后,Text('$count')的重绘路径

下一帧
子节点开始重绘
触发Column子Widget重绘
触发Text重绘
Text其实是个StatelessWidget
到这里只更新数据而不会重新创建Element
setState
Element.markNeedsBuild
TestWidget-ComponentElement.rebuild
ComponentElement.performRebuild
Column--MultiChildRenderObjectElement.update
RenderObjectElement.updateChildren
Element.updateChild
StatelessElement.update
RichText-RenderObjectElement.update
RichText-updateRenderObject

也就是说,setState后,整个Widget触发重绘,Flutter框架会将轻量的Widget重建,而尽量减少Element的重建,保证性能。但总的来说,如果是大Widget如很长的列表之类,重绘还是很耗性能的操作。如果想要局部刷新,最好还是用StatefulBuilderProvider之类的插件来实现。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值