internalInitController method

void internalInitController({
  1. required NodeFlowTheme theme,
  2. required Size portSizeResolver(
    1. Port port
    ),
  3. NodeShape? nodeShapeBuilder(
    1. Node<T> node
    )?,
  4. bool Function(Connection connection, Offset point)? connectionHitTesterBuilder(
    1. ConnectionPainter painter
    )?,
  5. List<Rect> connectionSegmentCalculator(
    1. Connection connection
    )?,
  6. NodeFlowEvents<T>? events,
})

Initializes the controller for use with the NodeFlow editor.

THIS IS THE CANONICAL INITIALIZATION POINT.

All editor infrastructure is set up here in a specific order. This method must be called from NodeFlowEditor.initState before any other operations.

@internal - This method is for internal use by NodeFlowEditor only. Do not call directly from application code.

Parameters

  • theme: The visual theme for the editor (required)
  • portSizeResolver: Resolves the size of a port for spatial indexing (required)
  • nodeShapeBuilder: Optional builder for custom node shapes
  • connectionHitTesterBuilder: Builder for the connection hit test callback. This receives the ConnectionPainter and returns a hit test function.
  • connectionSegmentCalculator: Calculates segment bounds for connection spatial indexing. Required for accurate connection hit testing.
  • events: Optional event handlers for node/connection/viewport events

Example

// In NodeFlowEditor.initState():
widget.controller.internalInitController(
  theme: widget.theme,
  portSizeResolver: (port) => port.size ?? widget.theme.portTheme.size,
  nodeShapeBuilder: widget.nodeShapeBuilder != null
      ? (node) => widget.nodeShapeBuilder!(context, node)
      : null,
  connectionHitTesterBuilder: (painter) => (connection, point) {
    // hit test logic using painter
  },
  connectionSegmentCalculator: (connection) {
    // calculate segment bounds
  },
  events: widget.events,
);

Implementation

void internalInitController({
  required NodeFlowTheme theme,
  required Size Function(Port port) portSizeResolver,
  NodeShape? Function(Node<T> node)? nodeShapeBuilder,
  bool Function(Connection connection, Offset point)? Function(
    ConnectionPainter painter,
  )?
  connectionHitTesterBuilder,
  List<Rect> Function(Connection connection)? connectionSegmentCalculator,
  NodeFlowEvents<T>? events,
}) {
  // Idempotent - only initialize once
  if (_editorInitialized) return;
  _editorInitialized = true;

  // =========================================================================
  // Step 1: Store the theme
  // =========================================================================
  runInAction(() => _themeObservable.value = theme);

  // =========================================================================
  // Step 2: Set up node shape builder
  // =========================================================================
  // This must happen before spatial index setup and connection painter
  // creation since both use the shape builder.
  _nodeShapeBuilder = nodeShapeBuilder;

  // =========================================================================
  // Step 3: Set up spatial index callbacks
  // =========================================================================
  // These callbacks are used when calculating bounds for nodes and ports.
  // They must be set before any spatial index operations.
  _spatialIndex.portSizeResolver = portSizeResolver;
  _spatialIndex.nodeShapeBuilder = nodeShapeBuilder;

  // =========================================================================
  // Step 4: Create connection painter
  // =========================================================================
  // The connection painter handles rendering and hit testing of connections.
  // It needs the theme and optionally the node shape builder.
  _connectionPainter = ConnectionPainter(
    theme: theme,
    nodeShape: nodeShapeBuilder != null
        ? (node) => nodeShapeBuilder(node as Node<T>)
        : null,
  );

  // =========================================================================
  // Step 5: Set up connection hit tester
  // =========================================================================
  // Now that the connection painter exists, we can create the hit tester.
  if (connectionHitTesterBuilder != null) {
    _spatialIndex.connectionHitTester = connectionHitTesterBuilder(
      _connectionPainter!,
    );
  }

  // =========================================================================
  // Step 6: Set up render order provider
  // =========================================================================
  // This enables accurate hit testing based on visual stacking order.
  _spatialIndex.renderOrderProvider = () => sortedNodes;

  // =========================================================================
  // Step 7: Store connection segment calculator for later use
  // =========================================================================
  _connectionSegmentCalculator = connectionSegmentCalculator;

  // =========================================================================
  // Step 8: Set up spatial index reactions
  // =========================================================================
  // These reactions automatically sync the spatial index when nodes,
  // connections, or theme properties change.
  _setupSpatialIndexReactions();

  // =========================================================================
  // Step 9: Set up event handlers
  // =========================================================================
  if (events != null) {
    _events = events;
  }

  // =========================================================================
  // Step 10: Initialize spatial indexes and node infrastructure
  // =========================================================================
  // If nodes were pre-loaded (e.g., from loadDocument before editor mounted),
  // we need to set up their infrastructure and rebuild spatial indexes now.
  _initializeLoadedNodes();
  _rebuildSpatialIndexes();
}