美文网首页
InheritedWidget

InheritedWidget

作者: 肖散 | 来源:发表于2020-04-09 13:36 被阅读0次

[TOC]

/// Base class for widgets that efficiently propagate information down the tree.

主要作用就是提供了一个传递数据的一个工具。

实现原理

1. 上层中维护一个map

v1.16.3

  Map<Type, InheritedElement> _inheritedWidgets;

2. map中保存数据(相当于保存一个单例),并传递给子view。

实现是在mount过程的时候实现 v1.16.3

  @mustCallSuper
  void mount(Element parent, dynamic newSlot) {
    ...
    _updateInheritance();
    ...
  }

普通Elementv1.16.3
只实现数据传递。

  void _updateInheritance() {
    assert(_active);
    _inheritedWidgets = _parent?._inheritedWidgets;
  }

InheritedElement实现v1.16.3
向_inheritedWidgets中注入自己的类型单例。

  @override
  void _updateInheritance() {
    assert(_active);
    final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
    if (incomingWidgets != null)
      _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
    elsed
      _inheritedWidgets = HashMap<Type, InheritedElement>();
    _inheritedWidgets[widget.runtimeType] = this;
  }

从这里可以看出来,_inheritedWidgets传递数据如果出现相同的类型,这个地方会被覆盖,所以,一个类型的数据只能出现一次

3. 子view取出map中的数据然后使用

通过Element,也就是buildcontex的dependOnInheritedWidgetOfExactType方法获取
v1.16.3

aspect:用来现实部分刷新用的。目前没找到例子

  ///拿到_inheritedWidgets中存的数据
  @override
  T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({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;
  }
  
  ///去除数据中存放的widget。另外还进行了通知。
    @override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
    assert(ancestor != null);
    _dependencies ??= HashSet<InheritedElement>();
    _dependencies.add(ancestor);
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }

注意事项

1. 一个类型在一个view树上只能有一个。不然会被覆盖。因为上面源码是直接覆盖。

2.StatefulWidget中使用的时候,获取InheritedWidget需要在created之后,也就是initstate()之后。可以放到didChangeDependencies中处理。

原因是在StatefulElement中重写了dependOnInheritedElement方法。
判断了状态不能为 _StateLifecycle.created 以及 _StateLifecycle.defunct(State.dispose之后)。
v1.16.3

  @override
  InheritedWidget dependOnInheritedElement(Element ancestor, { Object aspect }) {
    assert(ancestor != null);
    assert(() {
      final Type targetType = ancestor.widget.runtimeType;
      if (state._debugLifecycleState == _StateLifecycle.created) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
        ....
        ]);
      }
      if (state._debugLifecycleState == _StateLifecycle.defunct) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ....
        ]);
      }
      return true;
    }());
    return super.dependOnInheritedElement(ancestor as InheritedElement, aspect: aspect);
  }

v1.16.3

  @override
  void _firstBuild() {
    assert(_state._debugLifecycleState == _StateLifecycle.created);
    try {
      _debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
      final dynamic debugCheckForReturnedFuture = _state.initState() as dynamic;
      ....
    } finally {
      _debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
    }
    assert(() {
      _state._debugLifecycleState = _StateLifecycle.initialized;
      return true;
    }());
    _state.didChangeDependencies();
    assert(() {
      _state._debugLifecycleState = _StateLifecycle.ready;
      return true;
    }());
    super._firstBuild();
  }

可以看到是在完成了initstate之后,状态才更改为initialized,然后再调用了didChangeDependencies。这里有个时序的问题。

使用

参考

问题:该demo中的用法,每次都重新build了整个view树。这样使用感觉没有达到应有的效果(数据共享,数据更新)
在该demo的基础上修改

1 修改InheritedTestModel,让他真的成为一个数据模块。同时承担数据共享与通知更新的业务。

class InheritedTestModel {
  int count;
  Function() change;

  InheritedTestModel(this.count);

  void doChange(int c) {
    count += c;
    if (change != null) {
      change();
    }
  }
}

2 修改用于呈现数据更新的TestWidgetB。这个过程中需要更新ui的就他自己。

class TestWidgetB extends StatefulWidget {
  @override
  _TestWidgetBState createState() => _TestWidgetBState();
}

