美文网首页Flutter
数据传递/状态管理 一InheritedWidget使用示例

数据传递/状态管理 一InheritedWidget使用示例

作者: HawkFlying | 来源:发表于2020-06-17 10:20 被阅读0次

    介绍:

    在Theme.of(context)、 MediaQuery.of(context)等实现代码都能看到InheritedWidget的身影:

    • Theme:Theme.of(context)->_InheritedTheme->InheritedTheme->InheritedWidget
    • MediaQuery: MediaQuery.of(context)->MediaQuery->InheritedWidget
      其实Theme和MediaQuery最终都是通过InheritedWidget实现的

    InheritedWidget是Flutter中非常重要的一个功能型组件,它提供了一种数据在widget树中从上到下传递、共享的方式,比如我们在应用的根widget中通过InheritedWidget共享了一个数据,那么我们便可以在任意子widget中来获取该共享的数据!这个特性在一些需要在widget树中共享数据的场景中非常方便!

    InheritedWidget使用步骤:

    1、首先继承InheritedWidget,并实现自定义of静态方法和重写updateShouldNotify方法,如下:

    /*
     * <p> 数据共享InheritedWidget</p>
     *
     * @author 许志新
     * @version 1.0  (2020-06-14)
     */
    import 'package:flutter/material.dart';
    
    class ShareDataInheritedWidget extends InheritedWidget {
      final String name; //用于区分子widget调用的是哪个ShareDataInheritedWidget
    
      //共享数据
      final InheritedShareModel shareModel;
    
      //点击+号的方法
      final Function() increment;
    
      //点击-号的方法
      final Function() reduce;
    
      ShareDataInheritedWidget(
        this.name, {
        Key key,
        @required this.shareModel,
        @required this.increment,
        @required this.reduce,
        @required Widget child,
      }) : super(key: key, child: child);
    
      ///定义一个便捷方法,方便子树中的widget获取共享数据--InheritedWidget变化时会通知子widget的didChangeDependencies
      static ShareDataInheritedWidget of(BuildContext context) {
        return context.dependOnInheritedWidgetOfExactType();
      }
    
      ///定义一个便捷方法,方便子树中的widget获取共享数据--InheritedWidget变化时不会通知子widget的didChangeDependencies
      static ShareDataInheritedWidget ofNotNoticeDependence(BuildContext context) {
        return context
            .getElementForInheritedWidgetOfExactType<ShareDataInheritedWidget>()
            ?.widget;
      }
    
      ///该回调决定当data发生变化时,是否通知子树中依赖data的Widget
      @override
      bool updateShouldNotify(ShareDataInheritedWidget oldWidget) {
        //如果返回true,则子树中依赖(build函数中有调用)本widget
        //的子widget的`state.didChangeDependencies`会被调用
        return (null != shareModel?.count &&
            null != oldWidget?.shareModel?.count &&
            shareModel.count != oldWidget.shareModel.count);
      }
    
      ///用于展示信息
      String showInfo() {
        return '来自${name ?? ''} InheritedWidget:\ncount值为${shareModel?.count ?? ''}';
      }
    }
    
    class InheritedShareModel {
      final int count;
    
      const InheritedShareModel(this.count);
    }
    

    说明:

    • 自定义静态of方法:提供给子树中的widget来访问我们自定义InheritedWidget,进而可以访问InheritedWidget共享的数据或方法,如上面共享的shareModel或increment等;
    • 重写updateShouldNotify方法:根据自己的业务,决定当共享发生变化时,是否通知子树中依赖共享数据的Widget;如果返回true,则子树中依赖本InheritedWidget的子widget的state.didChangeDependencies会被调用;返回false,则不会调用;

    2、子widget使用自定义InheritedWidget的共享数据或方法,如下:

    class DependenceWidgetA extends StatefulWidget {
      @override
      _DependenceWidgetAState createState() => new _DependenceWidgetAState();
    }
    
    class _DependenceWidgetAState extends State<DependenceWidgetA> {
      @override
      Widget build(BuildContext context) {
        //使用InheritedWidget中的共享数据
        final shareDataInheritedWidget = ShareDataInheritedWidget.of(context);
    
        return Padding(
          padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
          child: Text(
            'DependenceWidgetA :  ${shareDataInheritedWidget?.showInfo() ?? 'null'}',textAlign: TextAlign.center,
            style: TextStyle(fontSize: 20.0),
          ),
        );
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
        //如果build中没有依赖InheritedWidget,则此回调不会被调用。
        print("DependenceWidgetA didChangeDependencies");
      }
    }
    

    说明:

    • 访问方式:子widget通过InheritedWidget的自定义静态of方法来访问InheritedWidget共享的数据和方法;
    • 有效前提条件:子widget必须在InheritedWidget的子树里,才能访问到InheritedWidget共享的数据和方法;否则通过静态of方法获取到的InheritedWidget实例为空;

    3、InheritedWidget状态变化通知子widget,如下:

    class DependenceWidgetA extends StatefulWidget {
      @override
      _DependenceWidgetAState createState() => new _DependenceWidgetAState();
    }
    
    class _DependenceWidgetAState extends State<DependenceWidgetA> {
      @override
      Widget build(BuildContext context) {
        //使用InheritedWidget中的共享数据
        final shareDataInheritedWidget = ShareDataInheritedWidget.of(context);
    
        return Padding(
          padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
          child: Text(
            'DependenceWidgetA :  ${shareDataInheritedWidget?.showInfo() ?? 'null'}',textAlign: TextAlign.center,
            style: TextStyle(fontSize: 20.0),
          ),
        );
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
        //如果build中没有依赖InheritedWidget,则此回调不会被调用。
        print("DependenceWidgetA didChangeDependencies");
      }
    }
    

    说明:

    • didChangeDependencies:当子widget依赖的InheritedWidget变化时,将会调用子widget的didChangeDependencies来通知子widget;
    • 有效提前条件:
      (1) 、InheritedWidget的updateShouldNotify返回true;
      (2) 、InheritedWidget自定of静态方法里面实现调用的是dependOnInheritedWidgetOfExactType而不是getElementForInheritedWidgetOfExactType;

    InheritedWidget完整使用示例:

    自定义InheritedWidget->ShareDataInheritedWidget,用于共享数据和方法,如下:

    class ShareDataInheritedWidget extends InheritedWidget {
      final String name; //用于区分子widget调用的是哪个ShareDataInheritedWidget
    
      //共享数据
      final InheritedShareModel shareModel;
    
      //点击+号的方法
      final Function() increment;
    
      //点击-号的方法
      final Function() reduce;
    
      ShareDataInheritedWidget(
        this.name, {
        Key key,
        @required this.shareModel,
        @required this.increment,
        @required this.reduce,
        @required Widget child,
      }) : super(key: key, child: child);
    
      ///定义一个便捷方法,方便子树中的widget获取共享数据--InheritedWidget变化时会通知子widget的didChangeDependencies
      static ShareDataInheritedWidget of(BuildContext context) {
        return context.dependOnInheritedWidgetOfExactType();
      }
    
      ///定义一个便捷方法,方便子树中的widget获取共享数据--InheritedWidget变化时不会通知子widget的didChangeDependencies
      static ShareDataInheritedWidget ofNotNoticeDependence(BuildContext context) {
        return context
            .getElementForInheritedWidgetOfExactType<ShareDataInheritedWidget>()
            ?.widget;
      }
    
      ///该回调决定当data发生变化时,是否通知子树中依赖data的Widget
      @override
      bool updateShouldNotify(ShareDataInheritedWidget oldWidget) {
        //如果返回true,则子树中依赖(build函数中有调用)本widget
        //的子widget的`state.didChangeDependencies`会被调用
        return (null != shareModel?.count &&
            null != oldWidget?.shareModel?.count &&
            shareModel.count != oldWidget.shareModel.count);
      }
    
      ///用于展示信息
      String showInfo() {
        return '来自${name ?? ''} InheritedWidget:\ncount值为${shareModel?.count ?? ''}';
      }
    }
    
    class InheritedShareModel {
      final int count;
    
      const InheritedShareModel(this.count);
    }
    

    说明:定义name共享变量主要是为了区别子widget访问到的共享数据来自哪个InheritedWidget

    自定义InheritedWidget->TestInheritedWidget,用于理解InheritedWidget在树中的位置对子widget访问InheritedWidget的影响,如下:

    class TestInheritedWidget extends InheritedWidget {
      final String name; //用于区分子widget调用的是哪个TestInheritedWidget
    
      TestInheritedWidget(
        this.name, {
        Key key,
        @required Widget child,
      }) : super(key: key, child: child);
    
      static TestInheritedWidget of(BuildContext context) {
        return context.dependOnInheritedWidgetOfExactType();
      }
    
      ///是否重建widget就取决于数据是否相同
      @override
      bool updateShouldNotify(InheritedWidget oldWidget) {
        return true;
      }
    
      ///用于展示信息
      String showInfo() {
        return '来自${name ?? ''} TestInheritedWidget ';
      }
    }
    

    各种widget访问自定义InheritedWidget里的共享数据和方法,如下:

    class IncreaseWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final shareDataInheritedWidget = ShareDataInheritedWidget.of(context);
    
    //    print('IncreaseWidget :  ${shareDataInheritedWidget?.showInfo()??'null'}');
        return Padding(
          padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
          child: RaisedButton(
              textColor: Colors.black,
              child: Text(
                '+',
                textAlign: TextAlign.center,
              ),
              onPressed: shareDataInheritedWidget?.increment),
        );
      }
    }
    
    class DataWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final shareDataInheritedWidget = ShareDataInheritedWidget.of(context);
    
        final inheritedShareModel = shareDataInheritedWidget?.shareModel;
    //    print('DataWidget :  ${shareDataInheritedWidget?.showInfo() ?? ''}');
    
        return Padding(
          padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
          child: Text(
            '来自${shareDataInheritedWidget?.name ?? ''} InheritedWidget:\ncount值为${inheritedShareModel?.count ?? ''}',
            textAlign: TextAlign.center,
            style: TextStyle(fontSize: 20.0),
          ),
        );
      }
    }
    
    class ReduceWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final shareDataInheritedWidget = ShareDataInheritedWidget.of(context);
    
    //    print('ReduceWidget :  ${shareDataInheritedWidget?.showInfo() ?? ''}');
    
        return Padding(
          padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
          child: RaisedButton(
              textColor: Colors.black,
              child: Text('-', textAlign: TextAlign.center),
              onPressed: shareDataInheritedWidget?.reduce),
        );
      }
    }
    
    class DependenceWidgetA extends StatefulWidget {
      @override
      _DependenceWidgetAState createState() => new _DependenceWidgetAState();
    }
    
    class _DependenceWidgetAState extends State<DependenceWidgetA> {
      @override
      Widget build(BuildContext context) {
        //使用InheritedWidget中的共享数据
        final shareDataInheritedWidget = ShareDataInheritedWidget.of(context);
    
        return Padding(
          padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
          child: Text(
            'DependenceWidgetA :  ${shareDataInheritedWidget?.showInfo() ?? 'null'}',
            textAlign: TextAlign.center,
            style: TextStyle(fontSize: 20.0),
          ),
        );
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
        //如果build中没有依赖InheritedWidget,则此回调不会被调用。
        print("DependenceWidgetA didChangeDependencies");
      }
    }
    
    class DependenceWidgetB extends StatefulWidget {
      @override
      _DependenceWidgetBState createState() => new _DependenceWidgetBState();
    }
    
    class _DependenceWidgetBState extends State<DependenceWidgetB> {
      @override
      Widget build(BuildContext context) {
        //使用InheritedWidget中的共享数据
        final shareDataInheritedWidget =
            ShareDataInheritedWidget.ofNotNoticeDependence(context);
    
        return Padding(
          padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
          child: Text(
            'DependenceWidgetB :  ${shareDataInheritedWidget?.showInfo() ?? 'null'}',
            textAlign: TextAlign.center,
            style: TextStyle(fontSize: 20.0),
          ),
        );
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
        //如果build中没有依赖InheritedWidget,则此回调不会被调用。
        print("DependenceWidgetB didChangeDependencies");
      }
    }
    

    在app层使用InheritedWidget,如下:

    void main() => runApp(App());
    
    class App extends StatefulWidget {
      @override
      _AppState createState() => _AppState();
    }
    
    class _AppState extends State<App> {
      InheritedShareModel _inheritedShareModel;
    
      @override
      void initState() {
        super.initState();
        _initData();
      }
    
      _initData() {
        _inheritedShareModel = InheritedShareModel(0); //初始为0
      }
    
      _incrementCount() {
        setState(() {
          _inheritedShareModel =
              InheritedShareModel(_inheritedShareModel.count + 2); //增幅度为2
        });
      }
    
      _reduceCount() {
        setState(() {
          _inheritedShareModel =
              InheritedShareModel(_inheritedShareModel.count - 2); //减幅度为2
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return ShareDataInheritedWidget(
          'app层-在MaterialApp上',
          shareModel: _inheritedShareModel,
          increment: _incrementCount,
          reduce: _reduceCount,
          child: MaterialApp(
            title: 'InheritedWidget使用示例',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: TestInheritedWidget('app层-在MaterialApp里面', child: HomePage()),
          ),
          /*   child: TestInheritedWidget('app层-在MaterialApp上',child: MaterialApp(
            title: 'InheritedWidget使用示例',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: TestInheritedWidget('app层-在MaterialApp里面', child: HomePage()),
          ),),*/
        );
      }
    }
    

    在home页使用InheritedWidget,如下:

    class HomePage extends StatefulWidget {
      @override
      _HomePageState createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      @override
      Widget build(BuildContext context) {
        return HomePageWidget();
      }
    
      @override
      void deactivate() {
        super.deactivate();
        print('HomePage page deactivate');
      }
    
      @override
      void dispose() {
        super.dispose();
        print('HomePage page dispose');
      }
    }
    
    class HomePageWidget extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return _HomePageWidgetState();
      }
    }
    
    class _HomePageWidgetState extends State<HomePageWidget> {
      CiteModel _testCiteModel;
      InheritedShareModel _inheritedShareModel;
    
      @override
      void initState() {
        super.initState();
        _initData();
        _testCiteModel = CiteModel(1);
      }
    
      @override
      void deactivate() {
        super.deactivate();
        print('test page deactivate  ${_testCiteModel ?? 'null'}');
      }
    
      _initData() {
        _inheritedShareModel = InheritedShareModel(1); //初始化为1
      }
    
      _incrementCount() {
        setState(() {
          _inheritedShareModel =
              InheritedShareModel(_inheritedShareModel.count + 1); //增幅度为1
        });
      }
    
      _reduceCount() {
        setState(() {
          _inheritedShareModel =
              InheritedShareModel(_inheritedShareModel.count - 1); //减幅度为1
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return ShareDataInheritedWidget('home',
            shareModel: _inheritedShareModel,
            increment: _incrementCount,
            reduce: _reduceCount,
            child: Scaffold(
              appBar: AppBar(
                title: Text('InheritedWidget使用示例'),
              ),
              body: Container(
                child: SingleChildScrollView(
                  child: Container(
                    /* width: double.infinity,
                  height: double.infinity,*/
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: <Widget>[
                        SizedBox(
                          height: 20,
                        ),
                        IncreaseWidget(),
                        DataWidget(),
                        ReduceWidget(),
                        DependenceWidgetA(),
                        DependenceWidgetB(),
                        SizedBox(
                          height: 20,
                        ),
                        RaisedButton(
                          child: Text("下一页"),
                          onPressed: () {
                            Navigator.push(
                              //跳转到第二个界面
                              context,
                              MaterialPageRoute(builder: (context) {
                                return NextPage();
                              }),
                            );
                          },
                        ),
                        SizedBox(
                          height: 20,
                        ),
                        RaisedButton(
                          child: Text("home context"),
                          onPressed: () {
                            final ShareDataInheritedWidget
                                shareDataInheritedWidget =
                                ShareDataInheritedWidget.of(context); //home context
    
                            print(
                                'home context ShareDataInheritedWidget :  ${shareDataInheritedWidget?.showInfo() ?? 'null'}');
    
                            final TestInheritedWidget testInheritedWidget =
                                TestInheritedWidget.of(context);
                            print(
                                'home context TestInheritedWidget:  ${testInheritedWidget?.showInfo() ?? 'null'}');
                          },
                        ),
                        SizedBox(
                          height: 20,
                        ),
                        Builder(builder: (BuildContext context) {
                          //context变成home 子widget context
                          return RaisedButton(
                            child: Text("home 子widget context"),
                            onPressed: () {
                              final ShareDataInheritedWidget
                                  shareDataInheritedWidget =
                                  ShareDataInheritedWidget.of(
                                      context); //home 子widget context
    
                              print(
                                  'home 子widget context ShareDataInheritedWidget:  ${shareDataInheritedWidget?.showInfo() ?? 'null'}');
    
                              final TestInheritedWidget testInheritedWidget =
                                  TestInheritedWidget.of(context);
                              print(
                                  'home 子widget context TestInheritedWidget:  ${testInheritedWidget?.showInfo() ?? 'null'}');
                            },
                          );
                        }),
                        SizedBox(
                          height: 20,
                        ),
                        RaisedButton(
                          child: Text("测试type作为map key"),
                          onPressed: () {
                            Navigator.push(
                              context,
                              MaterialPageRoute(builder: (context) {
                                return TestTypePage();
                              }),
                            );
                          },
                        ),
                        SizedBox(
                          height: 20,
                        ),
                        RaisedButton(
                          child: Text("测试引用计数"),
                          onPressed: () {
                            Navigator.push(
                              context,
                              MaterialPageRoute(builder: (context) {
                                return TestModelCitePage(_testCiteModel);
                              }),
                            );
                          },
                        ),
                        Padding(
                          padding: const EdgeInsets.only(
                              left: 10.0, top: 10.0, right: 10.0),
                          child: Text(
                            'Theme.of(context).textTheme\nMediaQuery.of(context).size等\n就是通过InheritedWidget实现的',
                            style: TextStyle(fontSize: 20.0),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
                width: double.infinity,
                height: double.infinity,
              ),
            ));
      }
    }
    

    从home页用路由跳到下一页使用InheritedWidget,如下:

    class NextPage extends StatefulWidget {
      @override
      _NextPageState createState() => _NextPageState();
    }
    
    class _NextPageState extends State<NextPage> {
      @override
      void initState() {
        super.initState();
        //直接在iniState使用InheritedWidget会报错
         /*ShareDataInheritedWidget shareDataInheritedWidget =
        ShareDataInheritedWidget.of(context);
        print(
            'next page initState shareDataInheritedWidget:  ${shareDataInheritedWidget?.showInfo() ?? 'null'}');*/
        Future.delayed(Duration.zero, () {
          final ShareDataInheritedWidget shareDataInheritedWidget =
              ShareDataInheritedWidget.of(context);
          print(
              'next page initState delayed shareDataInheritedWidget:  ${shareDataInheritedWidget?.showInfo() ?? 'null'}');
        });
      }
    
      @override
      Widget build(BuildContext context) {
        final TestInheritedWidget testInheritedWidget =
        TestInheritedWidget.of(context);
        print(
            'next page build TestInheritedWidget:  ${testInheritedWidget?.showInfo() ?? 'null'}');
    
        final ShareDataInheritedWidget shareDataInheritedWidget =
            ShareDataInheritedWidget.of(context);
        print(
            'next page build ShareDataInheritedWidget:  ${shareDataInheritedWidget?.showInfo() ?? 'null'}');
        return Scaffold(
          appBar: AppBar(
            title: Text('下一页'),
          ),
          body: Container(
            width: double.infinity,
            height: double.infinity,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                IncreaseWidget(),
                DataWidget(),
                ReduceWidget(),
              ],
            ),
          ),
        );
      }
    }
    

    操作如下:


    效果.gif

    示例说明:

    • 自定义InheritedWidget通过name参数来区分InheritedWidget的类别和相同类别InheritedWidget在widget中的位置;
    • <home页>界面展示的是子widget访问到的InheritedWidget是在widget树中离它最近的父InheritedWidget;
      +<下一页>界面展示的是路由页面之前的InheritedWidget不能共享,除非InheritedWidget为MaterialApp祖先;
    • 更多内容请结合代码注释和demo理解;

    详细信息请运行demo体验:InheritedWidget_Sample

    从示例中可以得出以下结论:

    • 子widget通过自定义InheritedWidget提供的of静态方法来访问自定义InheritedWidget的共享数据和方法;
    • 子widget和InheritedWidget必须在同一棵树上,并且InheritedWidget为子widget的祖先,这样子widget才能访问到InheritedWidget共享的数据和方法;
    • 当widget树中存在多个相同类别的InheritedWidget,子widget访问到的是离它最近的父InheritedWidget;
    • InheritedWidget状态变化时,只有当InheritedWidget的of静态方法是dependOnInheritedWidgetOfExactType实现,并且updateShouldNotify返回true,才会通知子widget的didChangeDependencies;
    • 用路由切换的页面,由于不在同一棵widget上,所以InheritedWidget在路由页面之间不起作用;
    • 只有作为MaterialApp祖先的InheritedWidget,才能在路由页面之间共享;

    InheritedWidget内部原理分析:

    InheritedWidget原理分析请移步:InheritedWidget内部实现原理浅析

    相关文章

      网友评论

        本文标题:数据传递/状态管理 一InheritedWidget使用示例

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