Port Shapes

Customize the visual appearance of connection ports

Port Shapes

Port shapes define how connection points appear on your nodes. Vyuh Node Flow provides six built-in shapes, each serving different visual and functional purposes.

All port shapes displayed together

Available Shapes

Circle

The default port shape - a simple circle. Universal and works in all contexts.

Port(
  id: 'port-1',
  name: 'Output',
  position: PortPosition.right,
  shape: PortShapes.circle, // Default
)

Circle port shape

Best for:

  • General purpose
  • Data flow diagrams
  • When no specific meaning is needed

Characteristics:

  • Symmetrical in all directions
  • Easy to recognize and interact with
  • Works with all port positions (left, right, top, bottom)

Square

A rectangular port shape. Good for technical and structured diagrams.

Port(
  id: 'port-1',
  name: 'Control',
  position: PortPosition.right,
  shape: PortShapes.square,
)

Square port shape

Best for:

  • Control flow ports
  • Event triggers
  • Grid-aligned designs

Characteristics:

  • Sharp, technical appearance
  • Aligns well with rectangular nodes
  • Clear visual distinction from circular ports

Diamond

A diamond (rotated square) shape. Excellent for conditional or decision points.

Port(
  id: 'condition-port',
  name: 'Decision',
  position: PortPosition.right,
  shape: PortShapes.diamond,
)

Diamond port shape

Best for:

  • Conditional/decision ports
  • Branch points
  • Special connection types

Characteristics:

  • Visually distinct
  • Suggests branching or decisions
  • Common in flowchart conventions

Triangle

A triangular shape that points in the direction of the port position. Shows directionality.

Port(
  id: 'output-port',
  name: 'Output',
  position: PortPosition.right,
  shape: PortShapes.triangle, // Points right
)

Triangle port shapes in all directions

Best for:

  • Directional data flow
  • Output ports
  • Signal paths

Characteristics:

  • Directional - points toward port position
  • Strong visual indicator of flow direction
  • Orientation automatically matches port position:
    • PortPosition.right → points right (▶)
    • PortPosition.left → points left (◀)
    • PortPosition.top → points up (▲)
    • PortPosition.bottom → points down (▼)

Capsule Half

A half-capsule (semi-circle) shape that opens toward the connection direction.

Port(
  id: 'connector',
  name: 'Socket',
  position: PortPosition.left,
  shape: PortShapes.capsuleHalf, // Opens left
)

Capsule half port shapes

Best for:

  • Socket/plug metaphors
  • Interface connection points
  • Hardware connection diagrams

Characteristics:

  • Suggests physical connection
  • Opens in the direction of port position
  • Visually suggests "plugging in"

None

An invisible port shape. The port is functional but not visually rendered.

Port(
  id: 'invisible-port',
  name: 'Hidden',
  position: PortPosition.right,
  shape: PortShapes.none,
)

Best for:

  • Minimalist designs
  • When connections should appear to connect directly to nodes
  • Hidden functionality

Characteristics:

  • Fully functional for connections
  • No visual representation
  • Connection endpoints still render normally

Setting Port Shapes

Per-Port Configuration

Node(
  id: 'node-1',
  inputPorts: [
    Port(
      id: 'in-1',
      name: 'Data Input',
      position: PortPosition.left,
      shape: PortShapes.circle,
    ),
    Port(
      id: 'trigger',
      name: 'Trigger',
      position: PortPosition.top,
      shape: PortShapes.square,
    ),
  ],
  outputPorts: [
    Port(
      id: 'out-1',
      name: 'Output',
      position: PortPosition.right,
      shape: PortShapes.triangle,
    ),
    Port(
      id: 'error',
      name: 'Error',
      position: PortPosition.bottom,
      shape: PortShapes.diamond,
    ),
  ],
)

Type-Based Shapes

Use different shapes for different port types:

