markNeedsBuild()方法作用:
标记该元素为"dirty"并将其添加到全局小部件列表中,以在下一帧中重建。
由于在一帧中两次构建一个元素是低效的,因此应用程序和小部件应该被构造成仅在帧开始之前的事件处理程序中标记小部件为"dirty",而不是在构建过程中标记。
markNeedsBuild() 是 Flutter 框架中 Element 类的方法之一,用于标记当前元素需要进行重建(rebuild)。每个 Flutter widget 都是由一个或多个 Element 对象来管理的,而 markNeedsBuild() 方法通常是在这些元素的 build() 方法中被调用的。
在 Flutter 中,widget 树是通过递归构建的。当一个 widget 的状态发生变化时,它会通知框架自己需要重新构建。markNeedsBuild() 方法就是用于发出这样的通知的。具体来说,当一个 widget 的状态发生变化时,它会调用自己对应的 Element 对象的 markNeedsBuild() 方法,这个方法会将 _dirty 标志位设置为 true,并将这个元素添加到其拥有者(parent)元素的重建队列中,以便在下一次 build 循环中重建。
需要注意的是,为了保证 Flutter 构建的正确性,markNeedsBuild() 方法只能在特定的上下文中调用,例如,它不能在一个 widget 的 build() 方法中直接被调用,而只能由 setState() 或类似方法间接地调用。此外,如果在不允许的上下文中调用了 markNeedsBuild(),Flutter 框架会抛出异常以避免构建错误。
调用时机:
- setState()方法调用
- InheritedWidget build时调用
![](https://img.haomeiwen.com/i45726/93abd24cf6e90050.png)
1、ComponentElement performRebuild方法
![](https://img.haomeiwen.com/i45726/959c12467115e530.png)
void performRebuild() {
assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(true));
Widget? built;
try {
assert(() {
_debugDoingBuild = true;
return true;
}());
built = build();
assert(() {
_debugDoingBuild = false;
return true;
}());
debugWidgetBuilderValue(widget, built);
} catch (e, stack) {
_debugDoingBuild = false;
built = ErrorWidget.builder(
_debugReportException(
ErrorDescription('building $this'),
e,
stack,
informationCollector: () => <DiagnosticsNode>[
if (kDebugMode)
DiagnosticsDebugCreator(DebugCreator(this)),
],
),
);
} finally {
// We delay marking the element as clean until after calling build() so
// that attempts to markNeedsBuild() during build() will be ignored.
_dirty = false;
assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(false));
}
try {
_child = updateChild(_child, built, slot);
assert(_child != null);
} catch (e, stack) {
built = ErrorWidget.builder(
_debugReportException(
ErrorDescription('building $this'),
e,
stack,
informationCollector: () => <DiagnosticsNode>[
if (kDebugMode)
DiagnosticsDebugCreator(DebugCreator(this)),
],
),
);
_child = updateChild(null, built, slot);
}
}
2、 _InheritedNotifierElement build 和 InheritedElement updated ---》notifyClients
Widget build() {
if (_dirty)
notifyClients(widget as InheritedNotifier<T>);
return super.build();
}
- InheritedElement updated
void updated(InheritedWidget oldWidget) {
if ((widget as InheritedWidget).updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
- ProxyElement super.updated(oldWidget);
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
3、 _InheritedNotifierElement notifyClients
void notifyClients(InheritedNotifier<T> oldWidget) {
super.notifyClients(oldWidget);
_dirty = false;
}
4、InheritedElement notifyClients
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);
}
}
然后调用
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
5、Element didChangeDependencies
void didChangeDependencies() {
assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
markNeedsBuild();
}
6、Element markNeedsBuild
void markNeedsBuild() {
assert(_lifecycleState != _ElementLifecycle.defunct);
if (_lifecycleState != _ElementLifecycle.active)
return;
assert(owner != null);
assert(_lifecycleState == _ElementLifecycle.active);
assert(() {
if (owner!._debugBuilding) {
assert(owner!._debugCurrentBuildTarget != null);
assert(owner!._debugStateLocked);
if (_debugIsInScope(owner!._debugCurrentBuildTarget!))
return true;
if (!_debugAllowIgnoredCallsToMarkNeedsBuild) {
final List<DiagnosticsNode> information = <DiagnosticsNode>[
ErrorSummary('setState() or markNeedsBuild() called during build.'),
ErrorDescription(
'This ${widget.runtimeType} widget cannot be marked as needing to build because the framework '
'is already in the process of building widgets. A widget can be marked as '
'needing to be built during the build phase only if one of its ancestors '
'is currently building. This exception is allowed because the framework '
'builds parent widgets before children, which means a dirty descendant '
'will always be built. Otherwise, the framework might not visit this '
'widget during this build phase.',
),
describeElement('The widget on which setState() or markNeedsBuild() was called was'),
];
if (owner!._debugCurrentBuildTarget != null)
information.add(owner!._debugCurrentBuildTarget!.describeWidget('The widget which was currently being built when the offending call was made was'));
throw FlutterError.fromParts(information);
}
assert(dirty); // can only get here if we're not in scope, but ignored calls are allowed, and our call would somehow be ignored (since we're already dirty)
} else if (owner!._debugStateLocked) {
assert(!_debugAllowIgnoredCallsToMarkNeedsBuild);
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() or markNeedsBuild() called when widget tree was locked.'),
ErrorDescription(
'This ${widget.runtimeType} widget cannot be marked as needing to build '
'because the framework is locked.',
),
describeElement('The widget on which setState() or markNeedsBuild() was called was'),
]);
}
return true;
}());
if (dirty)
return;
_dirty = true;
owner!.scheduleBuildFor(this);
}
网友评论