performRebuild method

  1. @override
void performRebuild()
override

Inspired by SliverMultiBoxAdaptorElement.performRebuild.

This method is called to process any pending updates (for example, new intervals have been added, old intervals have changed state, reordering has started, and so on).

It considers all pending build updates to move each old last rendered child to its new position.

It also calls the _AnimatedRenderSliverMultiBoxAdaptor.didChangeDependencies method if a dependency has been changed.

Implementation

@override
void performRebuild() {
  _dbgBegin('performRebuild');
  _intervalListManager.updates.forEach((e) => _dbgPrint(e.toString()));

  if (_didChangeDependencies) {
    renderObject.didChangeDependencies(this);
    _didChangeDependencies = false;
  }

  super.performRebuild();

  assert(_currentlyUpdatingChildIndex == null);
  _currentBeforeChild = null;
  try {
    // moving children will temporary violate the integrity
    renderObject.debugChildIntegrityEnabled = false;

    final results = <_PopUpList?, SplayTreeMap<int, _RebuildResult>>{};
    final removeList = <Element>[];

    final newChildrenMap = <_PopUpList?, SplayTreeMap<int, Element?>>{};

    Element? update(Element? element, _ReindexResult r) {
      if (r.needsRebuild) {
        if (r.discardElement && element != null) {
          _dbgPrint(
              'updating ${debugElement(element)} discard and update...');
          element =
              updateChild(element, null, _Slot(r.newIndex!, r.popUpList));
        } else {
          _dbgPrint('updating ${debugElement(element)} update...');
        }
        _currentlyUpdatingChildIndex = r.newIndex;
        _currentlyUpdatingPopUpList = r.popUpList;
        final widget = build(r.newIndex!, popUpList: r.popUpList)!;
        element =
            updateChild(element, widget, _Slot(r.newIndex!, r.popUpList))!;
        _currentlyUpdatingChildIndex = null;
        _currentlyUpdatingPopUpList = null;
        _dbgPrint('    ....updated into ${debugElement(element)}');
      } else {
        assert(!r.discardElement);
        final newSlot = _Slot(r.newIndex!, r.popUpList);
        if (newSlot != element?.slot) {
          _dbgPrint(
              'updating ${debugElement(element)} slot only: ${element?.slot} -> $newSlot');
        }
        _currentlyUpdatingChildIndex = r.newIndex;
        _currentlyUpdatingPopUpList = r.popUpList;
        updateChild(element, element!.widget, newSlot);
        _currentlyUpdatingChildIndex = null;
        _currentlyUpdatingPopUpList = null;
      }
      return element;
    }

    // final offsets = <PopUpList, double>{};
    void considerElements(
        _PopUpList? popUpList, SplayTreeMap<int, Element?> childElements) {
      _dbgBegin('considering popUpList=${popUpList?.debugId}');
      _dbgPrint('children=${_debugChildrenList(childElements)}');
      _dbgPrint('renderBoxes=${renderObject.debugRenderBoxes(popUpList)}');
      for (int? index in childElements.keys.toList()) {
        var element = childElements[index]!;

        final r = oldIndexToNewIndex(index!, popUpList);

        if (r.newIndex == null) {
          removeList.add(element);
          _dbgPrint(
              'element ${debugElement(element)} scheduled for removing');
        } else {
          _dbgPrint(
              'element ${debugElement(element)} from pl(${popUpList?.debugId}):$index -> pl(${r.popUpList?.debugId}}):${r.newIndex}');
          results.putIfAbsent(r.popUpList,
                  () => SplayTreeMap<int, _RebuildResult>())[r.newIndex!] =
              _RebuildResult(r, element);
        }
      }
      _dbgEnd();
    }

    _childElements.forEach((popUpList, elements) {
      results.putIfAbsent(
          popUpList, () => SplayTreeMap<int, _RebuildResult>());
      considerElements(popUpList, elements);
    });

    for (final popUpList in results.keys) {
      final newChildren = newChildrenMap.putIfAbsent(
          popUpList, () => SplayTreeMap<int, Element?>());

      _currentBeforeChild = null;

      _dbgBegin('finalizing popUpList=${popUpList?.debugId}');
      for (final index in results[popUpList]!.keys) {
        final rr = results[popUpList]![index]!;
        final r = rr.result;
        var childParentData = _parentDataOf(rr.element);

        if (childParentData != null && r.clearLayoutOffset) {
          _dbgPrint('clearing layout offset of ${debugElement(rr.element)}');
          childParentData.layoutOffset = null;
        }

        _currentlyUpdatingChildIndex = r.newIndex;

        final element = update(rr.element, r)!;

        newChildren[r.newIndex!] = element;

        childParentData = _parentDataOf(element);
        if (r.newIndex == 0) {
          childParentData?.layoutOffset = 0.0;
        }

        if (!childParentData!.keptAlive) {
          _currentBeforeChild = element.renderObject as RenderBox?;
        }
      }

      _childElements.putIfAbsent(popUpList, () => newChildren);
      _childElements[popUpList] = newChildren;
      _dbgPrint(
          'result childrenList: ${_debugChildrenList(_childElements[popUpList]!)}');

      if (newChildren.isEmpty && popUpList != null) {
        _dbgPrint('dismissing popup ${popUpList.debugId}');
        _childElements.remove(popUpList);
      }

      _dbgEnd();
    }

    _dbgBegin('result RenderBoxes');
    for (final popUpList in results.keys) {
      _dbgPrint(
          'popup ${popUpList?.debugId}: ${renderObject.debugRenderBoxes(popUpList)}');
    }
    _dbgEnd();

    for (final element in removeList) {
      _dbgPrint('removing ${debugElement(element)}');
      final slot = element.slot as _Slot;
      _currentlyUpdatingChildIndex = slot.index;
      _currentlyUpdatingPopUpList = slot.popUpList;
      updateChild(element, null, slot);
    }
  } catch (e) {
    _dbgPrint('EXCEPTION!!!!');
  } finally {
    _currentlyUpdatingChildIndex = null;
    _currentlyUpdatingPopUpList = null;
    renderObject.debugChildIntegrityEnabled = true;
    _intervalListManager.clearUpdates();
    // renderObject.removeEmptyKeys();
    _dbgEnd();
  }
}