Skip to content

Commit 606139c

Browse files
authored
Gestures (#19)
* feat: leveraging gesture detectors to handle nodes, ports, annotations. Only connections leverage the top-level listener. This ensures that we can have widgets and interactive widgets inside nodes and annotations, and have it even work outside the original stack bounds. * feat: enhance interaction handling with connection hover states and streamline zoom-related logic * fix: connection validation example * chore(release): publish packages - [email protected]
1 parent c7cd648 commit 606139c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+3870
-1749
lines changed

packages/demo/lib/example_registry.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ import 'examples/advanced/workbench.dart';
2222
import 'examples/basics/callbacks.dart';
2323
import 'examples/basics/controlling_nodes.dart';
2424
import 'examples/basics/dynamic_ports.dart';
25+
// Connections examples
26+
import 'examples/basics/interactive_widgets.dart';
2527
import 'examples/basics/minimap.dart';
2628
import 'examples/basics/node_shapes.dart';
2729
import 'examples/basics/port_labels.dart';
28-
// Connections examples
2930
import 'examples/basics/ports.dart';
3031
import 'examples/basics/simple.dart';
3132

@@ -101,6 +102,14 @@ class ExampleRegistry {
101102
icon: Icons.monitor_heart,
102103
builder: (_) => const CallbacksExample(),
103104
),
105+
Example(
106+
id: 'interactive-widgets',
107+
title: 'Interactive Node Widgets',
108+
description:
109+
'Test buttons, text fields, and sliders inside nodes - drag nodes outside bounds to verify gestures work',
110+
icon: Icons.touch_app,
111+
builder: (_) => const InteractiveWidgetsExample(),
112+
),
104113
],
105114
),
106115

