InheritedWidget 源码分析和应用

目前,开发的flutter项目中,状态管理库使用是ProviderProvider 基于 InheritedWidget 组件封装,想要减少日常开发采坑,就不得不去了解 InheritedWidget 组件的工作原理。由于要从源码角度分析 InheritedWidget 组件的工作原理,在阅读本文前,最好对 flutter 的知识有一定了解,这样才能更好的了解,本文所要表达的意思。

  • 熟悉 flutter 基本使用。
  • 了解 provider 的框架。
  • 了解 WidgetElement 之间关系。
  • 了解 Elementflutter 渲染时方法的调用。

一、 InheritedWidget

本文中用到的生产环境

生产环境

1.1 InheritedWidget 简述

/// 有效地沿树向下传播信息的 Widget 的基类,子 Widget 要想获取最近特定类型的 InheritedWidget实例,请使用 
/// [BuildContext.dependOnInheritedWidgetOfExactType]。子 Widget 以这种方式引用时,当 InheritedWidget 改变状态
/// 时,会重新构建依赖的子 Widget。
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);

复制代码

InheritedWidget 代码很简单,主要有两个方法:

  • createElement 生成 InheritedElement 对象。
  • updateShouldNotify 用于控制当前 InheritedWidget 发生变化, 所依赖的 Widget 是否需要重建。
1.1.1 BuildContext

InheritedWidget 描述可得知,如果子 Widget 需要获取 InheritedWidget 对象,可以通过 BuildContext.dependOnInheritedWidgetOfExactType 获取。看下 BuildContext 类的 dependOnInheritedWidgetOfExactType

abstract class BuildContext {
  /// 获取给定类型“T”的最近 widget,它必须是具体 [InheritedWidget] 子类的类型,并将此构建上下文注册到该 widget,以便当该 widget 更改时(或引入该类型的新 widget, 或 widget 消失),此构建上下文将被重建,以便它可以从该 widget 获取新值。
   T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object aspect });
  
  /// 获取与给定类型“T”的最近 widget 对应的 element,该 element 必须是具体 [InheritedWidget] 子类的类型。 如果找不到这样的 element,则返回 null。     
  /// 调用这个方法是 O(1) 一个小的常数因子。 此方法不会像 [dependOnInheritedWidgetOfExactType] 那样与目标建立关
  /// 系。 不应从 [State.dispose] 调用此方法,因为此时 element 树不再稳定。 要从该方法引用祖先,请通过在 
  /// [State.didChangeDependencies] 中调用 [dependOnInheritedWidgetOfExactType] 来保存对祖先的引用。 使用 
  /// [State.deactivate] 中的此方法是安全的,每当 widget 从树中移除时都会调用该方法。
  InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();
}
复制代码

1.2 InheritedWidget 应用

接下来,我们写一个基于 InheritedWidget 组件实现的 计数器

1.2.1 CountScope
class CountScope extends InheritedWidget {
  const CountScope({this.count, Widget child}) : super(child: child);

  final int count;

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

  @override
  bool updateShouldNotify(CountScope oldWidget) {
    return oldWidget.count != count;
  }
}
复制代码

CountScope 继承 InheritedWidget

  1. of ,子 Widget 获取 CountScope 对象。[见1.1小节]
  2. updateShouldNotifyoldWidget.count != count 刷新依赖的 widget[见1.1小节]
1.2.2 CountWidget
class CountWidget extends StatefulWidget {
  const CountWidget({Key key}) : super(key: key);

  @override
  _CountWidgetState createState() => _CountWidgetState();
}

class _CountWidgetState extends State<CountWidget> {
  @override
  Widget build(BuildContext context) {
    print('_CountWidgetState build');
    final int count = CountScope.watch(context).count;
    return Container(child: Text('$count', style: Theme.of(context).textTheme.headline4));
  }
}
复制代码

CountWidget 调用 CountScope.of(context).count 显示计数结果。

1.2.3 MyHomePage
class MyHomePage extends StatefulWidget {
  const MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    print('_MyHomePageState build');
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('You have pushed the button this many times:'),
            CountScope(count: _counter, child: CountWidget()),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}
复制代码

到这里计数器功能差不多就完成了,当我们点击浮动 + 按钮时,计数器的数值会累加,最后可以看到效果就是这样。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享