美文网首页
Flutter中的数据共享InheritedWidget探讨

Flutter中的数据共享InheritedWidget探讨

作者: AndyLiYL | 来源:发表于2022-10-19 11:40 被阅读0次

    说到InheritedWidget,想必大家也都知道这个是Flutter里面非常重要的组件,很多功能场景都是基于InheritedWidget处理实现的,如:Locale、Theme、登录信息以及provider状态管理也是基于InheritedWidget实现

    下面先用个小例子,初步学习一下InheritedWidget在不同的widget中通过dependOnInheritedWidgetOfExactType获得共同的数据:

    先创建个model

    class CNUserModel {
      final int count;
      const CNUserModel(this.count);
    }
    
    

    再创建一个MyIneritedWidget:

    class MyInheritedTest extends InheritedWidget {
      final CNUserModel? countModel;
      final Function()? add;
    
      MyInheritedTest({Key? key, this.countModel, this.add, Widget? child})
          : super(key: key, child: child!);
    
      //静态方法 方便调用
      // static MyInheritedTest? of(BuildContext context) {
      //   return context.dependOnInheritedWidgetOfExactType(aspect: MyInheritedTest());
      // }
    
      //静态方法 方便调用
      static MyInheritedTest? of(BuildContext context) {
        return context.dependOnInheritedWidgetOfExactType(aspect: MyInheritedTest);
      }
    
      //是否重建widget就取决于数据是否相同
      @override
      bool updateShouldNotify(covariant MyInheritedTest oldWidget) {
        // TODO: implement updateShouldNotify
        return countModel != oldWidget.countModel;
      }
    }
    

    这里代码有个地方有个问题,我当时敲的时候,没很注意,就是:context.dependOnInheritedWidgetOfExactType(aspect: MyInheritedTest()),结果就是理所当然的报错:

    When the exception was thrown, this was the stack: 
    #0      new MyInheritedTest (package:getx_demo/first_page.dart:133:37)
    #1      MyInheritedTest.of (package:getx_demo/first_page.dart:137:17)
    #2      A_View.build (package:getx_demo/first_page.dart:58:43)
    #3      StatelessElement.build (package:flutter/src/widgets/framework.dart:4876:49)
    #4      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4806:15)
    #5      Element.rebuild (package:flutter/src/widgets/framework.dart:4529:5)
    #6      BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2659:19)
    #7      WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:891:21)
    #8      RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:370:5)
    #9      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1146:15)
    #10     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1083:9)
    #11     SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:864:7)
    

    所以,敲的时候要注意哈!!!

    创建两个View使用处理和接收一下数据,看是否能共享成:

    class A_View extends StatelessWidget {
      const A_View({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        final inheritedTest = MyInheritedTest.of(context);
        final testModel = inheritedTest?.countModel;
        debugPrint('A_View中的值:  ${testModel?.count}');
    
        return Padding(
          padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
          child: GestureDetector(
            onTap: inheritedTest?.add,
            child: Container(
                height: 40,
                width: 100,
                alignment: Alignment.center,
                decoration: BoxDecoration(
                    color: Colors.blue, borderRadius: BorderRadius.circular(5)),
                child: const Text(
                  'add method',
                  style: TextStyle(color: Colors.white),
                )),
          ),
        );
      }
    }
    
    class B_View extends StatelessWidget {
      const B_View({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        final testModel = MyInheritedTest.of(context)?.countModel;
        print('B_View中的值:  ${testModel?.count}');
        return Padding(
          padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
          child: Text(
            '当前count:${testModel?.count}',
            style: const TextStyle(fontSize: 20.0),
          ),
        );
      }
    }
    

    使用刚刚创建的MyInheritedWidget :

    class FirstPage extends StatefulWidget {
      const FirstPage({Key? key}) : super(key: key);
    
      @override
      _FirstPageState createState() => _FirstPageState();
    }
    
    class _FirstPageState extends State<FirstPage> {
      CNUserModel? testModel;
    
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
        _initData();
      }
    
      _initData() {
        testModel = const CNUserModel(0);
      }
    
      _add() {
        setState(() {
          testModel = CNUserModel(testModel!.count + 1);
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return MyInheritedTest(
          countModel: testModel,
          add: _add,
          child: Scaffold(
            appBar: AppBar(
              title: const Text("InheritedWidgetTestDemo"),
            ),
            body: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: const [A_View(), B_View()],
              ),
            ),
          ),
        );
      }
    }
    
    截屏2022-10-19 10.43.38.png

    通过控制台日志可以看出,数据操作和共享是成功的了

    简单demo到这就完成了,那么demo终究是demo,怎么实际应用到项目中去呢,接下来咱们就稍微修改修改,使用Provider二者结合实现保存和共享用户信息操作:

    model修改下,要保存用户信息嘛:

    class CNUserModel {
      String? name;
      int? sex;
    
      CNUserModel({this.name, this.sex});
    }
    

    新增一个继承ChangeNotifier的CNUserTool用来接收更新数据:

    class CNUserTool extends ChangeNotifier {
      CNUserTool(this._userInfo);
    
      CNUserModel _userInfo;
    
      CNUserModel get userInfo => _userInfo;
    
      void update(CNUserModel userInfo) {
        _userInfo = userInfo;
        notifyListeners();
      }
    }
    

    MyInheritedTest稍作修改:

    class MyInheritedTest extends InheritedWidget {
      // final CNUserModel? userModel;
      ValueNotifier<CNUserModel>? _valueNotifier;
      ValueNotifier<CNUserModel> get valueNotifier => _valueNotifier!;
    
      MyInheritedTest({Key? key, CNUserModel? userModel, Widget? child})
          : super(key: key, child: child!) {
        _valueNotifier = ValueNotifier<CNUserModel>(userModel!);
      }
    
      void updateData(CNUserModel info) {
        _valueNotifier?.value = info;
      }
    
      //静态方法 方便调用
      static MyInheritedTest? of(BuildContext context) {
        return context.dependOnInheritedWidgetOfExactType<MyInheritedTest>();
      }
    
      @override
      bool updateShouldNotify(covariant MyInheritedTest oldWidget) {
        return false;
      }
    }
    
    

    B_View也稍作修改:

    class B_View extends StatefulWidget {
      const B_View({Key? key}) : super(key: key);
    
      @override
      State<B_View> createState() => _B_ViewState();
    }
    
    class _B_ViewState extends State<B_View> {
      @override
      void initState() {
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
    
        CNUserTool userTool = Provider.of<CNUserTool>(context);
    
        return Text('name: ${userTool.userInfo.name}\n'
            '性别: ${userTool.userInfo.sex! % 2 == 0 ? "男" : "女"}');
    
      }
    }
    

    使用:

        return Scaffold(
          appBar: AppBar(title: const Text('InheritedWidgetDemoPage')),
          body: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
    
              B_View(),
              const SizedBox(
                height: 20,
              ),
              Consumer<CNUserTool>(
                builder: (BuildContext context, value, Widget? child) {
                  return ElevatedButton(
                      onPressed: () {
                        value.update(CNUserModel(name: 'lyl', sex: _clickCount++));
                      },
                      child: const Text("修改信息"));
                },
              ),
            ],
          ),
        );
    
    

    因为是全局共享用户信息嘛,所以入口函数就要处理对应操作:

    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
    
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        //初始数据值,一般是读取本地存储,这里直接给默认,具体场景具体处理
        CNUserModel userModel = CNUserModel(name: "lyl", sex: 2);
        return MultiProvider(
          providers: [
            // ChangeNotifierProvider.value(value: CNUserTool(userModel)),
            ChangeNotifierProvider.value(value: CNUserTool(userModel)),
          ],
          child: GetMaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: const HomePage(),
            routes: const {},
          ),
        );
      }
    }
    
    
    20221019-112802.gif

    通过gif可以在HomePage里面也读取了对应用户信息数据,可见是正常获取以及共享的。至此,实际应用场景也完成了。

    生活百般滋味,人生需要笑对~😄

    相关文章

      网友评论

          本文标题:Flutter中的数据共享InheritedWidget探讨

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