packages/demo/lib/examples/advanced/animated_connections.dart

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ class _AnimatedConnectionsExampleState
263263
position: const Offset(50, 180),
264264
size: const Size(150, 80),
265265
data: {'label': 'Data Source'},
266-
outputPorts: const [
266+
outputPorts: [
267267
Port(
268268
id: 'out_right',
269269
name: 'Output',
@@ -291,15 +291,15 @@ class _AnimatedConnectionsExampleState
291291
position: const Offset(350, 180),
292292
size: const Size(150, 80),
293293
data: {'label': 'Processor Middle'},
294-
inputPorts: const [
294+
inputPorts: [
295295
Port(
296296
id: 'in_left',
297297
name: 'Input',
298298
position: PortPosition.left,
299299
offset: Offset(-2, 40),
300300
),
301301
],
302-
outputPorts: const [
302+
outputPorts: [
303303
Port(
304304
id: 'out_right',
305305
name: 'Output',
@@ -315,7 +315,7 @@ class _AnimatedConnectionsExampleState
315315
position: const Offset(650, 180),
316316
size: const Size(150, 80),
317317
data: {'label': 'Data Sync'},
318-
inputPorts: const [
318+
inputPorts: [
319319
Port(
320320
id: 'in_left',
321321
name: 'Input',
@@ -344,15 +344,15 @@ class _AnimatedConnectionsExampleState
344344
position: const Offset(350, 30),
345345
size: const Size(150, 80),
346346
data: {'label': 'Processor Top'},
347-
inputPorts: const [
347+
inputPorts: [
348348
Port(
349349
id: 'in_bottom',
350350
name: 'Input',
351351
position: PortPosition.bottom,
352352
offset: Offset(75, 2),
353353
),
354354
],
355-
outputPorts: const [
355+
outputPorts: [
356356
Port(
357357
id: 'out_top',
358358
name: 'Output',
@@ -368,15 +368,15 @@ class _AnimatedConnectionsExampleState
368368
position: const Offset(350, 330),
369369
size: const Size(150, 80),
370370
data: {'label': 'Processor Bottom'},
371-
inputPorts: const [
371+
inputPorts: [
372372
Port(
373373
id: 'in_top',
374374
name: 'Input',
375375
position: PortPosition.top,
376376
offset: Offset(75, -2),
377377
),
378378
],
379-
outputPorts: const [
379+
outputPorts: [
380380
Port(
381381
id: 'out_bottom',
382382
name: 'Output',

packages/demo/lib/examples/advanced/connection_labels.dart

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ class _ConnectionLabelsStore {
164164
position: const Offset(100, 150),
165165
data: {'label': 'Source Node'},
166166
size: const Size(150, 80),
167-
inputPorts: const [],
168-
outputPorts: const [
167+
inputPorts: [],
168+
outputPorts: [
169169
Port(
170170
id: 'out-1',
171171
name: 'Out',
@@ -180,24 +180,24 @@ class _ConnectionLabelsStore {
180180
position: const Offset(400, 150),
181181
data: {'label': 'Target Node'},
182182
size: const Size(150, 80),
183-
inputPorts: const [
183+
inputPorts: [
184184
Port(
185185
id: 'in-1',
186186
name: 'In',
187187
position: PortPosition.left,
188188
offset: Offset(-2, 40),
189189
),
190190
],
191-
outputPorts: const [],
191+
outputPorts: [],
192192
),
193193
Node<Map<String, dynamic>>(
194194
id: 'node-3',
195195
type: 'simple',
196196
position: const Offset(100, 300),
197197
data: {'label': 'Input A'},
198198
size: const Size(150, 80),
199-
inputPorts: const [],
200-
outputPorts: const [
199+
inputPorts: [],
200+
outputPorts: [
201201
Port(
202202
id: 'out-3',
203203
name: 'Out',
@@ -212,15 +212,15 @@ class _ConnectionLabelsStore {
212212
position: const Offset(400, 300),
213213
data: {'label': 'Output B'},
214214
size: const Size(150, 80),
215-
inputPorts: const [
215+
inputPorts: [
216216
Port(
217217
id: 'in-4',
218218
name: 'In',
219219
position: PortPosition.left,
220220
offset: Offset(-2, 40),
221221
),
222222
],
223-
outputPorts: const [],
223+
outputPorts: [],
224224
),
225225
];
226226

packages/demo/lib/examples/advanced/editable_connections.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class _EditableConnectionsExampleState
5252
position: const Offset(100, 100),
5353
data: {'label': 'Input A'},
5454
size: const Size(150, 100),
55-
outputPorts: const [
55+
outputPorts: [
5656
Port(
5757
id: 'output',
5858
name: 'Out',
@@ -67,7 +67,7 @@ class _EditableConnectionsExampleState
6767
position: const Offset(100, 250),
6868
data: {'label': 'Input B'},
6969
size: const Size(150, 100),
70-
outputPorts: const [
70+
outputPorts: [
7171
Port(
7272
id: 'output',
7373
name: 'Out',
@@ -82,7 +82,7 @@ class _EditableConnectionsExampleState
8282
position: const Offset(400, 150),
8383
data: {'label': 'Process'},
8484
size: const Size(150, 120),
85-
inputPorts: const [
85+
inputPorts: [
8686
Port(
8787
id: 'input1',
8888
name: 'In 1',
@@ -96,7 +96,7 @@ class _EditableConnectionsExampleState
9696
offset: Offset(-2, 80),
9797
),
9898
],
99-
outputPorts: const [
99+
outputPorts: [
100100
Port(
101101
id: 'output',
102102
name: 'Out',
@@ -111,7 +111,7 @@ class _EditableConnectionsExampleState
111111
position: const Offset(700, 175),
112112
data: {'label': 'Output'},
113113
size: const Size(150, 100),
114-
inputPorts: const [
114+
inputPorts: [
115115
Port(
116116
id: 'input',
117117
name: 'In',

packages/demo/lib/examples/advanced/serialization.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class _SerializationExampleState extends State<SerializationExample> {
6565
position: const Offset(100, 150),
6666
size: const Size(150, 80),
6767
data: {'label': 'Input', 'value': 42},
68-
outputPorts: const [
68+
outputPorts: [
6969
Port(
7070
id: 'out',
7171
name: 'Output',
@@ -81,15 +81,15 @@ class _SerializationExampleState extends State<SerializationExample> {
8181
position: const Offset(350, 100),
8282
size: const Size(150, 100),
8383
data: {'label': 'Process A', 'operation': 'multiply'},
84-
inputPorts: const [
84+
inputPorts: [
8585
Port(
8686
id: 'in',
8787
name: 'Input',
8888
position: PortPosition.left,
8989
offset: Offset(-2, 50), // Vertical center of 100 height
9090
),
9191
],
92-
outputPorts: const [
92+
outputPorts: [
9393
Port(
9494
id: 'out',
9595
name: 'Result',
@@ -105,15 +105,15 @@ class _SerializationExampleState extends State<SerializationExample> {
105105
position: const Offset(350, 250),
106106
size: const Size(150, 100),
107107
data: {'label': 'Process B', 'operation': 'add'},
108-
inputPorts: const [
108+
inputPorts: [
109109
Port(
110110
id: 'in',
111111
name: 'Input',
112112
position: PortPosition.left,
113113
offset: Offset(-2, 50), // Vertical center of 100 height
114114
),
115115
],
116-
outputPorts: const [
116+
outputPorts: [
117117
Port(
118118
id: 'out',
119119
name: 'Result',
@@ -129,7 +129,7 @@ class _SerializationExampleState extends State<SerializationExample> {
129129
position: const Offset(600, 150),
130130
size: const Size(150, 80),
131131
data: {'label': 'Output', 'format': 'json'},
132-
inputPorts: const [
132+
inputPorts: [
133133
Port(
134134
id: 'in1',
135135
name: 'Input 1',

packages/demo/lib/examples/advanced/theming.dart

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ class _ThemingExampleState extends State<ThemingExample> {
166166
id: 'in-bottom',
167167
name: 'Bottom',
168168
position: PortPosition.bottom,
169+
type: PortType.input,
169170
offset: const Offset(75, 2), // Horizontal center at mid-width
170171
shape: _selectedPortShape,
171172
showLabel: true,
@@ -285,32 +286,34 @@ class _ThemingExampleState extends State<ThemingExample> {
285286
/// Custom port builder that colors ports based on whether they're input or output
286287
Widget _buildCustomPort(
287288
BuildContext context,
289+
NodeFlowController<Map<String, dynamic>> controller,
288290
Node<Map<String, dynamic>> node,
289291
Port port,
290292
bool isOutput,
291293
bool isConnected,
292-
bool isHighlighted,
294+
Rect nodeBounds,
293295
) {
294296
final portTheme = _theme.portTheme;
295297

296298
// Use different colors for input vs output ports
299+
// Note: Highlighting is handled via Port.highlighted observable
297300
final baseColor = isOutput ? Colors.green : Colors.blue;
298-
final color = isHighlighted
299-
? baseColor.shade700
300-
: isConnected
301-
? baseColor.shade400
302-
: baseColor.shade200;
301+
final color = isConnected ? baseColor.shade400 : baseColor.shade200;
303302

304303
return PortWidget(
305304
port: port,
306305
theme: portTheme,
306+
controller: controller,
307+
nodeId: node.id,
308+
isOutput: isOutput,
309+
nodeBounds: nodeBounds,
307310
isConnected: isConnected,
308-
isHighlighted: isHighlighted,
309311
color: color,
310312
connectedColor: baseColor.shade400,
311-
snappingColor: baseColor.shade700,
313+
highlightColor: baseColor.shade300,
314+
highlightBorderColor: baseColor.shade900,
312315
borderColor: baseColor.shade800,
313-
borderWidth: isHighlighted ? 2.0 : 1.0,
316+
borderWidth: 1.0,
314317
);
315318
}
316319

@@ -320,6 +323,7 @@ class _ThemingExampleState extends State<ThemingExample> {
320323
Connection connection,
321324
ConnectionLabel label,
322325
Rect position,
326+
void Function()? onTap,
323327
) {
324328
final labelTheme = _theme.labelTheme;
325329

0 commit comments

Comments
 (0)