buildScope method

void buildScope(
  1. Element context, [
  2. 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);
}