import 'package:flutter/material.dart'; class SnapToGridCanvas extends StatefulWidget { @override _SnapToGridCanvasState createState() => _SnapToGridCanvasState(); } class _SnapToGridCanvasState extends State { final double snappingThreshold = 10.0; final List elements = []; // Store elements' bounds Offset selectedElementPosition = Offset(100, 100); Size selectedElementSize = Size(100, 100); Offset? snapToElementOrCanvas(Offset newPosition, Size canvasSize) { for (var element in elements) { // Snap to other elements if ((newPosition.dx - element.right).abs() <= snappingThreshold) { return Offset(element.right, newPosition.dy); // Snap to the right of another element } else if ((newPosition.dx - element.left).abs() <= snappingThreshold) { return Offset(element.left, newPosition.dy); // Snap to the left of another element } else if ((newPosition.dy - element.bottom).abs() <= snappingThreshold) { return Offset(newPosition.dx, element.bottom); // Snap to the bottom of another element } else if ((newPosition.dy - element.top).abs() <= snappingThreshold) { return Offset(newPosition.dx, element.top); // Snap to the top of another element } } // Snap to canvas center final canvasCenter = Offset(canvasSize.width / 2, canvasSize.height / 2); if ((newPosition.dx - canvasCenter.dx).abs() <= snappingThreshold) { return Offset(canvasCenter.dx, newPosition.dy); // Snap horizontally to canvas center } else if ((newPosition.dy - canvasCenter.dy).abs() <= snappingThreshold) { return Offset(newPosition.dx, canvasCenter.dy); // Snap vertically to canvas center } return null; // No snapping } @override Widget build(BuildContext context) { final canvasSize = MediaQuery.of(context).size; return Scaffold( appBar: AppBar(title: Text("Snap to Grid Canvas")), body: GestureDetector( onPanUpdate: (details) { setState(() { final newPosition = selectedElementPosition + details.delta; // Check for snapping final snappedPosition = snapToElementOrCanvas(newPosition, canvasSize); if (snappedPosition != null) { selectedElementPosition = snappedPosition; } else { selectedElementPosition = newPosition; } }); }, child: Stack( children: [ // Canvas background Container(color: Colors.grey[200]), // Render all elements ...elements.map((element) { return Positioned( left: element.left, top: element.top, child: Container( width: element.width, height: element.height, color: Colors.blue, ), ); }).toList(), // Selected element Positioned( left: selectedElementPosition.dx, top: selectedElementPosition.dy, child: Container( width: selectedElementSize.width, height: selectedElementSize.height, color: Colors.red, child: Center(child: Text("Drag Me")), ), ), ], ), ), ); } }