didUpdateWidget method

  1. @override
void didUpdateWidget(
  1. covariant SchematicCanvas oldWidget
)
override

Called whenever the widget configuration changes.

If the parent widget rebuilds and requests that this location in the tree update to display a new widget with the same runtimeType and Widget.key, the framework will update the widget property of this State object to refer to the new widget and then call this method with the previous widget as an argument.

Override this method to respond when the widget changes (e.g., to start implicit animations).

The framework always calls build after calling didUpdateWidget, which means any calls to setState in didUpdateWidget are redundant.

If a State's build method depends on an object that can itself change state, for example a ChangeNotifier or Stream, or some other object to which one can subscribe to receive notifications, then be sure to subscribe and unsubscribe properly in initState, didUpdateWidget, and dispose:

  • In initState, subscribe to the object.
  • In didUpdateWidget unsubscribe from the old object and subscribe to the new one if the updated widget configuration requires replacing the object.
  • In dispose, unsubscribe from the object.

Implementations of this method should start with a call to the inherited method, as in super.didUpdateWidget(oldWidget).

See the discussion at Element.rebuild for more information on when this method is called.

Implementation

@override
void didUpdateWidget(covariant SchematicCanvas oldWidget) {
  super.didUpdateWidget(oldWidget);
  // Swap incoming signal listener if the notifier reference changed.
  if (widget.incomingSignalPaths != oldWidget.incomingSignalPaths) {
    oldWidget.incomingSignalPaths?.removeListener(_onIncomingSignals);
    widget.incomingSignalPaths?.addListener(_onIncomingSignals);
  }
  // Clear stale hover tooltip when layout or data changes
  if (widget.layout != oldWidget.layout ||
      widget.externalHierarchy != oldWidget.externalHierarchy ||
      widget.netlistJson != oldWidget.netlistJson) {
    _dismissTooltip(immediate: true);
    _hoverTooltipNotifier.value = (
      tooltipKey: null,
      lines: <String>[],
      position: Offset.zero,
    );
  }
  // Rebuild hierarchy if external hierarchy or netlistJson changed
  if (widget.externalHierarchy != oldWidget.externalHierarchy ||
      widget.netlistJson != oldWidget.netlistJson) {
    _buildNetlistIndex();
  }
  // Rebuild bridge if layout changed and hierarchy present
  if (widget.layout != oldWidget.layout && _hierarchy != null) {
    _bridge = LayoutHierarchyBridge.build(
      hierarchy: _hierarchy!,
      layout: widget.layout,
    );
  }
  if (widget.layout != oldWidget.layout) {
    if (_pendingAutoFitSkips > 0) {
      _pendingAutoFitSkips--;
      // When the last suppressed rebuild arrives and a search-driven
      // zoom is pending, schedule it for after this frame so the new
      // layout has been fully laid-out and painted.
      if (_pendingAutoFitSkips == 0 && _pendingZoomAction != null) {
        final zoomAction = _pendingZoomAction!;
        _pendingZoomAction = null;
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (mounted) {
            zoomAction();
          }
        });
      }
    } else {
      // Capture the port's viewport position BEFORE the post-frame
      // callback so we can keep it in the same spot after re-layout.
      Offset? portViewportAnchor;
      final portId = widget.focusPortId;
      if (portId != null && portId.isNotEmpty) {
        for (final oldPort in oldWidget.layout.ports) {
          if (oldPort.id == portId) {
            final cx = oldPort.x + oldPort.width / 2;
            final cy = oldPort.y + oldPort.height / 2;
            portViewportAnchor = Offset(
              cx * _scale + _offset.dx,
              cy * _scale + _offset.dy,
            );
            break;
          }
        }
      }

      WidgetsBinding.instance.addPostFrameCallback((_) {
        // Port-level focus takes priority: pan so the port stays
        // in its original viewport position (or centred if unknown).
        if (portId != null && portId.isNotEmpty) {
          _focusOnPort(portId, viewportAnchor: portViewportAnchor);
          return;
        }
        // If a node was just toggled, zoom to fit that node; otherwise
        // fit entire layout.
        final targetNodeId = widget.recentlyToggledNodeId;
        if (targetNodeId != null && targetNodeId.isNotEmpty) {
          _fitToNode(targetNodeId);
        } else {
          _fitToCanvas();
        }
      });
    }
  }
}