import 'dart:ui_web'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/common/custom_expansion_tile.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/view/sidebar_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; class SpaceManagementPage extends StatefulWidget { @override SpaceManagementPageState createState() => SpaceManagementPageState(); } class SpaceManagementPageState extends State { // Store created spaces List spaces = []; List connections = []; void _handleCommunitySelection(String community) { // Handle community selection here print("Selected Community: $community"); } @override Widget build(BuildContext context) { Size screenSize = MediaQuery.of(context).size; return Scaffold( backgroundColor: ColorsManager.whiteColors, appBar: AppBar( title: const Text('Space Management'), ), body: Stack( clipBehavior: Clip.none, children: [ Row( children: [ // Sidebar for navigation and community list SidebarWidget( onCommunitySelected: _handleCommunitySelection, ), // Right Side: Community Structure Area Expanded( child: Container( decoration: const BoxDecoration( color: ColorsManager.whiteColors, border: Border( left: BorderSide( color: Colors.white, width: 1.0), // Light left border to match ), ), // Background color for canvas child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), width: double.infinity, decoration: BoxDecoration( color: ColorsManager.whiteColors, boxShadow: [ BoxShadow( color: Colors.black .withOpacity(0.2), // Subtle shadow spreadRadius: 0, // No spread blurRadius: 8, // Softer shadow edges offset: Offset(0, 4), // Shadow only on the bottom ), ], ), child: Text( 'Community Structure', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), ), ), // Use Expanded to ensure InteractiveViewer takes the available space Expanded( child: InteractiveViewer( boundaryMargin: const EdgeInsets.all(500), // Adjusted to 500 minScale: 0.5, // Minimum zoom scale maxScale: 2.5, // Maximum zoom scale panEnabled: true, // Enable panning scaleEnabled: true, // Enable zooming child: Container( width: 2000, // Large canvas height: 2000, // Large canvas color: Colors.transparent, // Transparent background child: Stack( clipBehavior: Clip.none, children: [ // Draw lines using a CustomPaint widget CustomPaint( size: Size(2000, 2000), // Explicit canvas size painter: CurvedLinePainter(connections), ), Center( child: spaces.isEmpty ? AddSpaceButton( onTap: () { _showCreateSpaceDialog(screenSize); }, ) : Stack( children: spaces .asMap() .entries .map((entry) => _buildSpaceCard( entry.key, screenSize)) .toList(), ), ), ], ), ), ), ), ], ), ), ), ], ), Positioned( top: 0, bottom: 0, left: 300, // Align with the sidebar's width width: 8, // Width of the gradient border child: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [ Color(0x3F000000).withOpacity(0.1), // Light gray Colors.transparent, // Transparent fade ], ), ), ), ), ], ), ); } // Function to open the Create Space dialog void _showCreateSpaceDialog(Size screenSize, {Offset? position, int? parentIndex, String? direction}) { showDialog( context: context, builder: (BuildContext context) { return CreateSpaceDialog( onCreateSpace: (String name, String icon) { setState(() { // Set the first space in the center or use passed position Offset centerPosition = position ?? Offset( screenSize.width / 2 - 75, // Center horizontally screenSize.height / 2 - 100, // Slightly above the center vertically ); SpaceData newSpace = SpaceData(name: name, icon: icon, position: centerPosition); spaces.add(newSpace); // Add connection for down-button if (parentIndex != null && direction != null) { connections.add(Connection( startSpace: spaces[parentIndex], endSpace: newSpace, direction: direction, )); } }); }, ); }, ); } // Function to build a draggable space card Widget _buildSpaceCard(int index, Size screenSize) { return Positioned( left: spaces[index].position.dx, top: spaces[index].position.dy, child: GestureDetector( onPanUpdate: (details) { // Update the position of the space card while dragging setState(() { spaces[index].position += details.delta; }); }, child: MouseRegion( onEnter: (_) { // Show plus buttons on hover setState(() { spaces[index].isHovered = true; }); }, onExit: (_) { // Hide plus buttons when not hovered setState(() { spaces[index].isHovered = false; }); }, child: Stack( clipBehavior: Clip.none, children: [ _buildSpaceContainer(index), if (spaces[index].isHovered) ...[ _buildPlusButton( index, 'left', const Offset(-21, 20), screenSize), _buildPlusButton( index, 'right', const Offset(140, 20), screenSize), _buildPlusButton( index, 'down', const Offset(63, 50), screenSize), ], ], ), ), ), ); } // Function to build the space container with the styled format Widget _buildSpaceContainer(int index) { return Container( width: 150, height: 60, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(15), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.5), spreadRadius: 2, blurRadius: 5, offset: const Offset(0, 3), ), ], ), child: Row( children: [ Container( width: 40, height: 60, decoration: const BoxDecoration( color: Color(0xFF023DFE), borderRadius: BorderRadius.only( topLeft: Radius.circular(15), bottomLeft: Radius.circular(15), ), ), child: Center( child: SvgPicture.asset( spaces[index].icon, width: 24, height: 24, color: Colors.white, ), ), ), const SizedBox(width: 10), Text( spaces[index].name, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), ], ), ); } // Function to build plus buttons for new space creation Widget _buildPlusButton( int index, String direction, Offset offset, Size screenSize) { return Positioned( left: offset.dx, top: offset.dy, child: GestureDetector( onTap: () { Offset newPosition; switch (direction) { case 'left': newPosition = spaces[index].position + const Offset(-200, 0); break; case 'right': newPosition = spaces[index].position + const Offset(200, 0); break; case 'down': newPosition = spaces[index].position + const Offset(0, 150); break; default: newPosition = spaces[index].position; } // Open the dialog to create a new space and pass down the new position _showCreateSpaceDialog(screenSize, position: newPosition, parentIndex: index, direction: direction); }, child: Container( width: 30, height: 30, decoration: const BoxDecoration( color: Color(0xFF023DFE), shape: BoxShape.circle, ), child: const Icon(Icons.add, color: Colors.white, size: 20), ), ), ); } } // Model for storing space information class SpaceData { final String name; final String icon; Offset position; bool isHovered; SpaceData({ required this.name, required this.icon, required this.position, this.isHovered = false, }); } // Class for connection lines between spaces class Connection { final SpaceData startSpace; final SpaceData endSpace; final String direction; Connection( {required this.startSpace, required this.endSpace, required this.direction}); } // Custom painter to draw lines between connected spaces class CurvedLinePainter extends CustomPainter { final List connections; CurvedLinePainter(this.connections); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.black ..strokeWidth = 2 ..style = PaintingStyle.stroke; // Ensure connections exist before painting if (connections.isEmpty) { return; // Nothing to paint if there are no connections } for (var connection in connections) { // Ensure positions are valid before drawing lines if (connection.startSpace.position == null || connection.endSpace.position == null) { continue; } Offset start = connection.startSpace.position + Offset(75, 60); // Center bottom of start space Offset end = connection.endSpace.position + Offset(75, 0); // Center top of end space if (connection.direction == 'down') { // Curved line for down connections final controlPoint = Offset((start.dx + end.dx) / 2, start.dy + 50); final path = Path() ..moveTo(start.dx, start.dy) ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, end.dx, end.dy); canvas.drawPath(path, paint); } else if (connection.direction == 'right') { // Straight line for right connections canvas.drawLine(start, end, paint); } else if (connection.direction == 'left') { // Straight line for left connections canvas.drawLine(start, end, paint); } // Draw small connection dots at the start and end points final dotPaint = Paint()..color = Colors.black; canvas.drawCircle(start, 5, dotPaint); // Start dot canvas.drawCircle(end, 5, dotPaint); // End dot } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; } }