Port createPort({
  required String id,
  required String name,
  required PortPosition position,
  required String portType,
}) {
  PortShape shape;

  switch (portType) {
    case 'data':
      shape = PortShapes.circle;
      break;
    case 'control':
      shape = PortShapes.square;
      break;
    case 'event':
      shape = PortShapes.triangle;
      break;
    case 'condition':
      shape = PortShapes.diamond;
      break;
    case 'socket':
      shape = PortShapes.capsuleHalf;
      break;
    default:
      shape = PortShapes.circle;
  }

  return Port(
    id: id,
    name: name,
    position: position,
    shape: shape,
  );
}

Shape Comparison

ShapeUse CaseDirectionalityVisual Weight
CircleGeneral purposeNoneMedium
SquareControl flowNoneMedium
DiamondDecisionsNoneHigh
TriangleDirectional flowYesHigh
Capsule HalfConnectionsYesMedium
NoneMinimalistN/ANone

Styling Port Shapes

Customize appearance through theme:

NodeFlowEditor(
  theme: NodeFlowTheme(
    portTheme: PortTheme(
      size: 12,                      // Port diameter
      color: Colors.blue,            // Fill color
      hoverColor: Colors.blue[700]!, // Hover state
      borderColor: Colors.white,     // Border color
      borderWidth: 2,                // Border thickness
    ),
  ),
)

Shape Orientation

Directional shapes (triangle, capsuleHalf) automatically orient based on port position:

// Points in direction of port
Port(
  position: PortPosition.right,
  shape: PortShapes.triangle, // ▶
)

Port(
  position: PortPosition.left,
  shape: PortShapes.triangle, // ◀
)

Port(
  position: PortPosition.top,
  shape: PortShapes.triangle, // ▲
)

Port(
  position: PortPosition.bottom,
  shape: PortShapes.triangle, // ▼
)
// Opens toward connection direction
Port(
  position: PortPosition.left,
  shape: PortShapes.capsuleHalf, // Opens left ⊂
)

Port(
  position: PortPosition.right,
  shape: PortShapes.capsuleHalf, // Opens right ⊃
)

Visual Conventions

Consider these common conventions when choosing shapes:

Data Flow Diagrams

  • Circle: Data ports
  • Triangle: Output direction indicators
  • None: Clean, minimal design

Control Flow / BPMN

  • Square: Event/message ports
  • Diamond: Gateway/decision points
  • Circle: Standard sequence flow

Circuit Diagrams

  • Capsule Half: Pin connections
  • Circle: General connection points
  • Square: Digital signal ports

Creating Custom Port Shapes

Extend PortShape to create custom shapes:

class StarPortShape extends PortShape {
  const StarPortShape();

  @override
  void paint(
    Canvas canvas,
    Offset center,
    double size,
    Paint fillPaint,
    Paint? borderPaint, {
    ShapeOrientation? orientation,
  }) {
    final path = Path();
    final radius = size / 2;

    // Draw 5-point star
    for (int i = 0; i < 5; i++) {
      final angle = (i * 4 * pi / 5) - pi / 2;
      final x = center.dx + radius * cos(angle);
      final y = center.dy + radius * sin(angle);

      if (i == 0) {
        path.moveTo(x, y);
      } else {
        path.lineTo(x, y);
      }
    }
    path.close();

    canvas.drawPath(path, fillPaint);
    if (borderPaint != null) {
      canvas.drawPath(path, borderPaint);
    }
  }

  @override
  String get typeName => 'star';
}

// Usage
Port(
  id: 'special-port',
  name: 'Special',
  position: PortPosition.right,
  shape: const StarPortShape(),
)

Learn More: See the API Reference for detailed guidance on creating custom port shapes.

Best Practices

  1. Consistency: Use the same shape for the same port type across all nodes
  2. Meaning: Choose shapes that convey meaning (diamonds for decisions, triangles for outputs)
  3. Contrast: Use different shapes to distinguish different port types
  4. Size: Keep port sizes between 8-16 pixels for good usability
  5. Color Coding: Combine shape with color for maximum clarity

Interactive Behavior

All port shapes support the same interaction features:

  • Hover: Visual feedback when mouse is over port
  • Connection dragging: Start new connections from source ports
  • Multi-connections: Allow multiple connections based on port settings
  • Validation: Connection validation works regardless of shape

See Also

On this page