[TOC]
/// Base class for widgets that efficiently propagate information down the tree.
主要作用就是提供了一个传递数据的一个工具。
实现原理
1. 上层中维护一个map
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);
}
@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(),
],
),
));
}
}
网友评论