flutter inheritedwidget(共享数据)

一、Flutter Sdk正是通过inheritedWidget实现在widget树中共享数据的(Theme和Locale当前语言环境)

1.inheritedWidget是Flutter中一个重要的功能型组件,它提供了一种数据在widget树中从上到下传递、共享的方式,比如我们在应用的根widget中通过InheritedWidget共享了一个数据,那么我们便可以在任意子widget中来获取该共享的数据
2.didChangeDependencies

在statefulwidget 的State中有一个didChangeDependencies方法,它指的正是组件是否依赖了父widget的inheritedwidget共享数据,如果inheritedwidget的数据发生变化,那么didChangeDependencies方法就会被调用,进而更新组件

示例:通过继承Inheritedwidget实现一个点击自增数据以清楚其原理

  a:InheritedWidget 的updateShouldNotify方法,此方法决定是否调用State的didChangeDependencies方法,如果返回true则调用

  

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的数据,那么TestWidget的State 的didChangeDependencies是不会被调用的。

重点结论:InheritedWidget正是Flutter框架在widget树中实现共享数据,子widget依赖发生变化时让子组件实现更新的

3.应该在didChangeDependencies()中做什么?

一般来说,子widget很少会重写此方法,因为在依赖改变后framework也都会调用build()方法。但是,如果你需要在依赖改变后执行一些昂贵的操作,比如网络请求,这时最好的方式就是在此方法中执行,这样可以避免每次build()都执行这些昂贵操作。

  

4.dependOnInheritedWidgetOfExactType和getElementForInheritedWidgetOfExactType的区别
//定义一个便捷方法,方便子树中的widget获取共享数据
static ShareDataWidget of(BuildContext context) {
  //return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
  return context.getElementForInheritedWidgetOfExactType<ShareDataWidget>().widget;
}

  

@override
InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
  assert(_debugCheckStateIsActiveForAncestorLookup());
  final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
  return ancestor;
}
@override
InheritedWidget dependOnInheritedWidgetOfExactType({ Object aspect }) {
  assert(_debugCheckStateIsActiveForAncestorLookup());
  final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
  //多出的部分
  if (ancestor != null) {
    assert(ancestor is InheritedElement);
    return dependOnInheritedElement(ancestor, aspect: aspect) as T;
  }
  _hadUnsatisfiedDependencies = true;
  return null;
}

  

  @override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
    assert(ancestor != null);
    _dependencies ??= HashSet<InheritedElement>();
    _dependencies.add(ancestor);
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }

  

查看代码可以发现dependOn方法比getElementFor多了一个注册到ancertor和dependOnInheritedElement,而dependOnInheritedElement方法中执行了ancestor.updateDependencies方法,正是这个方法让State的didChangeDependen方法被调用

 

二、Provide正是通过InheritedWidget实现的数据共享

这里可参考大神的跨组件状态共享加深理解:https://book.flutterchina.club/chapter7/provider.html

Flutter的Provide重要的有以下几个点:

1.ChangeNotifierProvider 变更通知提供者,也是订阅者 Comsumer基本都会在订阅者的子widget,因为订阅者需要依赖共享数据

2.Comsumer 消费者,就是消费共享数据的

3.ChangeNotifier 变更通知者,就是当依赖的model数据变化时,通知到各个监听者 这里往往需要共享的数据,某某Model 会extends继承ChangeNotifier

这里给个baseWidget 使用了Provide的示例,代码如下:

import 'package:business/base/base_viewmodel.dart';
import 'package:flutter/material.dart';

import 'package:provider/provider.dart';

class BaseWidget<T extends ChangeNotifier> extends StatefulWidget {
  final Widget Function(BuildContext context, T model, Widget child) builder;
  final T model;
  final Widget child;
  final Function(T) onModelReady;

  BaseWidget({
    Key key,
    this.builder,
    this.model,
    this.child,
    this.onModelReady, loginService,
  }) : super(key: key);

  _BaseWidgetState<T> createState() => _BaseWidgetState<T>();
}

class _BaseWidgetState<T extends ChangeNotifier> extends State<BaseWidget<T>> {
  T model;

  @override
  void initState() {
    model = widget.model;

    if (widget.onModelReady != null) {
      widget.onModelReady(model);
    }

    super.initState();
  }

  @override
  void dispose() {
    if(model != null){
      if(model is BaseViewModel){
        BaseViewModel m = model as BaseViewModel;
        m.isDisposed = true;
      }
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<T>(
      create: (context) => model,
      child: Consumer<T>(
        builder: widget.builder,
        child: widget.child,
      ),
    );
  }
}

 注意:这里有一点就是onModelReady的model参数就是BaseWidget的model域。同时builder 的构造,(context,model,child){} 的model也是和onModelReady是同一个model数据。因为下面这个

Widget build(BuildContext context) {
    return ChangeNotifierProvider<T>(
      create: (context) => model,
      child: Consumer<T>(
        builder: widget.builder,
        child: widget.child,
      ),
    );
  }

 

 

posted on 2020-10-19 17:30  我是你爷爷的爷爷  阅读(416)  评论(0编辑  收藏  举报

导航