class _TestWidgetBState extends State<TestWidgetB> {
  @override
  Widget build(BuildContext context) {
    final inheritedContext = InheritedContext.of(context);
    final inheritedTestModel = inheritedContext.inheritedTestModel;
    print('TestWidgetB 中count的值:  ${inheritedTestModel.count}');
    return Padding(
      padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
      child: new Text(
        '当前count:${inheritedTestModel.count}',
        style: new TextStyle(fontSize: 20.0),
      ),
    );
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    InheritedContext.of(context).inheritedTestModel.change = _change;
  }

  void _change() {
    setState(() {});
  }
}

3 _InheritedWidgetTestContainerState取消更新ui

  _incrementCount() {
    inheritedTestModel.doChange(1);
  }

  _reduceCount() {
    inheritedTestModel.doChange(-1);
  }

dmeo 代码

import 'package:flutter/material.dart';
import 'package:flutter_module/carplaylib.dart';

class InheritedTestModel {
  int count;
  Function() change;

  InheritedTestModel(this.count);

  void doChange(int c) {
    count += c;
    if (change != null) {
      change();
    }
  }
}

class InheritedContext extends InheritedWidget {
  //数据
  final InheritedTestModel inheritedTestModel;

  //点击+号的方法
  final Function() increment;

  //点击-号的方法
  final Function() reduce;

  InheritedContext({
    Key key,
    @required this.inheritedTestModel,
    @required this.increment,
    @required this.reduce,
    @required Widget child,
  }) : super(key: key, child: child);

  static InheritedContext of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<InheritedContext>();
  }

  //是否重建widget就取决于数据是否相同
  @override
  bool updateShouldNotify(InheritedContext oldWidget) {
    return inheritedTestModel != oldWidget.inheritedTestModel;
  }
}

class TestWidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final inheritedContext = InheritedContext.of(context);

    final inheritedTestModel = inheritedContext.inheritedTestModel;

    print('TestWidgetA 中count的值:  ${inheritedTestModel.count}');
    return Padding(
      padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
      child: new RaisedButton(
          textColor: Colors.black,
          child: new Text('+'),
          onPressed: inheritedContext.increment),
    );
  }
}

class TestWidgetB extends StatefulWidget {
  @override
  _TestWidgetBState createState() => _TestWidgetBState();
}

class _TestWidgetBState extends State<TestWidgetB> {
  @override
  Widget build(BuildContext context) {
    final inheritedContext = InheritedContext.of(context);

    final inheritedTestModel = inheritedContext.inheritedTestModel;

    print('TestWidgetB 中count的值:  ${inheritedTestModel.count}');

    return Padding(
      padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
      child: new Text(
        '当前count:${inheritedTestModel.count}',
        style: new TextStyle(fontSize: 20.0),
      ),
    );
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    InheritedContext.of(context).inheritedTestModel.change = _change;
  }

  void _change() {
    setState(() {});
  }
}

class TestWidgetC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final inheritedContext = InheritedContext.of(context);
    final inheritedTestModel = inheritedContext.inheritedTestModel;
    print('TestWidgetC 中count的值:  ${inheritedTestModel.count}');
    return Padding(
      padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
      child: new RaisedButton(
          textColor: Colors.black,
          child: new Text('-'),
          onPressed: inheritedContext.reduce),
    );
  }
}

class InheritedWidgetTestContainer extends StatelessWidget {

  final InheritedTestModel inheritedTestModel = InheritedTestModel(0);

  _incrementCount() {
    inheritedTestModel.doChange(1);
  }

  _reduceCount() {
    inheritedTestModel.doChange(-1);
  }

  @override
  Widget build(BuildContext context) {
    return InheritedContext(
        inheritedTestModel: inheritedTestModel,
        increment: _incrementCount,
        reduce: _reduceCount,
        child: new Scaffold(
          appBar: new AppBar(
            title: new Text('InheritedWidgetTest'),
          ),
          body: new Column(
            children: <Widget>[
              new Padding(
                padding:
                    const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
                child: new Text(
                  '我们常使用的\nTheme.of(context).textTheme\nMediaQuery.of(context).size等\n就是通过InheritedWidget实现的',
                  style: new TextStyle(fontSize: 20.0),
                ),
              ),
              TestWidgetA(),
              TestWidgetB(),
              TestWidgetC(),
            ],
          ),
        ));
  }
}


相关文章

网友评论

      本文标题:InheritedWidget

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