Interatividade e Manipulação
Sistema de Gestos
stateDiagram-v2
[*] --> Idle
Idle --> Dragging: onPanStart
Idle --> Resizing: onPanStart(handle)
Dragging --> Dragging: onPanUpdate
Resizing --> Resizing: onPanUpdate
Dragging --> Idle: onPanEnd
Resizing --> Idle: onPanEnd
Drag and Drop
Estado de Drag
class _JsonCanvasViewerState extends State<JsonCanvasViewer> {
int? _draggedElementIndex; // Elemento sendo arrastado
Offset? _dragStartPosition; // Posição inicial
bool _isDragging = false; // Flag de estado
}
Fluxo Completo
1. Inicialização
onPanStart
Salva índice e posição inicial
2. Movimento
onPanUpdate
Atualiza posição com compensação de escala
3. Notificação
onElementMoved
Callback para atualizar JSON
4. Finalização
onPanEnd
Limpa estado e flags
Atualização Durante Drag
void _onPanUpdate(DragUpdateDetails details, int elementIndex) {
final delta = details.localPosition - _dragStartPosition!;
final element = elements[elementIndex];
// Compensa escala
final scaleFactor = _getElementScaleFactor();
final newX = currentX + (delta.dx / scaleFactor);
final newY = currentY + (delta.dy / scaleFactor);
// Atualiza elemento
element['x'] = newX;
element['y'] = newY;
// Notifica callback
Future.microtask(() {
widget.onElementMoved?.call(elementIndex, newX, newY);
});
}
Resize
Handles de Resize
top-left
Canto superior esquerdo
top-right
Canto superior direito
bottom-left
Canto inferior esquerdo
bottom-right
Canto inferior direito
Lógica de Resize
switch (_resizeHandle!) {
case 'top-left':
newWidth = currentWidth - deltaX;
newHeight = currentHeight - deltaY;
newX = currentX + deltaX;
newY = currentY + deltaY;
break;
case 'bottom-right':
newWidth = currentWidth + deltaX;
newHeight = currentHeight + deltaY;
break;
}
// Garante dimensões mínimas
newWidth = math.max(newWidth, 10.0);
newHeight = math.max(newHeight, 10.0);
Visual Feedback
Elemento Arrastado
Borda azul de 2px
Texto Arrastado
Background azul com 10% opacidade
Cursor
Grab durante hover, grabbing durante drag
Handles Visíveis
Aparecem apenas quando elemento selecionado
Atualização do JSON
Fluxo Bidirecional
sequenceDiagram
participant U as Usuário
participant JCV as JsonCanvasViewer
participant CV as CanvasViewer
participant HP as HomePage
participant JE as JsonEditor
U->>JCV: Arrasta elemento
JCV->>JCV: Atualiza posição local
JCV->>CV: onElementMoved(index, x, y)
CV->>CV: Atualiza Map JSON
CV->>HP: onJsonUpdated(updatedJson)
HP->>HP: setState(_jsonData)
HP->>JE: externalJsonData atualizado
JE->>U: Editor exibe JSON atualizado
Callback para CanvasViewerWidget
void _updateJsonOnElementMove(
int elementIndex,
double newX,
double newY,
{double? newWidth, double? newHeight}
) {
final decoded = const JsonDecoder().convert(widget.jsonData);
final elements = decoded['elements'] as List<dynamic>;
if (elementIndex < elements.length) {
final element = elements[elementIndex];
element['x'] = newX;
element['y'] = newY;
if (newWidth != null) element['width'] = newWidth;
if (newHeight != null) element['height'] = newHeight;
const encoder = JsonEncoder.withIndent(' ');
final updatedJson = encoder.convert(decoded);
widget.onJsonUpdated?.call(updatedJson);
}
}
Limitações
Elementos Não Redimensionáveis
text
Apenas drag (sem resize)
icon
Tamanho controlado por size
line
Largura/espessura fixas
Conversão de Centralização
Quando texto é arrastado, valores “center” são convertidos para números:
if (element['x'] is String) {
element['x'] = newX; // Converte "center" para número
}
element.remove('centerX'); // Remove flag