flutter数据共享系列——随记

2023-05-16

Provider

InheritedWidget 解决了数据共享问题。迎面也带来数据刷新导致的组件不必要更新问题。Provider基于InheritedWidget实现数据共享,数据更新,定向通知组件更新等。

接下来我们先从Provider使用开始切入,逐步分析Provider的实现,以及对组件的应用进行熟悉。

就拿官方文档开始:

新建一个模型Counter

class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

在合适的位置初始化

这里我们选择main方法:

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => Counter()),
      ],
      child: const MyApp(),
    ),
  );
}

使用并修改数据

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Example'),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: const <Widget>[
            Text('You have pushed the button this many times:'),

             Extracted as a separate widget for performance optimization.
             As a separate widget, it will rebuild independently from [MyHomePage].
            
             This is totally optional (and rarely needed).
             Similarly, we could also use [Consumer] or [Selector].
            Count(),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        key: const Key('increment_floatingActionButton'),

         Calls `context.read` instead of `context.watch` so that it does not rebuild
         when [Counter] changes.
        onPressed: () => context.read<Counter>().increment(),
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

class Count extends StatelessWidget {
  const Count({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(

         Calls `context.watch` to make [Count] rebuild when [Counter] changes.
        '${context.watch<Counter>().count}',
        key: const Key('counterState'),
        style: Theme.of(context).textTheme.headline4);
  }
}

使用注意事项

1. 共享数据定义为私有属性,提供get方法和update方法

这样可以有效的保护数据结构,统一修改入口和获取方法。

2. 适当隔离会进行rebuild的组件,常用的方式有三种:

  • 单独封装组件
  • 通过Consumer包裹
  • 通过Selector包裹,selector可以在某些值不变的情况下,防止rebuild。常用的地方是针对列表中个别数据进行修改。

3. 区分watchread的使用

watchread是Provider框架内部对BuildContext的扩展类。用户获取父级组件指定数据入口。区别在于是否添加了linsten,这个关系到是否需要实时刷新。
简单区分两种场景:

  • watch:界面监听数据,更新页面
  • read:响应业务交互,去操作更新数据

两种方法的源码也很简单,只是为了方便生成的拓展类:

 Exposes the [read] method.
extension ReadContext on BuildContext {
  T read<T>() {
    return Provider.of<T>(this, listen: false);
  }
}

 Exposes the [watch] method.
extension WatchContext on BuildContext {
  T watch<T>() {
    return Provider.of<T>(this);
  }
}

这里我们来看看Provider.of源码:

  static T of<T>(BuildContext context, {bool listen = true}) {
// 移出部分不必要代码

    final inheritedElement = _inheritedElementOf<T>(context);

    if (listen) {
      context.dependOnInheritedElement(inheritedElement);
    }
    return inheritedElement.value;
  }
  
    static _InheritedProviderScopeElement<T> _inheritedElementOf<T>(
    BuildContext context,
  ) {
    // 移出部分不必要代码
    _InheritedProviderScopeElement<T>? inheritedElement;

    if (context.widget is _InheritedProviderScope<T>) {
      context.visitAncestorElements((parent) {
        inheritedElement = parent.getElementForInheritedWidgetOfExactType<
            _InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>?;
        return false;
      });
    } else {
      inheritedElement = context.getElementForInheritedWidgetOfExactType<
          _InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>?;
    }

    if (inheritedElement == null) {
      throw ProviderNotFoundException(T, context.widget.runtimeType);
    }

    return inheritedElement!;
  }

原来加不加listen的区别在于获取数据的方式是getElementForInheritedWidgetOfExactTypeordependOnInheritedElementdependOnInheritedElement会新增一个注册,这个注册会调用在数据变更后,调用消费者的didChangeDependencies。稍微具体点分析可以查看我之前的文章——
记InheritedWidget使用思考

ChangeNotifier

实现Listenable接口的一个简单类,官方给的说明很简单:

A class that can be extended or mixed in that provides a change notification

可以扩展或混合的类,提供更改通知

实现了算法复杂度为O(1)去添加监听,O(N)去移除监听。对数据更新高效通知页面去刷新。provider的数据模型均得继承与它。

ChangeNotifierProvider

有了数据模型,接下来就开始创建我们的ChangeNotifier,就要用到ChangeNotifierProvider

先说一个错误的示例,错误的示例,错误的示例,在build中通过ChangeNotifierProvider.value去创建:

 ChangeNotifierProvider.value(
   value: new MyChangeNotifier(),
   child: ...
 )

这样会造成内存泄露和潜在的bug——参考。

当然,这个方法存在肯定是有他的意义的——如果你已经有了ChangeNotifier实例,就可以通过ChangeNotifierProvider.value进行构造。而不是选用create

正确的方法是通过creat方法去构建:

 ChangeNotifierProvider(
   create: (_) => new MyChangeNotifier(),
   child: ...
 )

不要传入变量去构建ChangeNotifier,这样的话,当变量更新,ChangeNotifier是不会去更新的。

int count;

ChangeNotifierProvider(
  create: (_) => new MyChangeNotifier(count),
  child: ...
)

如果你确实需要传入变量,请使用ChangeNotifierProxyProvider。具体ChangeNotifierProxyProvider使用,这里就不探讨了。

本人博客

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

flutter数据共享系列——随记 的相关文章

随机推荐

  • Spark常用API<Scala>

