InheritedWidget简介
用于在 widget 树中高效地向下传递数据,允许子组件获取最近Widget树中的InheritedWidget的数据
- 数据共享:在 widget 树中向下共享数据
- 高效更新:当数据变化时,只重建依赖该数据的子 widget
- 自动依赖管理:子 widget 自动"注册"为依赖项(dependOn)
示例
组件结构
子组件TextShowWidget 通过dependOnInheritedWidgetOfExactType()和NameInheritedWidget关联,当InputNameWidget组件TextFiled输入变化的时,触发NameInheritedWidget变化,当updateShouldNotify返回true,从而通知和NameInheritedWidget关联的组件TextShowWidget变化
代码示例
TestInheritedWidget/AppChildInheritedWidget
在Flutter中Widget是不可变的,InheritedWidget也是一个Widget,所有要更新widget还是得构造一个StatfulWidget,通过setState()来更新, 在 AppChildInheritedWidget中当name发生变化,重新rebuild,来更新NameInheritedWidget数据,
但是此时的InputShowWidget是通过构造方法传入的,不会改变
class TestInheritedWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("TestInheritedWidget111");
return Scaffold(
appBar: AppBar(
title: Text(
"Inherited",
style: TextStyle(fontSize: 28, color: Colors.white),
),
backgroundColor: Color(0xFFE51BE5),
),
body: AppChildInheritedWidget(child: InputShowWidget()));
}
}
class AppChildInheritedWidget extends StatefulWidget {
final Widget child;
const AppChildInheritedWidget({required this.child, super.key});
@override
State<AppChildInheritedWidget> createState() => _AppChildInheritedWidgetState();
}
class _AppChildInheritedWidgetState extends State<AppChildInheritedWidget> {
String? name;
@override
Widget build(BuildContext context) {
return NameInheritedWidget(
child: widget.child,
name: name,
onNameChange: (value) {
setState(() {
name = value;
});
},
);
}
}
NameInheritedWidget
dependOnInheritedWidgetOfExactType和 InheritedWidget 产生依赖关系,主动获取数据,当InheritedWidget数据变化时,关联的组件也会自动更新(build),
getInheritedWidgetOfExactType 单纯的获取InheritedWidget的数据,不产生关联,被动获取,
updateShouldNotify比较新旧widget数据是否一致,true触发更新
class NameInheritedWidget extends InheritedWidget {
final String? name;
void Function(String? name) onNameChange;
NameInheritedWidget({required super.child, this.name, required this.onNameChange});
static String? of(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType<NameInheritedWidget>()!
.name;
}
static String? maybe(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType<NameInheritedWidget>()
?.name;
}
static NameInheritedWidget? getNameInheritedWidget(BuildContext context) {
return context.getInheritedWidgetOfExactType<NameInheritedWidget>();
}
@override
bool updateShouldNotify(covariant NameInheritedWidget oldWidget) {
return oldWidget.name != this.name;
}
}
InputShowWidget
class InputShowWidget extends StatelessWidget {
const InputShowWidget({super.key});
@override
Widget build(BuildContext context) {
print("InputShowWidget222");
return Column(
children: [
Expanded(flex: 2, child: InputNameWidget()),
Expanded(flex: 1, child: TextShowWidget())
],
);
}
}
class TextShowWidget extends StatelessWidget {
const TextShowWidget({super.key});
@override
Widget build(BuildContext context) {
print("TextShowWidget");
var name = NameInheritedWidget.maybe(context) ?? "未知输入";
return Padding(
padding: const EdgeInsets.all(20.0),
child: Center(
child: Text(
name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: Colors.deepOrangeAccent),
)),
);
}
}
InputNameWidget/TextShowWidget
class InputNameWidget extends StatefulWidget {
const InputNameWidget({super.key});
@override
State<InputNameWidget> createState() => _InputNameWidgetState();
}
class _InputNameWidgetState extends State<InputNameWidget> {
TextEditingController _textEditingController = TextEditingController(text: "爱好");
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
_textEditingController.dispose();
}
@override
Widget build(BuildContext context) {
print("InputNameWidget333");
return LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraints.maxHeight),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
spacing: 20,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
style: TextStyle(color: Colors.pink),
controller: _textEditingController,
onChanged: (value){
print(value);
NameInheritedWidget.getNameInheritedWidget(context)
?.onNameChange(value);
},
decoration: InputDecoration(
focusColor: Colors.red,
hintText: "请输入",
hintStyle: TextStyle(color: Colors.blueAccent),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.black)),
filled: true,
fillColor: Color(0xE5BBBBC4)),
),
SizedBox(height: 20,),
FilledButton(
style: ButtonStyle(
backgroundColor: WidgetStatePropertyAll(Colors.black),
padding: WidgetStatePropertyAll(
EdgeInsets.symmetric(vertical: 18)),
shape: WidgetStatePropertyAll(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)))),
onPressed: () {
NameInheritedWidget.getNameInheritedWidget(context)
?.onNameChange(_textEditingController.text);
},
child: Text("点击"))
],
),
),
),
);
});
}
}
class TextShowWidget extends StatelessWidget {
const TextShowWidget({super.key});
@override
Widget build(BuildContext context) {
print("TextShowWidget");
var name = NameInheritedWidget.maybe(context) ?? "未知输入";
return Padding(
padding: const EdgeInsets.all(20.0),
child: Center(
child: Text(
name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: Colors.deepOrangeAccent),
)),
);
}
}
日志
首次进入所有Widget的build方法全部执行
TextField发生变化,触发底部文字变化
dependOnInheritedWidgetOfExactType探索如何关联
PersistentHashMap<Type, InheritedElement>? _inheritedElements; 存储InheritedElement类型的Map集合
@override
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement? ancestor = _inheritedElements?[T];
if (ancestor != null) {
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
在mount和 activate 赋值
void _updateInheritance() {
assert(_lifecycleState == _ElementLifecycle.active);
_inheritedElements = _parent?._inheritedElements;
}
总结
子组件 _dependencies 存储所关联的InheritedElement, 通过ancestor.updateDependencies(this, aspect);把子组件和 InheritedElement 关联,把子组件存储在InheritedElement的 Map1 (key值) ,
当InheritedElement更新updated,通过updateShouldNotify 判断是否更新super.updated(oldWidget),如需更新调用 notifyClients,遍历上述Map1的keys,执行
dependent.didChangeDependencies(); 这时的dependent就是上述子组件,执行子组件的markNeedsBuild,执行owner!.scheduleBuildFor(this),把子组件加入_dirtyElements.add(element);_dirtyElements集合中,并执行scheduleRebuild?.call();
WidgetsBinding中执行ensureVisualUpdate,执行scheduleFrame请求新的帧,等待VSync信号,执行onDrawFrame,执行 rootElement.buildScope._flushDirtyElements()中_dirtyElements排序遍历 _dirtyElements,执行element.rebuild,
执行element.performRebuild 执行widget.build,执行element.updateChild挂载到element上,到此流程结束。
dependOnInheritedElement
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, {Object? aspect}) {
_dependencies ??= HashSet<InheritedElement>();
_dependencies!.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget as InheritedWidget;
}
@protected
void updateDependencies(Element dependent, Object? aspect) {
setDependencies(dependent, null);
}
@protected
void setDependencies(Element dependent, Object? value) {
_dependents[dependent] = value;
}
void updated(InheritedWidget oldWidget) {
if ((widget as InheritedWidget).updateShouldNotify(oldWidget)) {
super.updated(oldWidget);
}
}
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
@override
void notifyClients(InheritedWidget oldWidget) {
assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
for (final Element dependent in _dependents.keys) {
assert(() {
// check that it really is our descendant
Element? ancestor = dependent._parent;
while (ancestor != this && ancestor != null) {
ancestor = ancestor._parent;
}
return ancestor == this;
}());
// check that it really depends on us
assert(dependent._dependencies!.contains(this));
notifyDependent(oldWidget, dependent);
}
}
}
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
子组件的 didChangeDependencies/markNeedsBuild
@mustCallSuper
void didChangeDependencies() {
assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
markNeedsBuild();
}
void markNeedsBuild() {
if (dirty) {
return;
}
_dirty = true;
owner!.scheduleBuildFor(this);
}
void scheduleBuildFor(Element element) {
final BuildScope buildScope = element.buildScope;
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
onBuildScheduled!();
}
buildScope._scheduleBuildFor(element);
}
void _scheduleBuildFor(Element element) {
if (!element._inDirtyList) {
_dirtyElements.add(element);
element._inDirtyList = true;
}
if (!_buildScheduled && !_building) {
_buildScheduled = true;
scheduleRebuild?.call();
}
if (_dirtyElementsNeedsResorting != null) {
_dirtyElementsNeedsResorting = true;
}
}
mixin WidgetsBinding
on
BindingBase,
ServicesBinding,
SchedulerBinding,
GestureBinding,
RendererBinding,
SemanticsBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
_buildOwner = BuildOwner();
buildOwner!.onBuildScheduled = _handleBuildScheduled;
platformDispatcher.onLocaleChanged = handleLocaleChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
SystemChannels.backGesture.setMethodCallHandler(_handleBackGestureInvocation);
assert(() {
FlutterErrorDetails.propertiesTransformers.add(debugTransformDebugCreator);
return true;
}());
platformMenuDelegate = DefaultPlatformMenuDelegate();
}
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
@protected
void ensureFrameCallbacksRegistered() {
platformDispatcher.onBeginFrame ??= _handleBeginFrame;
platformDispatcher.onDrawFrame ??= _handleDrawFrame;
}
if (rootElement != null) {
buildOwner!.buildScope(rootElement!);
}
void buildScope(Element context, [VoidCallback? callback]) {
final BuildScope buildScope = context.buildScope;
if (callback == null && buildScope._dirtyElements.isEmpty) {
return;
}
try {
_scheduledFlushDirtyElements = true;
buildScope._building = true;
if (callback != null) {
assert(_debugStateLocked);
Element? debugPreviousBuildTarget;
try {
callback();
} finally {
assert(() {
assert(_debugCurrentBuildTarget == context);
_debugCurrentBuildTarget = debugPreviousBuildTarget;
_debugElementWasRebuilt(context);
return true;
}());
}
}
buildScope._flushDirtyElements(debugBuildRoot: context);
} finally {
buildScope._building = false;
_scheduledFlushDirtyElements = false;
if (!kReleaseMode) {
FlutterTimeline.finishSync();
}
}
assert(_debugStateLockLevel >= 0);
}
buildScope._flushDirtyElements(debugBuildRoot: context);
_tryRebuild(element);
@pragma('vm:notify-debugger-on-exception')
void _flushDirtyElements({required Element debugBuildRoot}) {
assert(_dirtyElementsNeedsResorting == null, '_flushDirtyElements must be non-reentrant');
//排序
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
try {
for (int index = 0; index < _dirtyElements.length; index = _dirtyElementIndexAfter(index)) {
final Element element = _dirtyElements[index];
if (identical(element.buildScope, this)) {
_tryRebuild(element);
}
}
assert(() {
final Iterable<Element> missedElements = _dirtyElements.where(
(Element element) =>
element.debugIsActive && element.dirty && identical(element.buildScope, this),
);
} finally {
for (final Element element in _dirtyElements) {
if (identical(element.buildScope, this)) {
element._inDirtyList = false;
}
}
_dirtyElements.clear();
_dirtyElementsNeedsResorting = null;
_buildScheduled = false;
}
}
element.rebuild();
void _tryRebuild(Element element) {
assert(element._inDirtyList);
assert(identical(element.buildScope, this));
final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(element.widget);
if (isTimelineTracked) {
Map<String, String>? debugTimelineArguments;
assert(() {
if (kDebugMode && debugEnhanceBuildTimelineArguments) {
debugTimelineArguments = element.widget.toDiagnosticsNode().toTimelineArguments();
}
return true;
}());
FlutterTimeline.startSync('${element.widget.runtimeType}', arguments: debugTimelineArguments);
}
try {
element.rebuild();
} catch (e, stack) {
}
}
performRebuild
void rebuild({bool force = false}) {
performRebuild();
}
执行 build,updateChild()挂载到element上
void performRebuild() {
Widget? built;
try {
assert(() {
_debugDoingBuild = true;
return true;
}());
built = build();
} catch (e, stack) {
} finally {
// We delay marking the element as clean until after calling build() so
// that attempts to markNeedsBuild() during build() will be ignored.
super.performRebuild(); // clears the "dirty" flag
}
try {
_child = updateChild(_child, built, slot);
assert(_child != null);
} catch (e, stack) {
}
}