completeConnectionDrag method
Completes a connection drag by creating the connection.
Call this from PortWidget's GestureDetector.onPanEnd when over a valid target.
Parameters:
targetNodeId: The ID of the target nodetargetPortId: The ID of the target port
Returns the created connection, or null if creation failed.
Implementation
Connection? completeConnectionDrag({
required String targetNodeId,
required String targetPortId,
}) {
final temp = interaction.temporaryConnection.value;
if (temp == null) {
// Fire connection end event with failure
events.connection?.onConnectEnd?.call(false);
return null;
}
// Validate connection before creating
final validationResult = canConnect(
targetNodeId: targetNodeId,
targetPortId: targetPortId,
);
if (!validationResult.allowed) {
cancelConnectionDrag();
return null;
}
// Reset highlighted port
final highlightedNode = _nodes[targetNodeId];
if (highlightedNode != null) {
final highlightedPort = highlightedNode.allPorts
.where((p) => p.id == targetPortId)
.firstOrNull;
if (highlightedPort != null) {
runInAction(() => highlightedPort.highlighted.value = false);
}
}
// Determine actual source/target based on port direction:
// - If started from output: start is source, target is target
// - If started from input: target is source, start is target
final String sourceNodeId;
final String sourcePortId;
final String actualTargetNodeId;
final String actualTargetPortId;
if (temp.isStartFromOutput) {
// Output → Input: start is source, target is target
sourceNodeId = temp.startNodeId;
sourcePortId = temp.startPortId;
actualTargetNodeId = targetNodeId;
actualTargetPortId = targetPortId;
} else {
// Input ← Output: target is source, start is target
sourceNodeId = targetNodeId;
sourcePortId = targetPortId;
actualTargetNodeId = temp.startNodeId;
actualTargetPortId = temp.startPortId;
}
// Check if target port allows multiple connections
final targetNode = _nodes[actualTargetNodeId];
if (targetNode != null) {
final targetPort = targetNode.allPorts
.where((p) => p.id == actualTargetPortId)
.firstOrNull;
if (targetPort != null && !targetPort.multiConnections) {
// Remove existing connections to target port
final connectionsToRemove = _connections
.where(
(conn) =>
conn.targetNodeId == actualTargetNodeId &&
conn.targetPortId == actualTargetPortId,
)
.toList();
runInAction(() {
for (final connection in connectionsToRemove) {
removeConnection(connection.id);
}
});
}
}
// Create the new connection
final createdConnection = runInAction(() {
final connection = Connection(
id: '${sourceNodeId}_${sourcePortId}_${actualTargetNodeId}_$actualTargetPortId',
sourceNodeId: sourceNodeId,
sourcePortId: sourcePortId,
targetNodeId: actualTargetNodeId,
targetPortId: actualTargetPortId,
);
addConnection(connection);
// Clear temporary connection state and re-enable panning
// Cursor is derived from state via Observer in widget MouseRegions
interaction.temporaryConnection.value = null;
interaction.panEnabled.value = true;
return connection;
});
// Fire connection end event with success
events.connection?.onConnectEnd?.call(true);
return createdConnection;
}