知其然,也要知其所以然。最近的搬砖工作中,开发ui页面都是运用flutter,android原生只沦为了后台逻辑处理的后台。在搬砖过程中,往往只要知道怎么用,便能搭起小房子,而要建的恢宏又大气,还是少不了对于原理的学习。 在接触
flutter
中,Widget
是咱们接触最多的类。咱们对于各种界面的建立用的便是各式各样的Widget
,有StatefullWidget
和StatelessWidget
。印象中Widget
便是最为ui建立而存在的,然而在flutter的中,还有一种widget
不负责ui控制,但却十分重要,那便是InheritedWidget
。 在咱们常用的第三方数据同享库中,都有InheritedWidget
的影子,比如下面两个。
- flutter_bloc
- Provider
还有开发中用到的许多数据,比如
Theme.of(context)
,DefaultTextStyle.of(context)
等,都是在父节点配置,子widget全局获取。这都离不开InheritedWidget
的特性。 既然如此重要,今日就好好学习下InheritedWidget
的大致原理吧。
一,InheritedWidget 介绍
InheritedWidget
是 Flutter 中非常重要的一个功能型组件,它供给了一种在 widget 树中从上到下同享数据的办法,比如咱们在运用的根 widget 中经过InheritedWidget
同享了一个数据,那么咱们便能够在任意子widget 中来获取该同享的数据!这个特性在一些需要在整个 widget 树中同享数据的场景中非常便利!如Flutter SDK中正是经过 InheritedWidget 来同享运用主题(Theme
)和 Locale (当前语言环境)信息的。
InheritedWidget
和 React 中的 context 功能相似,和逐级传递数据比较,它们能完成组件跨级传递数据。InheritedWidget
的在 widget 树中数据传递方向是从上到下的,这和告诉Notification
的传递方向正好相反。
二,组件树中的数据同享用法(运用 ValueNotrifier 和 InhertitedWidget 完成简略的状况办理)
- 咱们首先创立一个MyProvider 承继自InheritedWidget,以寄存状况数据,该状况承继自ChangeNotifier,即代码中的
model
变量。
import 'package:flutter/widgets.dart';
///供给状况存储
class MyProvider<T extends ChangeNotifier> extends InheritedWidget {
T model;
MyProvider({Key? key,required Widget child,required this.model}) : super(key: key,child:child);
@override
Widget build(BuildContext context) {
return this.child;
}
static MyProvider<T>? of<T extends ChangeNotifier>(BuildContext context){
return context.dependOnInheritedWidgetOfExactType<MyProvider<T>>();
}
@override
bool updateShouldNotify(covariant MyProvider oldWidget) {
return false;
}
}
- 接下来创立一个Customer,用来完成动态改写,和部分改写,原理主要是运用changeNotifier监听器更新数据更新,注册对应监听器改写Customer的子布局。
///供给状况消费,在状况ValueNotifier更新的时分,主动改写Customer组件
class Customer<T extends ChangeNotifier> extends StatefulWidget{
const Customer({required this.builder,});
final ProviderBuilder<T> builder;
@override
State<Customer<T>> createState() => _CustomerState<T>();
}
class _CustomerState<T extends ChangeNotifier> extends State<Customer<T>> {
late MyProvider? provider;
@override
void didChangeDependencies() {
super.didChangeDependencies();
provider=MyProvider.of<T>(context);
if(provider==null) throw "请在先人节点供给MyProvider<T extends ChangeNotifier>";
provider!.model.addListener(() {
setState(()=>{});
});
}
@override
Widget build(BuildContext context) {
return widget.builder(context,MyProvider.of<T>(context)!.model);
}
}
typedef ProviderBuilder<T extends ChangeNotifier> = Widget Function(BuildContext context,T value);
- 接着创立一个展现用的widget,包括显现数据的子widget 和 点击改写数据的 按钮,代码如下:
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:free/widget/my_provider.dart';
import 'package:provider/provider.dart';
class TestWidget extends StatefulWidget {
const TestWidget({Key? key}) : super(key: key);
@override
State<TestWidget> createState() => _TestWidgetState();
}
class TestModel extends ChangeNotifier{
var num=0;
TestModel() : super();
}
class _TestWidgetState extends State<TestWidget> {
var model=TestModel();
@override
Widget build(BuildContext context) {
return Scaffold(
body: MyProvider<TestModel>(
model:model,
child: Builder(
builder: (context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
MyTextWidget(),
MaterialButton(
color: Colors.blueAccent,
elevation: 10,
onPressed: () {
model.num++;
model.notifyListeners();
},
child: Text("点击加一")),
],
),
);
}
),
),
);
}
}
class MyTextWidget extends StatelessWidget {
const MyTextWidget ({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Customer<TestModel>(builder:(c,value) => Text("${value.num}"));
}
}
至此,运用InheritedWidget
和ChangeNotifier
完成的简易状况办理库就完成了。该库完成了跨组件同享数据,跨组件更新数据状况,并更新对应的兄弟组件。
这也是一些第三方开源状况库的基本原理。
三,InheritedWidget如何同享数据
先看看咱们获取 InheritedWidget实例的dependOnInheritedWidgetOfExactType办法,注释翻译如下:
获取给定类型T的最近小部件,它有必要是详细InheritedWidget子类的类型,并将此构建上下文注册到该小部件,以便当该小部件更改时(或引入该类型的新小部件,或小部件消失脱离),这个构建上下文被重建,以便它能够从那个小部件获取新值。 这通常从of()静态办法中隐式调用,例如Theme.of 。 不该从小部件结构函数或State.initState办法调用此办法,由于假如承继的值产生更改,这些办法将不会再次被调用。为了保证小部件在承继值更改时正确更新本身,只能从构建办法、布局和绘制回调或从State.didChangeDependencies调用(直接或间接)。 不该从State.dispose调用此办法,由于此刻元素树不再稳定。要从该办法引用先人,请将对先人的引用保存在State.didChangeDependencies中。从State.deactivate中运用此办法是安全的,每逢从树中删除小部件时都会调用该办法。 也能够从交互事情处理程序(例如手势回调)或计时器调用此办法,以获取一次值,假如该值不会被缓存并稍后重用。 调用此办法是 O(1),具有较小的常数因子,但会导致更频频地重建小部件。 一旦小部件经过调用此办法注册对特定类型的依靠关系,它将被重建,并且State.didChangeDependencies将被调用,每逢与该小部件相关的更改产生时,直到下一次移动小部件或其先人之一(例如例如,由于添加或删除了先人)。 aspect参数仅在T是支持部分更新的InheritedWidget子类时运用,例如InheritedModel 。它指定此上下文所依靠的承继小部件的“方面。 T? dependOnInheritedWidgetOfExactType({ Object? aspect });
- 咱们找到了
BuildContext
的该办法的详细完成,实际上是在Element
中,其完成了BuildContext
。
@override
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
if (ancestor != null) {
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
能够看到主要是从 **_inheritedWidgets**
中找到对应Type
的InheritedElement
。
- 咱们再去看
InheritedElement
源码,主要关注下_updateInheritance
办法
@override
void _updateInheritance() {
assert(_lifecycleState == _ElementLifecycle.active);
final Map<Type, InheritedElement>? incomingWidgets = _parent?._inheritedWidgets;
if (incomingWidgets != null)
_inheritedWidgets = HashMap<Type, InheritedElement>.of(incomingWidgets);
else
_inheritedWidgets = HashMap<Type, InheritedElement>();
_inheritedWidgets![widget.runtimeType] = this;
}
小结:能够看到_updateInheritance
办法中,其向_inheritedWidgets中添加了自己。而_inheritedWidgets在Element树中会被子Element承继,所以子树就能轻松获取到该InheritedElement
实例了。
而_updateInheritance
办法,是在Element
mount
和 active
办法中被调用的,当**InheritedElement**
被挂载到element树中或者被激活后执行,就能被子element获取到了。
至此就能大约了解**InheritedWidget**
如何同享数据了,主要是运用InhritedElement
作为中转持有。
最终:
最终咱们大约了解了InheritedWidget
的运用和原理。但是要进一步整理整个流程的时分,就需要整理Widget,Element和RenderObject三者的关系了。
学无止境,下一把就来了解下flutter的Widget体系。
加油!!