buildScope method
- Element context, [
- VoidCallback? callback
Establishes a scope for updating the widget tree, and calls the given
callback
, if any. Then, builds all the elements that were marked as
dirty using scheduleBuildFor, in depth order.
This mechanism prevents build methods from transitively requiring other build methods to run, potentially causing infinite loops.
The dirty list is processed after callback
returns, building all the
elements that were marked as dirty using scheduleBuildFor, in depth
order. If elements are marked as dirty while this method is running, they
must be deeper than the context
node, and deeper than any
previously-built node in this pass.
To flush the current dirty list without performing any other work, this function can be called with no callback. This is what the framework does each frame, in WidgetsBinding.drawFrame.
Only one buildScope can be active at a time.
A buildScope implies a lockState scope as well.
To print a console message every time this method is called, set debugPrintBuildScope to true. This is useful when debugging problems involving widgets not getting marked dirty, or getting marked dirty too often.
Implementation
@pragma('vm:notify-debugger-on-exception')
void buildScope(Element context, [ VoidCallback? callback ]) {
final BuildScope buildScope = context.buildScope;
if (callback == null && buildScope._dirtyElements.isEmpty) {
return;
}
assert(_debugStateLockLevel >= 0);
assert(!_debugBuilding);
assert(() {
if (debugPrintBuildScope) {
debugPrint(
'buildScope called with context $context; '
"its build scope's dirty list is: ${buildScope._dirtyElements}",
);
}
_debugStateLockLevel += 1;
_debugBuilding = true;
return true;
}());
if (!kReleaseMode) {
Map<String, String>? debugTimelineArguments;
assert(() {
if (debugEnhanceBuildTimelineArguments) {
debugTimelineArguments = <String, String>{
'build scope dirty count': '${buildScope._dirtyElements.length}',
'build scope dirty list': '${buildScope._dirtyElements}',
'lock level': '$_debugStateLockLevel',
'scope context': '$context',
};
}
return true;
}());
FlutterTimeline.startSync(
'BUILD',
arguments: debugTimelineArguments
);
}
try {
_scheduledFlushDirtyElements = true;
buildScope._building = true;
if (callback != null) {
assert(_debugStateLocked);
Element? debugPreviousBuildTarget;
assert(() {
debugPreviousBuildTarget = _debugCurrentBuildTarget;
_debugCurrentBuildTarget = context;
return true;
}());
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(_debugBuilding);
assert(() {
_debugBuilding = false;
_debugStateLockLevel -= 1;
if (debugPrintBuildScope) {
debugPrint('buildScope finished');
}
return true;
}());
}
assert(_debugStateLockLevel >= 0);
}