小程序开发|小程序制作|小程序开发网

搜索

Flutter局部刷新原理

2023-2-23 16:42| 发布者: xiaoxiao| 查看: 250| 评论: 2

摘要: 概述在Flutter中,我们知道,刷新界面要调用setState方法,在一个界面中,通常只需要刷新某个组件或者某一部分组件,这种情况下调用父级State的setState方法会造成不必要的资源浪费。 在这种需求下,我们需要找到一
  • 概述

    在Flutter中,我们知道,刷新界面要调用setState方法,在一个界面中,通常只需要刷新某个组件或者某一部分组件,这种情况下调用父级State的setState方法会造成不必要的资源浪费。 在这种需求下,我们需要找到一个方式可以进行局部刷新。

  • 做法和原理

    其实局部刷新很简单,我们只需要把需要刷新的组件聚到一个StatefulWidget中,通过一个State来管理,然后刷新的时候调用这个State的setState方法即可完成针对这部分组件的刷新,父级和兄弟级的StatefulWidget都不会被引起rebuild。

    原理也很好理解,就是调用setState的流程,调用setState方法:

    Element的markNeedsBuild方法会调用BuildOwner的scheduleBuildFor方法:

    scheduleBuildFor方法会把当前element放入BuildOwner的_dirtyElements中:

    当下一个Frame到来时框架会调用WidgetsBinding的drawFrame方法:

    这里会调用BuildOwner的buildScope方法:

    在buildScope方法中会循环 _dirtyElements,依次调用里面的element的rebuild方法进行构建,rebuild方法中又会调用performRebuild方法:

    performRebuild方法是在StatefulElement和StatelessElement的共同父类ComponentElement中实现的,在这个方法中会调用build方法创建Widget:

     state.build(this);//StatelessElement中实现的build方法@overrideWidget build() => widget.build(this);

    所以局部刷新原理的核心就是把需要刷新的区域收到一个State中,然后调用这个State的setState方法就会使当前的这个State的element变为dirty,把它放入需要重新构建的element集合中,在帧回调后会循环这个集合调用它的rebuild方法进行重新构建,因为我们更上一级的State并没有执行它的setState方法所以不会添加在需要重新构建的element集合中。

  • 关于get框架的应用

    get框架的局部刷新也是通过上面的原理完成的,下面我们来看看他是怎么封装的。

    首先它使用一个叫做GetxController的东西来提供统一刷新的api接口:

    ? ids, bool condition = true]) {    if (!condition) {      return;    }    //全部刷新    if (ids == null) {      refresh();    } else {      //局部刷新      for (final id in ids) {        refreshGroup(id);      }    }  }}

    在页面打开的时候会创建这个controller,然后通过调用这个controller的update方法执行局部构建,可以看到,局部构建需要一个id,这个id是什么时候绑定的呢?

    使用get框架的局部刷新需要把要刷新的组件们用一个GetBuilder包装起来,那这个GetBuilder构造时就可以传入一个id值,GetBuilder是一个StatefulWidget,他的State中的initState方法里调用了一个_subscribeToController方法:

    addListenerId方法中:

    [];  _updatersGroupIds![key]!.add(listener);  return () => _updatersGroupIds![key]!.remove(listener);}

    可以看到,这里根据id添加了一个回调函数,这里用的数组存放,可见可以通过指定同一个id的方式来实现几个区域联动刷新。

    回到上面的refreshGroup方法,内部会调用_notifyIdUpdate方法:

    可见,在这里根据id查找并执行了所有相关的函数回调。

    那么函数回调是什么呢?_subscribeToController方法中,addListenerId方法添加的函数回调如果默认的话是getUpdate,它指向一个函数,这个函数在GetBuilderState依赖的mixin—GetStateUpdaterMixin中定义:

    可以看到,正是在这里调用了setState来触发重新构建的,因为是在GetBuilderState中调用的setState方法,所以在GetBuilder之上的其他State是不会触发回调的,这和上面我们分析的原理是一样的。

  • 总结

    局部构建的原理就是用子State来拦截构建的范围,不把所有的组件树都放在一个大的State里面构建,通过调用子State的setState方法来实现针对子Widget树的重新构建,这样就实现了局部刷新。

    据此,我们当然可以不用get框架的局部刷新,完全可以自定义,我试着写了一下,有几个需要注意的点:

    1. setState一定要在需要局部刷新的State中调用;

    2. 调用setState的逻辑要通过一个函数暴露出来;

    3. 因为我们要保证随时可以刷新,所以我们需要一个随时获取且不会改变的对象来保存这个回调,相当于get的controller;

    4. 因为我们可能会有很多个局部需要刷新,它们必须独立且可以区分,所以我们需要保存回调函数的集合是一个可已按照key-value的形式来存放的集合,get中使用了String-List的形式,这样可以刷新好几块区域,我用的是一个String-dynamic的Map来存放,这个过程中发现了一个需要注意的点:

      Map的putIfAbsent方法的第二个参数规定是:

      如果使用这个方法设置回调函数,则需要在一个函数中返回这个回调函数才行。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

鲜花

握手

雷人

路过

鸡蛋
发表评论

最新评论

引用 附原 2023-3-11 14:02
向前辈学习!
引用 梅西 2023-3-7 13:48

查看全部评论(2)

返回顶部