美文网首页
flutter provider使用及其原理分析

flutter provider使用及其原理分析

作者: 某非著名程序员 | 来源:发表于2021-08-31 08:40 被阅读0次

    官方刷新框架provider及其实现原理。

    • Provider:实现数据改变时,对应的局部widget自动刷新。
    • 响应式的实现。解决InheritedWidget由上而下的传递方式,实现model改变,widget自动刷新

    provider的使用

    1. 定义对象并继承ChangeNotifier,在变化的时候发送通知方法notifyListeners:
    class OrgInfo extends ChangeNotifier {
      List<OrgModel> orgs;
      OrgModel curorg;
    
      OrgInfo({this.orgs, this.curorg});
    
      void setOrgInfo(List<OrgModel> orgsParams, OrgModel curorgParams) {
        orgs = orgsParams;
        curorg = curorgParams;
        notifyListeners();
      }
    
      //当被剔除组织时,移除列表数据
      void removeOrgInfo(String orgId) {
        for (var orgModel in orgs) {
          if (orgModel.orgId == orgId) {
            orgs.remove(orgModel);
            break;
          }
        }
        notifyListeners();
      }
    
      void addOrgModel(OrgModel orgModel) {
        orgs.add(orgModel);
        notifyListeners();
      }
    }
    
    1. 在app初始化时注册model:
    class _MyAppState extends State<MyApp> {
      ...
      @override
      Widget build(BuildContext context) {
        return MultiProvider(
            providers: [
              ChangeNotifierProvider.value(value: UserManager.instance.orgInfo),
            ],
            child: MaterialApp(
                ...);
      }
    }
    
    1. 在需要的自动局部的刷新使用Consumer,控件精确的越细越好:
    Widget build(BuildContext context) {
        return Column(
          children: [
            Expanded(
                child: MediaQuery.removePadding(
                    removeTop: true,
                    context: context,
                    child: Consumer<OrgInfo>(
                      builder: (context, OrgInfo orgInfo, _) => ListView.builder(
                        shrinkWrap: true,
                        itemBuilder: (BuildContext context, int index) {
                          if (index == 0) {
                            return orgHeader(
                                orgInfo.orgs[widget.selectIndex].orgName);
                          }
                          return cell(index - 1);
                        },
                        itemCount: orgInfo.orgs.length + 1,
                      ),
                    ))),
            SizedBox(
              height: 200,
            )
          ],
        );
      }
    

    原理

    其中model对象继承ChangeNotifier,被观察者对象,当model对象改变时,调用notifyListeners通知观察者刷新。
    通知如何与widget结合实现自动刷新。

    InheritedWidget

    参考文章:
    数据共享(InheritedWidget)
    关于跨组件传递数据,你只需要记住这三招

    案例:点击按钮,刷新子widget计数
    如果不使用InheritedWidget:正常做法定义一个count变量,然后当参数传递到子widget。
    如果使用InheritedWidget,代码如下:

    class ShareDataWidget extends InheritedWidget {
      ShareDataWidget({
        @required this.data,
        Widget child
      }) :super(child: child);
        
      final int data; //需要在子树中共享的数据,保存点击次数
        
      //定义一个便捷方法,方便子树中的widget获取共享数据  
      static ShareDataWidget of(BuildContext context) {
        return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
      }
    
      //该回调决定当data发生变化时,是否通知子树中依赖data的Widget  
      @override
      bool updateShouldNotify(ShareDataWidget old) {
        //如果返回true,则子树中依赖(build函数中有调用)本widget
        //的子widget的`state.didChangeDependencies`会被调用
        return old.data != data;
      }
    }
    
    class _TestWidget extends StatefulWidget {
      @override
      __TestWidgetState createState() => new __TestWidgetState();
    }
    
    class __TestWidgetState extends State<_TestWidget> {
      @override
      Widget build(BuildContext context) {
        //使用InheritedWidget中的共享数据
        return Text(ShareDataWidget
            .of(context)
            .data
            .toString());
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
        //如果build中没有依赖InheritedWidget,则此回调不会被调用。
        print("Dependencies change");
      }
    }
    
    class InheritedWidgetTestRoute extends StatefulWidget {
      @override
      _InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState();
    }
    
    class _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> {
      int count = 0;
    
      @override
      Widget build(BuildContext context) {
        return  Center(
          child: ShareDataWidget( //使用ShareDataWidget
            data: count,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              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),
                )
              ],
            ),
          ),
        );
      }
    }
    
    • ShareDataWidget继承InheritedWidget的模型对象,_TestWidget是InheritedWidgetTestRoute的子类。点击父类中的按钮,_TestWidget数据自动刷新。

    小结:

    • 在父widget中state构造ShareDataWidget,子widget的state可以ShareDataWidget.of(context).data获取数据,避免了数据传递。
    • 数据自上而下传递。
    • setState也能刷新子widget。使用ShareDataWidget继承InheritedWidget的模型对象,子类的didChangeDependencies也会刷新。
    • dependOnInheritedWidgetOfExactType 子state会调用didChangeDependencies;getElementForInheritedWidgetOfExactType 子state不会调用didChangeDependencies。

    缺点

    • setState会刷新所有widget
    • InheritedWidget也会全部刷新

    Provider

    InheritedProvider

    // 一个通用的InheritedWidget,保存任需要跨组件共享的状态
    class InheritedProvider<T> extends InheritedWidget {
      InheritedProvider({@required this.data, Widget child}) : super(child: child);
      
      //共享状态使用泛型
      final T data;
      
      @override
      bool updateShouldNotify(InheritedProvider<T> old) {
        //在此简单返回true,则每次更新都会调用依赖其的子孙节点的`didChangeDependencies`。
        return true;
      }
    }
    

    InheritedProvider是继承InheritedWidget对象,dependOnInheritedWidgetOfExactType时会刷新子类的didChangeDependencies。

    ChangeNotifier实现

    class ChangeNotifier implements Listenable {
      List listeners=[];
      @override
      void addListener(VoidCallback listener) {
         //添加监听器
         listeners.add(listener);
      }
      @override
      void removeListener(VoidCallback listener) {
        //移除监听器
        listeners.remove(listener);
      }
      
      void notifyListeners() {
        //通知所有监听器,触发监听器回调 
        listeners.forEach((item)=>item());
      }
       
      ... //省略无关代码
    }
    

    ChangeNotifier把方法添加到数组中,notifyListeners遍历调用。观察者实现。

    ChangeNotifierProvider

    class ChangeNotifierProvider<T extends ChangeNotifier> extends StatefulWidget {
      ChangeNotifierProvider({
        Key key,
        this.data,
        this.child,
      });
    
      final Widget child;
      final T data;
    
      //定义一个便捷方法,方便子树中的widget获取共享数据
      static T of<T>(BuildContext context) {
        final type = _typeOf<InheritedProvider<T>>();
        final provider =  context.dependOnInheritedWidgetOfExactType<InheritedProvider<T>>();
        return provider.data;
      }
    
      @override
      _ChangeNotifierProviderState<T> createState() => _ChangeNotifierProviderState<T>();
    }
    
    class _ChangeNotifierProviderState<T extends ChangeNotifier> extends State<ChangeNotifierProvider<T>> {
      void update() {
        //如果数据发生变化(model类调用了notifyListeners),重新构建InheritedProvider
        setState(() => {});
      }
    
      @override
      void didUpdateWidget(ChangeNotifierProvider<T> oldWidget) {
        //当Provider更新时,如果新旧数据不"==",则解绑旧数据监听,同时添加新数据监听
        if (widget.data != oldWidget.data) {
          oldWidget.data.removeListener(update);
          widget.data.addListener(update);
        }
        super.didUpdateWidget(oldWidget);
      }
    
      @override
      void initState() {
        // 给model添加监听器
        widget.data.addListener(update);
        super.initState();
      }
    
      @override
      void dispose() {
        // 移除model的监听器
        widget.data.removeListener(update);
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return InheritedProvider<T>(
          data: widget.data,
          child: widget.child,
        );
      }
    }
    
    • 泛型T表示数据InheritedProvider data,继承自ChangeNotifier。被观察者
    • ChangeNotifierProvider data的观察者,T data监听update,当数据发生变化时,update方法调用setState,创建InheritedProvider,刷新child。
    • 实现InheritedProvider data与widget child的绑定。

    data与UI的关系

    data是被观察者,ChangeNotifierProvider是data的观察者;data改变时,ChangeNotifierProvider监听到变化,调用setState,构建InheritedProvider刷新UI。实现data与UI的绑定。

    总结

    1. setState能刷新widget子树,刷新范围太大,并且需要把数据对象传递到子类。
    2. InheritedWidget不用传递数据对象,通过context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();获取父类的数据。但是刷新范围大,只能由上而下传递。
    3. Provider可以实现局部刷新。只要数据对象改变,UI能自动变化,实现响应式编程。屏蔽刷新逻辑,实现响应式数据与UI的绑定。无论是子类或父类改变数据都能刷新绑定的UI。


      image

    参考文章:

    相关文章

      网友评论

          本文标题:flutter provider使用及其原理分析

          本文链接:https://www.haomeiwen.com/subject/fgtjiltx.html