ome/img/nav3_on.png">
APP
系统平台
  • 建站知识
  • 联系我们
  • 咨询热线 :
    028-86922220

    疆括仕网站建设,新征程启航

    为企业提供网站建设、域名注册、服务器等服务

    flutter获取数据,flutter 传值

    flutter 状态管理 InheritedWidget 原理分析

    最近公司做技术分享写的文章的demo

    创新互联公司坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都网站设计、做网站、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的准格尔网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!

    Flutter中的InheritedWidget状态管理

    1.InheritedWidget是什么?

    InheritedWidget是Flutter中非常重要的一个功能型组件,它提供了一种数据在widget树中从上到下传递、共享的方式,比如我们在应用的根widget中通过InheritedWidget共享了一个数据,那么我们便可以在任意子widget中来获取该共享的数据!这个特性在一些需要在widget树中共享数据的场景中非常方便!如Flutter SDK中正是通过InheritedWidget来共享应用主题(Theme)和Locale (当前语言环境)信息的。

    InheritedWidget和React中的context功能类似,和逐级传递数据相比,它们能实现组件跨级传递数据。InheritedWidget的在widget树中数据传递方向是从上到下的,这和通知Notification的传递方向正好相反。

    2.源码分析

    InheritedWidget

    先来看下InheritedWidget的源码:

    abstract class InheritedWidget extends ProxyWidget {   const InheritedWidget({ Key key, Widget child }): super(key: key, child: child);  @override  InheritedElement createElement() =InheritedElement(this);  @protected  bool updateShouldNotify(covariant InheritedWidget oldWidget);}

    它继承自ProxyWidget:

    abstract class ProxyWidget extends Widget {   const ProxyWidget({ Key key, @required this.child }) : super(key: key);  final Widget child;}

    可以看出Widget内除了实现了createElement方法外没有其他操作了,它的实现关键一定就是InheritedElement了。

    InheritedElement 来看下InheritedElement源码

    class InheritedElement extends ProxyElement {   InheritedElement(InheritedWidget widget) : super(widget);  @override  InheritedWidget get widget = super.widget;  // 这个Set记录了所有依赖的Elementfinal MapElement, Object _dependents = HashMapElement, Object();

    //该方法会在Element mount和activate方法中调用,_inheritedWidgets为基类Element中的成员,用于提高Widget查找父节点中的InheritedWidget的效率,它使用HashMap缓存了该节点的父节点中所有相关的InheritedElement,因此查找的时间复杂度为o(1)   @override  void _updateInheritance() {final MapType, InheritedElement incomingWidgets = _parent?._inheritedWidgets;if (incomingWidgets != null)      _inheritedWidgets = HashMapType, InheritedElement.from(incomingWidgets);    else      _inheritedWidgets = HashMapType, InheritedElement();    _inheritedWidgets[widget.runtimeType] = this;  }

    //该方法在父类ProxyElement中调用,看名字就知道是通知依赖方该进行更新了,这里首先会调用重写的updateShouldNotify方法是否需要进行更新,然后遍历_dependents列表并调用didChangeDependencies方法,该方法内会调用mardNeedsBuild,于是在下一帧绘制流程中,对应的Widget就会进行rebuild,界面也就进行了更新   @override  void notifyClients(InheritedWidget oldWidget) {    assert(_debugCheckOwnerBuildTargetExists('notifyClients'));for (Element dependent in _dependents.keys) {      notifyDependent(oldWidget, dependent);    }  }

    其中_updateInheritance方法在基类Element中的实现如下:

    void _updateInheritance() {

    _inheritedWidgets = _parent?._inheritedWidgets;

    }

    总结来说就是Element在mount的过程中,如果不是InheritedElement,就简单的将缓存指向父节点的缓存,如果是InheritedElement,就创建一个缓存的副本,然后将自身添加到该副本中,这样做会有两个值得注意的点:

    InheritedElement的父节点们是无法查找到自己的,即InheritedWidget的数据只能由父节点向子节点传递,反之不能。

    如果某节点的父节点有不止一个同一类型的InheritedWidget,调用inheritFromWidgetOfExactType获取到的是离自身最近的该类型的InheritedWidget。

    看到这里似乎还有一个问题没有解决,依赖它的Widget是在何时被添加到_dependents这个列表中的呢?

    回忆一下从InheritedWidget中取数据的过程,对于InheritedWidget有一个通用的约定就是添加static的of方法,该方法中通过inheritFromWidgetOfExactType找到parent中对应类型的的InheritedWidget的实例并返回,与此同时,也将自己注册到了依赖列表中,该方法的实现位于Element类,实现如下:

    @overrideT dependOnInheritedWidgetOfExactType

    // 这里通过上述mount过程中建立的HashMap缓存找到对应类型的InheritedElement final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];if (ancestor != null) {    assert(ancestor is InheritedElement);return dependOnInheritedElement(ancestor, aspect: aspect);  }  _hadUnsatisfiedDependencies = true;  return null;}

    @overrideInheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {  assert(ancestor != null);

    // 这个列表记录了当前Element依赖的所有InheritedElement,用于在当前Element deactivate时,将自己从InheritedElement的_dependents列表中移除,避免不必要的更新操作   _dependencies ??= HashSetInheritedElement();  _dependencies.add(ancestor);  ancestor.updateDependencies(this, aspect);return ancestor.widget;}

    3.如何使用InheritedWidget

    1)、创建一个类继承自Inheritedwidget

    class InheritedContext extends InheritedWidget{  final InheritedTestModel inheritedTestModel;  InheritedContext({    Key key,    @required this.inheritedTestModel,    @required Widget child}): super(key: key, child: child);static InheritedContext  of (BuildContext context) {    return context.dependOnInheritedWidgetOfExactTypeInheritedContext();  }  @override  bool updateShouldNotify(InheritedContext oldWidget) {    return inheritedTestModel != oldWidget.inheritedTestModel;  }}

    2)、InheritedTestModel类为数据容器(这里定义了一个Listint数据源)

    class InheritedTestModel{ final List _list;  InheritedTestModel(this._list);  List getList(){    return _list;  }}

    class ArrayListData{  static List  _list ;static List  getListData (){     _list  = new List();     _list .add(1);     _list .add(2);     _list .add(3);     _list .add(4);return  _list ;  }}

    3)、定义一个Widget 使用 InheritedContext类的数据 InheritedTestModel 

    class ListDemo extends StatefulWidget{  @override  State createState() {    return new ListDemoState();  }}class ListDemoState extends StateListDemo{List _list;  InheritedTestModel _inheritedTestModel;  Timer _timer;  Duration oneSec = const Duration(seconds: 1);  @override  void initState() {    _list = ArrayListData. getListData ();    _inheritedTestModel = new InheritedTestModel(_list);    _timer = Timer.periodic(oneSec, (timer) {      _doTimer();    });  }  void _doTimer() {    for(int i = 0; i  _list.length; i++){      _list[i] = _list[i]+ 1;    }  setState(() {    _inheritedTestModel = new InheritedTestModel(_list);  });  }Widget _buildBody() {    return Container(child: ListDemo2(),    );  }  @override  Widget build(BuildContext context) {    return InheritedContext(inheritedTestModel: _inheritedTestModel,      child: Scaffold(appBar: AppBar(title: Text("ListDemo"),        actions: Widget[            IconButton(icon: Icon(Icons. add ),            )        ],),        body: _buildBody(),      ),    );  }  @override  void dispose() {    super.dispose();if (_timer != null) {      _timer.cancel();    }  }}

    4)、在ListDemo中通过Timer更新InheritedTestModel 中的数据,然后再下一个Widget中获取更新的数据作为展示

    class ListDemo2 extends StatefulWidget{  @override  State createState() {    return new ListDemoState2();  }}class ListDemoState2 extends StateListDemo2{InheritedTestModel _inheritedTestModel;  Widget _buildListItem(BuildContext context,int index) {    return  Container(height: 50,        width: 100,        alignment: Alignment. center ,        child: Text(_inheritedTestModel.getList()[index].toString()),    );  }Widget _buildBody() {    _inheritedTestModel = InheritedContext. of (context).inheritedTestModel;return Container(child: ListView.builder(itemBuilder:(context, index)=_buildListItem(context,index),itemCount: _inheritedTestModel.getList().length,),    );  }  @override  Widget build(BuildContext context) {    return  _buildBody();  }}

    这样就可以在父widget中更新数据,子View不需任何操作直接从数据容器InheritedTestModel 中获取到更新后的新数据

    这是一个数据共享的简单的例子,基本的流程,大致就是A去更新B的数据,A和B有一个共同的父类,实现数据的共享

    4.上面说了原理和基本的使用,但是在实际项目当中,我当然不建议这样来使用,Google 已经为我们封装好了功能更加强大的插件Provider,其内部原理就是基于InheritedWidget来实现的,我们理解了基本原理,可以更好的在项目中运用Provider

    Flutter之Image Widget(六)

    Image(图片组件)是显示图像的组件,一个显示图片的widget,支持图像格式:JPEG,PNG,GIF,动画GIF,WebP,动画WebP,BMP和WBM

    构造方法

    Image: 从ImageProvider获取数据

    Image.network: 加载网络图片。

    Image.asset: 加载本地图片文件。

    new Image.file: 加载本地图片文件(File文件)图片。

    new Image.memory: 加载Uint8List资源图片(byte数组)图片。

    常用属性

    方式一:CircleAvatar

    CircleAvatar可以实现圆形头像,也可以添加一个子Widget:

    在图片上加一个文本

    方式二:ClipOval

    ClipOval也可以实现圆角头像,而且通常是在只有头像时使用

    方式三:Container+BoxDecoration

    方法一:ClipRRect

    方法二:Container + BoxDecoration

    补充知识点

    Icon字体图标和图片图标的区别 ?

    Colors.red 是一个MaterialColor对象,为什么可以使用[](Colors.red[10])来设置颜色 ?

    MaterialColor 继承于ColorSwatch,ColorSwatch中有[] 运算符重载;

    Flutter中InheritedWidget的使用

    在Tree中从上往下高效传递数据的基类widget , 定义为:abstract class InheritedWidget extends ProxyWidget

    Flutter的响应式开发与React类似,数据都是自顶向下的。

    假设有祖先组点A,中间经过结点B, C,然后到结点D,D需要从A中获取数据f,那按照自顶向下数据流转,f需要依次传递给B及C,最后才到C。这样开发极为不灵活,成本也比较高。所有Flutter需要有跨结点(只能是祖先后代节点,不能跨兄弟节点)高效传递数据的方案。

    大体意思如下:

    InheritedWidget 是在树中高效向下传递信息的基类部件;

    调用[BuildContext.inheritFromWidgetOfExactType]方法可以从 BuildContext 中获取到最近的 InheritedWidget 类型的实例;

    在 InheritedWidget 类型的控件被引用,也就是调用过 inheritFromWidgetOfExactType 方法后,当 InheritedWidget 自身状态改变时,会导致引用了 InheritedWidget 类型的子控件重构(rebuild)。

    这里随便定义一个人 Person 类。

    创建一个类继承 InheritedWidget,并实现 updateShouldNotify 方法。

    之前说到调用[BuildContext.inheritFromWidgetOfExactType]方法可以从 BuildContext 中获取到最近的 InheritedWidget 类型的实例,所以此处定义一个静态的 of 方法,通过传入的 context 获取到最近的 InheriedDataWidget 实例。

    1.定义数据模型

    这里随便定义一个 Person 类。

    2.自定义 InheritedWidget 控件类

    创建一个类继承 InheritedWidget,并实现 updateShouldNotify 方法。

    之前说到调用[BuildContext.inheritFromWidgetOfExactType]方法可以从 BuildContext 中获取到最近的 InheritedWidget 类型的实例,所以此处定义一个静态的 of 方法,通过传入的 context 获取到最近的 InheriedDataWidget 实例。

    3.InheriedDataWidget 的使用

    InheriedDataWidget 使用起来也很简单,它本身也是一个控件,只要在任意一个页面的子控件调用其构造方法就行,这里我们定义一个形如的 Widget 树。

    WidgetA 是一个 StatefulWidget 类型的控件,可以调用 setState 刷新,如果是继承人 Stateless 类型的控件,那我们也可以通过 Stream 或者其他方式刷新数据,感兴趣的请看[什么是 Stream? Dart

    WidgetA1_1 类

    WidgetA1_2 类

    WidgetA1_3 类

    当我们点击 floatingActionButton 的时候,WidgetA1, WidgetA1_1, WidgetA1_2 的控件都会更新 Person 的信息,而且每点 floatingActionButton 一次, 当我们点击 floatingActionButton 的时候,WidgetA1, WidgetA1_1, WidgetA1_2 的控件都会更新 Person 的信息,而且每点 floatingActionButton 一次,都会输出:

    如果我们试图在和 WidgetA 的同一层级的兄弟节点去访问 InheriedDataWidget 的 Person 数据,是不行的,因为父节点中并没有插入 InheriedDataWidget。

    把 WidgetB 和 WidgetA 保持同一节点

    这也体现了 Inheried(遗传) 这一单词的特性,遗传只存在于父子。兄弟不存在遗传的关系。

    这种数据共享的方式在某些场景还是很有用的,就比如说全局主题,字体大小,字体颜色的变更,只要在 App 根层级共享出这些配置数据,然后在触发数据改变之后,所有引用到这些共享数据的地方都会刷新,这换主题,字体是不是就很轻松,事实上 Theme.of(context).primaryColor 之流就是这么干的。

    以上就是有关InheritedWidget的使用。

    自己也是从事Android开发5年有余了;整理了一些Android开发技术核心笔记和面经题纲,有关更多Android开发进阶技术资料、面经题纲、核心技术笔记; 想要进阶自己、拿高薪的同学请私信我回复“核心笔记”或“面试”领取!

    Flutter 之 文件操作(二十九)

    Dart的 IO 库包含了文件读写的相关类,它属于 Dart 语法标准的一部分,所以通过 Dart IO 库,无论是 Dart VM 下的脚本还是 Flutter,都是通过 Dart IO 库来操作文件的,不过和 Dart VM 相比,Flutter 有一个重要差异是文件系统路径不同,这是因为Dart VM 是运行在 PC 或服务器操作系统下,而 Flutter 是运行在移动操作系统中,他们的文件系统会有一些差异。

    Android 和 iOS 的应用存储目录不同, PathProvider 插件提供了一种平台透明的方式来访问设备文件系统上的常用位置。该类当前支持访问两个文件系统位置:

    File代表一个整体的文件,他有三个构造函数,分别是:

    文件读取本身有两种形式,一种是文本,一种是二进制。

    2.2.1 读取文本内容

    如果是文本文件,File提供了readAsString、readAsLines、readAsStringSync、readAsLinesSync方法,读取文本内容

    readAsString 一次性读取所有文本

    readAsLines 一行行的读取文本

    结果返回的是一个List,list中表示文件每行的内容

    readAsStringSync、readAsLinesSync同步读取文本

    2.2.2 读取二进制内容

    如果文件是二进制,那么可以使用readAsBytes或者同步的方法readAsBytesSync:

    dart中表示二进制有一个专门的类型叫做Uint8List,他实际上表示的是一个int的List。

    上面提到的读取方式,都是一次性读取整个文件,缺点就是如果文件太大的话,可能造成内存空间的压力。

    所以File为我们提供了另外一种读取文件的方法,流的形式来读取文件.

    示例

    dart提供了open和openSync两个方法来进行随机文件读写:

    写入和文件读取一样,可以一次性写入或者获得一个写入句柄,然后再写入。

    一次性写入的方法有四种,分别对应字符串和二进制

    句柄形式可以调用openWrite方法,返回一个IOSink对象,然后通过这个对象进行写入:

    默认情况下写入是会覆盖整个文件的,但是可以通过下面的方式来更改写入模式:

    虽然dart中所有的异常都是运行时异常,但是和java一样,要想手动处理文件读写中的异常,则可以使用try,catch:

    我们还是以计数器为例,实现在应用退出重启后可以恢复点击次数。 这里,我们使用文件来保存数据:

    1.引入PathProvider插件;在pubspec.yaml文件中添加如下声明:

    执行 flutter pub get

    2.实现如下

    参考:

    flutter provider的理解

    provider 是flutter 中的状态管理 开源库;

    存储的数据对象 必须extends ChangeNotifier;下层widget 通过 Provider.of(context) 函数 获取model对象 ,并且可以建立依赖关系;当数据对象发生变化时,依赖的widget 会重新build,像不像InheritedWidget Provider 没错 下层widget就是 封装了InheritedWidget

    主要 通过 Provider.ofT(context) 函数,来获取;

    推荐使用 Provider.of而不是 Consumer,因为 listen默认为true,也就是说 默认 依赖于 持有数据model的widget 对应的element;

    数据类 可继承的 ChangeNotifier,本身和privider框架 没有关系;

    ChangeNotifier 是 flutter框架 提供的工具类, 用来实现一对多的订阅通知功能。


    当前名称:flutter获取数据,flutter 传值
    网站地址:https://www.tyhkzb.com/article/dsgsosh.html
    在线咨询
    服务热线
    服务热线:028-86922220
    TOP