    概览 1 转换 2 动作1 Transformation 1 1一个RDD进行转换操作1 2 两个RDD的转换操作1 3对一个Pair RDD进行转化操作1 4对两个PairRDD进行转换操作2 Action 2 1对一个RDD进行行动操作
  • 关于特定网页打不开问题的解决

    如果有一些特定的网站打不开 排除被屏蔽的可能性的话 xff0c 试着把DNS设置成了自动获取ip试试看 我就这样子解决了打不开学校官网的问题
  • 渲染业务领域全景图

    最近图形学应用领域愈发广泛 xff0c 根据我的理解 xff0c 制作了一张渲染相关业务全景图 xff0c 希望对大家的职业规划有一定帮助
  • AI 入门怎么学?这份学习指南请收好!

    万事开头难 xff01 AI 入门对很多初学 AI 的同学来说是一大难题 搜集了一大堆入门资料 xff0c Python 数学 深度学习应有尽有 xff0c 但就是无从下手 xff0c 总是在第一章与放弃之间徘徊 那么 xff0c AI 应
  • FTP如何设置用户名密码

    1 新建FTP站点 xff0c 指定名称和物理路径 2 身份验证 选择 基本 xff0c 允许访问 选择 指定用户 xff0c 下面文本框中输入 本地用户和组 中现有的一个用户名即可 注意 xff1a 只能是 本地用户和组 中的用户 xff
  • Android布局 -- Navigation实现底部导航栏

    底部导航栏加页卡的切换 xff0c 很多App采用这种布局设计 xff0c 在以前的开发中 xff0c 需要自定义底部导航栏以及使用FragmentTransaction来管理Fragment的切换 xff0c 代码量较大 xff0c 而使
  • ViewModelProviders is deprecated

    原有的创建ViewModel的方法 xff1a viewModel 61 ViewModelProviders of this get ViewModel class 提示ViewModelProviders过时 改为 xff1a view
  • Android Fragment退出 返回上一个Fragment与直接退出

    例如应用底部有两个导航按钮A与B xff0c 刚进入的时候显示为第一个AFragment xff0c 点击B切换到BFragment 如果需求是在BFragment点击返回键回到AFragment xff0c 需要配置 app defaul
  • Android基础 -- 子线程可以修改UI吗?

    子线程可以修改UI吗 xff1f 为什么会产生这样的问题 xff0c 可能是因为在开发过程中遇到了 34 Only the original thread that created a view hierarchy can touch it
  • leetcode 417. 太平洋大西洋水流问题

    https leetcode cn com problems pacific atlantic water flow 思路是从海洋开始逆流 如果可以逆流到 就标记为1 然后检查两个海洋都可以逆流到的区域 DFS public List lt
  • Android模拟器检测常用方法

    在Android开发过程中 xff0c 防作弊一直是老生常谈的问题 xff0c 而模拟器的检测往往是防作弊中的重要一环 xff0c 接下来有关于模拟器的检测方法 xff0c 和大家进行一个简单的分享 1 传统的检测方法 传统的检测方法主要是
  • RecyclerView 隐藏部分分割线

    在项目中遇到复杂点的RecyclerView xff0c 可能会有隐藏部分分割线的需求 xff0c 例如item1和item3之间的分割线隐藏 xff0c item4和item5之间的分割线隐藏等 在看了文档里的ItemDecoration
  • 浅谈去中心化应用

    1 中心化应用 现在我们所使用的应用基本上都是中心化的应用 xff0c 什么是中心化应用呢 xff0c 举个栗子 xff0c 我们在天猫买东西的时候 xff0c 需要先付款给支付宝 xff0c 然后卖家发货 xff0c 我们确认收货之后 x
  • EGL综述

    参考 xff1a https www khronos org registry EGL specs eglspec 1 5 pdf 什么是EGL EGL是支持多平台 多操作系统的 xff0c 比如安卓 Unix Windows等 为了扩展性
  • Java二分搜索树及其添加删除遍历

    对于树这种结构 xff0c 相信大家一定耳熟能详 xff0c 二叉树 二分搜索树 AVL树 红黑树 线段树 Trie等等 xff0c 但是对于树的应用以及编写一棵解决特定问题的树 xff0c 不少同学都会觉得不是一件简单的事情 xff0c
  • Android自定RadioGroup实现点击切换效果

    一 xff1a java文件 public class SegmentedGroup extends RadioGroup private int mMarginDp private Resources resources private
  • opencv--将本地摄像头数据转换成ip摄像头数据流,并在客户端获取该流进行显示

    项目介绍 xff1a 在本项目中 xff0c 实现从本地摄像头获取数据帧 xff0c 然后将其转换成ip摄像头数据流并在客户端通过opencv代码实时获取该图像数据进行显示 xff1a 当然也能在浏览器通过输入地址进行视频的访问 方式一 x
  • 8. &amp;和&amp;&amp;的区别?

    答 xff1a amp 运算符有两种用法 xff1a 1 按位与 xff1b 2 逻辑与 amp amp 运算符是短路与运算 逻辑与跟短路与的差别是非常巨大的 xff0c 虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是t
  • 记InheritedWidget使用思考

    InheritedWidget 是项目中必不可少的组件 xff0c 用户数据共享 老生常谈的Provider框架也是基于InheritedWidget实现的 简介 InheritedWidget组件是功能性组局 xff0c 实现了由上向下共
  • flutter数据共享系列——随记

    Provider InheritedWidget 解决了数据共享问题 迎面也带来数据刷新导致的组件不必要更新问题 Provider基于InheritedWidget实现数据共享 xff0c 数据更新 xff0c 定向通知组件更新等 接下来我