performRebuild method

  1. @override
void performRebuild()
override

Cause the widget to update itself.

Called by rebuild after the appropriate checks have been made.

The base implementation only clears the dirty flag.

Implementation

@override
void performRebuild() {
  super.performRebuild();
  _currentBeforeChild = null;
  bool childrenUpdated = false;
  assert(_currentlyUpdatingChildIndex == null);
  try {
    final SplayTreeMap<int, Element?> newChildren = SplayTreeMap<int, Element?>();
    final Map<int, double> indexToLayoutOffset = HashMap<int, double>();
    final SliverMultiBoxAdaptorWidget adaptorWidget = widget as SliverMultiBoxAdaptorWidget;
    void processElement(int index) {
      _currentlyUpdatingChildIndex = index;
      if (_childElements[index] != null && _childElements[index] != newChildren[index]) {
        // This index has an old child that isn't used anywhere and should be deactivated.
        _childElements[index] = updateChild(_childElements[index], null, index);
        childrenUpdated = true;
      }
      final Element? newChild = updateChild(newChildren[index], _build(index, adaptorWidget), index);
      if (newChild != null) {
        childrenUpdated = childrenUpdated || _childElements[index] != newChild;
        _childElements[index] = newChild;
        final SliverMultiBoxAdaptorParentData parentData = newChild.renderObject!.parentData! as SliverMultiBoxAdaptorParentData;
        if (index == 0) {
          parentData.layoutOffset = 0.0;
        } else if (indexToLayoutOffset.containsKey(index)) {
          parentData.layoutOffset = indexToLayoutOffset[index];
        }
        if (!parentData.keptAlive) {
          _currentBeforeChild = newChild.renderObject as RenderBox?;
        }
      } else {
        childrenUpdated = true;
        _childElements.remove(index);
      }
    }
    for (final int index in _childElements.keys.toList()) {
      final Key? key = _childElements[index]!.widget.key;
      final int? newIndex = key == null ? null : adaptorWidget.delegate.findIndexByKey(key);
      final SliverMultiBoxAdaptorParentData? childParentData =
        _childElements[index]!.renderObject?.parentData as SliverMultiBoxAdaptorParentData?;

      if (childParentData != null && childParentData.layoutOffset != null) {
        indexToLayoutOffset[index] = childParentData.layoutOffset!;
      }

      if (newIndex != null && newIndex != index) {
        // The layout offset of the child being moved is no longer accurate.
        if (childParentData != null) {
          childParentData.layoutOffset = null;
        }

        newChildren[newIndex] = _childElements[index];
        if (_replaceMovedChildren) {
          // We need to make sure the original index gets processed.
          newChildren.putIfAbsent(index, () => null);
        }
        // We do not want the remapped child to get deactivated during processElement.
        _childElements.remove(index);
      } else {
        newChildren.putIfAbsent(index, () => _childElements[index]);
      }
    }

    renderObject.debugChildIntegrityEnabled = false; // Moving children will temporary violate the integrity.
    newChildren.keys.forEach(processElement);
    // An element rebuild only updates existing children. The underflow check
    // is here to make sure we look ahead one more child if we were at the end
    // of the child list before the update. By doing so, we can update the max
    // scroll offset during the layout phase. Otherwise, the layout phase may
    // be skipped, and the scroll view may be stuck at the previous max
    // scroll offset.
    //
    // This logic is not needed if any existing children has been updated,
    // because we will not skip the layout phase if that happens.
    if (!childrenUpdated && _didUnderflow) {
      final int lastKey = _childElements.lastKey() ?? -1;
      final int rightBoundary = lastKey + 1;
      newChildren[rightBoundary] = _childElements[rightBoundary];
      processElement(rightBoundary);
    }
  } finally {
    _currentlyUpdatingChildIndex = null;
    renderObject.debugChildIntegrityEnabled = true;
  }
}