NodeFlowController
API reference for the NodeFlowController class
NodeFlowController
The NodeFlowController manages all graph state including nodes, connections, selection, and viewport. It's the central point for programmatic graph manipulation.
Constructor
NodeFlowController<T>({
GraphViewport? initialViewport,
NodeFlowConfig? config,
})| Parameter | Type | Description |
|---|---|---|
initialViewport | GraphViewport? | Initial viewport position and zoom |
config | NodeFlowConfig? | Behavioral configuration (snap-to-grid, zoom limits, etc.) |
Node Operations
addNode
Add a node to the graph.
void addNode(Node<T> node)Example:
final node = Node<MyData>(
id: 'node-1',
position: Offset(100, 100),
size: Size(150, 80),
data: MyData(label: 'Process'),
inputPorts: [Port(id: 'in-1', name: 'Input')],
outputPorts: [Port(id: 'out-1', name: 'Output')],
);
controller.addNode(node);removeNode
Remove a node and all its connections.
void removeNode(String nodeId)Removing a node automatically removes all connections to and from that node, and removes the node from any group annotations.
moveNode
Move a node by a delta offset.
void moveNode(String nodeId, Offset delta)moveSelectedNodes
Move all selected nodes by a delta offset.
void moveSelectedNodes(Offset delta)setNodeSize
Update a node's size.
void setNodeSize(String nodeId, Size size)setNodePorts
Replace a node's ports.
void setNodePorts(String nodeId, {
List<Port>? inputPorts,
List<Port>? outputPorts,
})addInputPort / addOutputPort
Add ports to an existing node.
void addInputPort(String nodeId, Port port)
void addOutputPort(String nodeId, Port port)removePort
Remove a port and all its connections.
void removePort(String nodeId, String portId)getNode
Get a node by ID.
Node<T>? getNode(String nodeId)Connection Operations
addConnection
Create a connection between ports.
void addConnection(Connection connection)Example:
final connection = Connection(
id: 'conn-1',
sourceNodeId: 'node-1',
sourcePortId: 'out-1',
targetNodeId: 'node-2',
targetPortId: 'in-1',
);
controller.addConnection(connection);removeConnection
Remove a connection by ID.
void removeConnection(String connectionId)getConnectionsForNode
Get all connections for a node.
List<Connection> getConnectionsForNode(String nodeId)connections
Access all connections (read-only list).
List<Connection> get connectionsControl Points
Connections support user-defined control points for custom routing.
addControlPoint
void addControlPoint(String connectionId, Offset position, {int? index})updateControlPoint
void updateControlPoint(String connectionId, int index, Offset position)removeControlPoint
void removeControlPoint(String connectionId, int index)clearControlPoints
void clearControlPoints(String connectionId)Selection
selectNode
Select a node. Use toggle: true for multi-select behavior.
void selectNode(String nodeId, {bool toggle = false})selectNodes
Select multiple nodes.
void selectNodes(List<String> nodeIds, {bool toggle = false})selectConnection
Select a connection.
void selectConnection(String connectionId, {bool toggle = false})clearSelection
Clear all selections (nodes, connections, annotations).
void clearSelection()clearNodeSelection / clearConnectionSelection
Clear specific selection types.
void clearNodeSelection()
void clearConnectionSelection()selectedNodeIds
Get IDs of selected nodes.
Set<String> get selectedNodeIdshasSelection
Check if anything is selected.
bool get hasSelectionisNodeSelected
Check if a specific node is selected.
bool isNodeSelected(String nodeId)Viewport
viewport
Current viewport state.
GraphViewport get viewportReturns GraphViewport with:
x,y- Pan offsetzoom- Zoom level
setViewport
Set viewport directly.
void setViewport(GraphViewport viewport)panBy
Pan viewport by a delta.
void panBy(Offset delta)zoomBy
Zoom by a delta amount (positive = zoom in).
void zoomBy(double delta)zoomTo
Set zoom to a specific level.
void zoomTo(double zoom)fitToView
Fit all content in the viewport.
void fitToView()centerOnNode
Center viewport on a specific node.
void centerOnNode(String nodeId)Graph Operations
loadGraph
Load a complete graph (nodes, connections, annotations, viewport).
void loadGraph(NodeGraph<T> graph)exportGraph
Export current graph state.
NodeGraph<T> exportGraph()Example:
// Save
final graph = controller.exportGraph();
final json = graph.toJson((data) => data.toJson());
await saveToFile(jsonEncode(json));
// Load
final json = jsonDecode(await loadFromFile());
final graph = NodeGraph.fromJson(json, (map) => MyData.fromJson(map));
controller.loadGraph(graph);clearGraph
Remove all nodes, connections, and annotations.
void clearGraph()Alignment & Distribution
alignNodes
Align nodes to a specific edge or center.
void alignNodes(List<String> nodeIds, NodeAlignment alignment)| Alignment | Description |
|---|---|
NodeAlignment.left | Align to left edge |
NodeAlignment.right | Align to right edge |
NodeAlignment.top | Align to top edge |
NodeAlignment.bottom | Align to bottom edge |
NodeAlignment.horizontalCenter | Center horizontally |
NodeAlignment.verticalCenter | Center vertically |
distributeNodesHorizontally / distributeNodesVertically
Distribute nodes evenly.
void distributeNodesHorizontally(List<String> nodeIds)
void distributeNodesVertically(List<String> nodeIds)Annotations
addAnnotation / removeAnnotation
void addAnnotation(Annotation annotation)
void removeAnnotation(String annotationId)getAnnotation
Annotation? getAnnotation(String annotationId)selectAnnotation / clearAnnotationSelection
void selectAnnotation(String annotationId, {bool toggle = false})
void clearAnnotationSelection()Factory Methods
Convenience methods for creating common annotation types:
StickyAnnotation createStickyNote({
required Offset position,
required String text,
String? id,
double width = 200.0,
double height = 100.0,
Color? color,
})
GroupAnnotation createGroupAnnotation({
required List<String> nodeIds,
String? id,
String? label,
Color? color,
})
MarkerAnnotation createMarker({
required Offset position,
required String label,
String? id,
Color? color,
})Lifecycle
dispose
Dispose the controller and release resources.
void dispose()Always call dispose() when the controller is no longer needed to prevent memory leaks.
Complete Example
class WorkflowEditor extends StatefulWidget {
@override
State<WorkflowEditor> createState() => _WorkflowEditorState();
}
class _WorkflowEditorState extends State<WorkflowEditor> {
late final NodeFlowController<WorkflowData> controller;
@override
void initState() {
super.initState();
controller = NodeFlowController<WorkflowData>();
_setupGraph();
}
void _setupGraph() {
controller.addNode(Node(
id: 'start',
position: Offset(100, 100),
size: Size(120, 60),
data: WorkflowData(label: 'Start', type: 'trigger'),
outputPorts: [Port(id: 'start-out', name: 'Next')],
));
controller.addNode(Node(
id: 'process',
position: Offset(300, 100),
size: Size(120, 60),
data: WorkflowData(label: 'Process', type: 'action'),
inputPorts: [Port(id: 'process-in', name: 'Input')],
outputPorts: [Port(id: 'process-out', name: 'Output')],
));
controller.addConnection(Connection(
id: 'conn-1',
sourceNodeId: 'start',
sourcePortId: 'start-out',
targetNodeId: 'process',
targetPortId: 'process-in',
));
WidgetsBinding.instance.addPostFrameCallback((_) {
controller.fitToView();
});
}
void _addNode() {
final id = 'node-${DateTime.now().millisecondsSinceEpoch}';
controller.addNode(Node(
id: id,
position: Offset(200, 200),
size: Size(120, 60),
data: WorkflowData(label: 'New Node', type: 'action'),
inputPorts: [Port(id: '$id-in', name: 'Input')],
outputPorts: [Port(id: '$id-out', name: 'Output')],
));
controller.selectNode(id);
}
void _deleteSelected() {
for (final nodeId in controller.selectedNodeIds.toList()) {
controller.removeNode(nodeId);
}
}
void _saveGraph() async {
final graph = controller.exportGraph();
final json = graph.toJson((data) => data.toJson());
// Save to file or API
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Workflow Editor'),
actions: [
IconButton(icon: Icon(Icons.add), onPressed: _addNode),
IconButton(icon: Icon(Icons.delete), onPressed: _deleteSelected),
IconButton(icon: Icon(Icons.fit_screen), onPressed: controller.fitToView),
IconButton(icon: Icon(Icons.save), onPressed: _saveGraph),
],
),
body: NodeFlowEditor<WorkflowData>(
controller: controller,
theme: NodeFlowTheme.light,
nodeBuilder: (context, node) => Center(child: Text(node.data.label)),
),
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}