mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-09 22:57:21 +00:00
space canvas initial commit
This commit is contained in:
25
.vscode/launch.json
vendored
Normal file
25
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "web",
|
||||
"request": "launch",
|
||||
"type": "dart"
|
||||
},
|
||||
{
|
||||
"name": "web (profile mode)",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"flutterMode": "profile"
|
||||
},
|
||||
{
|
||||
"name": "web (release mode)",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"flutterMode": "release"
|
||||
}
|
||||
]
|
||||
}
|
3
assets/icons/desk_icon.svg
Normal file
3
assets/icons/desk_icon.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M48.291 25.6836H34.8732V23.666H39.5059C40.7174 23.666 41.7031 22.6804 41.7031 21.4688V12.418C41.7031 11.2063 40.7174 10.2207 39.5059 10.2207H25.8467C24.6352 10.2207 23.6494 11.2063 23.6494 12.418V15.5756L20.5188 14.6216C19.7096 14.4092 19.5266 13.5235 19.6035 12.797C20.6912 11.8364 21.3791 10.4331 21.3791 8.87139V3.37383C21.3791 1.51357 19.8656 0 18.0052 0H14.2766C12.4162 0 10.9026 1.51357 10.9026 3.37383V8.87129C10.9026 10.4736 11.6266 11.9094 12.7637 12.871C12.8421 13.583 12.6316 14.4282 11.8487 14.6363L7.70937 15.899C5.68281 16.5165 4.32129 18.3549 4.32129 20.4736V23.4863C4.32129 23.8908 4.64912 24.2188 5.05371 24.2188C5.4583 24.2188 5.78613 23.8908 5.78613 23.4863V20.4736C5.78613 19.0038 6.73057 17.7285 8.13652 17.3002L9.72246 16.8164L12.755 19.4111C13.0198 19.6377 13.3474 19.7488 13.6738 19.7488C14.0667 19.7488 14.458 19.5879 14.7358 19.2739L15.4084 18.5143V25.6836H10.6746V24.4149C10.6746 24.0105 10.3468 23.6825 9.94219 23.6825C9.5376 23.6825 9.20977 24.0105 9.20977 24.4149V25.6836H1.70898C0.766699 25.6836 0 26.4503 0 27.3926V29.834C0 30.7763 0.766699 31.543 1.70898 31.543H3.6623V49.2676C3.6623 49.6722 3.99014 50 4.39473 50C4.79932 50 5.12715 49.6722 5.12715 49.2676V31.543H48.291C49.2333 31.543 50 30.7763 50 29.834V27.3926C50 26.4503 49.2333 25.6836 48.291 25.6836ZM25.1143 16.5637C25.1143 16.5615 25.1143 16.5593 25.1143 16.5571V12.418C25.1143 12.0141 25.4428 11.6855 25.8467 11.6855H39.5059C39.9098 11.6855 40.2383 12.0141 40.2383 12.418V21.4688C40.2383 21.8727 39.9098 22.2012 39.5059 22.2012H34.8732V20.5566C34.8732 19.345 33.8875 18.3594 32.676 18.3594C31.4645 18.3594 30.4787 19.345 30.4787 20.5566V22.2012H25.8467C25.4428 22.2012 25.1143 21.8727 25.1143 21.4688V16.5637ZM33.4084 22.9373V25.6836H31.9436V20.5566C31.9436 20.1527 32.2721 19.8242 32.676 19.8242C33.0799 19.8242 33.4084 20.1527 33.4084 20.5566V22.9298C33.4084 22.9311 33.4082 22.9322 33.4082 22.9336C33.4082 22.935 33.4084 22.936 33.4084 22.9373ZM30.4787 23.666V25.6836H28.1006V23.666H30.4787ZM23.6494 17.1069V21.4688C23.6494 22.6804 24.6352 23.666 25.8467 23.666H26.6357V25.6836H23.0719V24.4149C23.0719 24.0105 22.744 23.6825 22.3395 23.6825C21.9349 23.6825 21.607 24.0105 21.607 24.4149V25.6836H16.8732V18.4802L17.6097 19.2925C17.8902 19.602 18.2729 19.76 18.658 19.76C18.9826 19.76 19.3091 19.6477 19.5772 19.4191L22.6482 16.8019L23.6494 17.1069ZM20.0921 16.0229L20.9848 16.2949L18.6631 18.2735L17.2884 16.7571L19.2561 15.6011C19.5074 15.7871 19.7892 15.9307 20.0921 16.0229ZM12.3676 3.37383C12.3676 2.32119 13.224 1.46484 14.2767 1.46484H18.0053C19.0579 1.46484 19.9144 2.32129 19.9144 3.37383V5.36426C18.5104 4.47334 16.8104 4.25869 15.2243 4.78125L12.3677 5.72236L12.3676 3.37383ZM12.3676 8.87129V7.26465L15.6825 6.17256C16.8846 5.77656 18.1759 5.95478 19.2257 6.66133L19.9143 7.12471V8.87139C19.9143 10.9521 18.2215 12.6447 16.1408 12.6447C14.0602 12.6447 12.3676 10.952 12.3676 8.87129ZM13.6709 18.2669L11.3835 16.3098L12.2757 16.0376C12.5794 15.9451 12.8609 15.8018 13.1113 15.6167L15.0085 16.7562L13.6709 18.2669ZM14.0172 14.4521C14.1114 14.2237 14.1749 13.9836 14.2054 13.7373C14.8046 13.9765 15.4573 14.1095 16.1408 14.1095C16.856 14.1095 17.5378 13.965 18.1594 13.7045C18.1888 13.9565 18.2528 14.2021 18.3488 14.4352L16.1449 15.73L14.0172 14.4521ZM48.5352 29.834C48.5352 29.9687 48.4257 30.0781 48.291 30.0781H1.70898C1.57432 30.0781 1.46484 29.9687 1.46484 29.834V27.3926C1.46484 27.2579 1.57432 27.1484 1.70898 27.1484H48.291C48.4257 27.1484 48.5352 27.2579 48.5352 27.3926V29.834ZM45.6057 33.3756C45.2011 33.3756 44.8732 33.7034 44.8732 34.108V49.2676C44.8732 49.6722 45.2011 50 45.6057 50C46.0103 50 46.3381 49.6722 46.3381 49.2676V34.108C46.3381 33.7034 46.0103 33.3756 45.6057 33.3756Z" fill="#023DFE" fill-opacity="0.7"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
@ -1,7 +1,7 @@
|
||||
import UIKit
|
||||
import Flutter
|
||||
|
||||
@UIApplicationMain
|
||||
@main
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
|
44
lib/pages/common/buttons/add_space_button.dart
Normal file
44
lib/pages/common/buttons/add_space_button.dart
Normal file
@ -0,0 +1,44 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AddSpaceButton extends StatelessWidget {
|
||||
final VoidCallback onTap;
|
||||
|
||||
const AddSpaceButton({super.key, required this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap, // Handle tap event
|
||||
child: Container(
|
||||
width: 120, // Width of the button
|
||||
height: 60, // Height of the button
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20), // Rounded corners
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5), // Shadow color
|
||||
spreadRadius: 5, // Spread radius of the shadow
|
||||
blurRadius: 7, // Blur effect
|
||||
offset: const Offset(0, 3), // Shadow position
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 40, // Size of the inner circle
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF5F6F7), // Light gray background
|
||||
shape: BoxShape.circle, // Circular shape for the icon container
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.add, // Add icon
|
||||
color: Colors.blue, // Icon color
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
38
lib/pages/common/buttons/cancel_button.dart
Normal file
38
lib/pages/common/buttons/cancel_button.dart
Normal file
@ -0,0 +1,38 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class CancelButton extends StatelessWidget {
|
||||
final String label;
|
||||
final VoidCallback? onPressed;
|
||||
final double? height; // Optional height parameter for customization
|
||||
final double? borderRadius; // Optional border radius customization
|
||||
final double? width;
|
||||
|
||||
const CancelButton({
|
||||
super.key,
|
||||
required this.label, // Button label
|
||||
required this.onPressed, // Button action
|
||||
this.height = 40, // Default height
|
||||
this.width = 140,
|
||||
this.borderRadius = 10, // Default border radius
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ElevatedButton(
|
||||
onPressed: onPressed,
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(ColorsManager.boxColor), // White background
|
||||
foregroundColor: WidgetStateProperty.all(Colors.black), // Black text color
|
||||
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(borderRadius ?? 10),
|
||||
side: const BorderSide(color: ColorsManager.boxColor), // Black border
|
||||
),
|
||||
),
|
||||
fixedSize: WidgetStateProperty.all(Size(width ?? 50, height ?? 40)), // Set button height
|
||||
),
|
||||
child: Text(label), // Dynamic label
|
||||
);
|
||||
}
|
||||
}
|
@ -15,7 +15,8 @@ class DefaultButton extends StatelessWidget {
|
||||
this.backgroundColor,
|
||||
this.foregroundColor,
|
||||
this.borderRadius,
|
||||
this.height,
|
||||
this.height = 40,
|
||||
this.width = 140,
|
||||
this.padding,
|
||||
});
|
||||
final void Function()? onPressed;
|
||||
@ -31,6 +32,8 @@ class DefaultButton extends StatelessWidget {
|
||||
final ButtonStyle? customButtonStyle;
|
||||
final Color? backgroundColor;
|
||||
final Color? foregroundColor;
|
||||
final double? width;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ElevatedButton(
|
||||
@ -39,6 +42,7 @@ class DefaultButton extends StatelessWidget {
|
||||
? null
|
||||
: customButtonStyle ??
|
||||
ButtonStyle(
|
||||
fixedSize: WidgetStateProperty.all(Size(width ?? 50, height ?? 40)), // Set button height
|
||||
textStyle: MaterialStateProperty.all(
|
||||
customTextStyle ??
|
||||
Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
@ -59,14 +63,11 @@ class DefaultButton extends StatelessWidget {
|
||||
? backgroundColor ?? ColorsManager.primaryColor
|
||||
: Colors.black.withOpacity(0.2);
|
||||
}),
|
||||
shape: MaterialStateProperty.all(
|
||||
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(borderRadius ?? 20),
|
||||
borderRadius: BorderRadius.circular(borderRadius ?? 10),
|
||||
),
|
||||
),
|
||||
fixedSize: MaterialStateProperty.all(
|
||||
const Size.fromHeight(50),
|
||||
),
|
||||
padding: MaterialStateProperty.all(
|
||||
EdgeInsets.all(padding ?? 10),
|
||||
),
|
||||
|
60
lib/pages/spaces_management/bloc/space_management_bloc.dart
Normal file
60
lib/pages/spaces_management/bloc/space_management_bloc.dart
Normal file
@ -0,0 +1,60 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart';
|
||||
|
||||
// Events
|
||||
abstract class SpaceEvent extends Equatable {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class LoadSpaces extends SpaceEvent {}
|
||||
|
||||
class SelectSpace extends SpaceEvent {
|
||||
final String selectedSpace;
|
||||
|
||||
SelectSpace(this.selectedSpace);
|
||||
|
||||
@override
|
||||
List<Object> get props => [selectedSpace];
|
||||
}
|
||||
|
||||
// States
|
||||
abstract class SpaceState extends Equatable {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class SpaceInitial extends SpaceState {}
|
||||
|
||||
class SpaceLoaded extends SpaceState {
|
||||
final List<SpaceModel> spaces;
|
||||
final String selectedSpace;
|
||||
|
||||
SpaceLoaded({required this.spaces, required this.selectedSpace});
|
||||
|
||||
@override
|
||||
List<Object> get props => [spaces, selectedSpace];
|
||||
}
|
||||
|
||||
// Bloc
|
||||
class SpacesManagementBloc extends Bloc<SpaceEvent, SpaceState> {
|
||||
SpacesManagementBloc() : super(SpaceInitial()) {
|
||||
on<LoadSpaces>((event, emit) {
|
||||
final List<SpaceModel> spaces = [
|
||||
SpaceModel(communityName: 'Downtown Dubai', subSpaces: ['Sub Space 1', 'Sub Space 2']),
|
||||
SpaceModel(communityName: 'Dubai Creek Harbour', subSpaces: ['Sub Space 1', 'Sub Space 2']),
|
||||
SpaceModel(communityName: 'Dubai Hills Estate', subSpaces: ['Sub Space 1', 'Sub Space 2']),
|
||||
];
|
||||
emit(SpaceLoaded(spaces: spaces, selectedSpace: spaces[0].communityName));
|
||||
});
|
||||
|
||||
on<SelectSpace>((event, emit) {
|
||||
if (state is SpaceLoaded) {
|
||||
final loadedState = state as SpaceLoaded;
|
||||
emit(SpaceLoaded(spaces: loadedState.spaces, selectedSpace: event.selectedSpace));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
6
lib/pages/spaces_management/model/space_model.dart
Normal file
6
lib/pages/spaces_management/model/space_model.dart
Normal file
@ -0,0 +1,6 @@
|
||||
class SpaceModel {
|
||||
final String communityName;
|
||||
final List<String> subSpaces;
|
||||
|
||||
SpaceModel({required this.communityName, required this.subSpaces});
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class CreateSpaceDialog extends StatefulWidget {
|
||||
// Add the onCreateSpace parameter as a required field
|
||||
final Function(String, String) onCreateSpace;
|
||||
|
||||
const CreateSpaceDialog({super.key, required this.onCreateSpace});
|
||||
|
||||
@override
|
||||
CreateSpaceDialogState createState() => CreateSpaceDialogState();
|
||||
}
|
||||
|
||||
class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
String selectedIcon = Assets.location; // Initially selected icon
|
||||
String enteredName = ''; // Store entered space name
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Create New Space'),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
content: SizedBox(
|
||||
width: 600, // Set width for the dialog
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFFF5F6F7),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
SvgPicture.asset(
|
||||
selectedIcon, // Display the selected icon here
|
||||
width: 60,
|
||||
height: 60,
|
||||
),
|
||||
Positioned(
|
||||
top: 2,
|
||||
left: 2,
|
||||
child: InkWell(
|
||||
onTap: () =>
|
||||
_showIconSelectionDialog(), // Open the icon selection dialog
|
||||
child: Container(
|
||||
width: 20,
|
||||
height: 20,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: SvgPicture.asset(Assets.iconEdit,
|
||||
width: 10, height: 10),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
onChanged: (value) {
|
||||
enteredName = value; // Capture entered name
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Please enter the name',
|
||||
filled: true,
|
||||
fillColor: const Color(0xFFF5F6F7),
|
||||
border: OutlineInputBorder(
|
||||
borderSide: const BorderSide(color: Color(0xFFF5F6F7)),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Color(0xFFF5F6F7),
|
||||
width: 1), // Default border
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide:
|
||||
BorderSide(color: Color(0xFFF5F6F7), width: 1),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CancelButton(
|
||||
label: 'Cancel',
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
onPressed: () {
|
||||
if (enteredName.isNotEmpty) {
|
||||
widget.onCreateSpace(enteredName, selectedIcon); // Pass name and icon back
|
||||
Navigator.of(context).pop(); // Close dialog
|
||||
}
|
||||
},
|
||||
child: const Text('OK'),
|
||||
backgroundColor: const Color(0xFF023DFE),
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// Icon selection dialog
|
||||
void _showIconSelectionDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Select Icon'),
|
||||
backgroundColor: Colors.white,
|
||||
content: Container(
|
||||
width: 500, // Width of the icon selection dialog
|
||||
height: 200, // Height of the dialog
|
||||
padding: const EdgeInsets.all(18),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF5F6F7),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: GridView.builder(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 7, // Number of icons per row
|
||||
crossAxisSpacing: 10, // Space between icons horizontally
|
||||
mainAxisSpacing: 22, // Space between icons vertically
|
||||
),
|
||||
itemCount: _iconList.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedIcon =
|
||||
_iconList[index]; // Update the selected icon
|
||||
});
|
||||
Navigator.of(context)
|
||||
.pop(); // Close the icon selection dialog
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
_iconList[index],
|
||||
width: 50, // Adjust size as needed
|
||||
height: 50,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Icon list containing SVG asset paths
|
||||
final List<String> _iconList = [
|
||||
Assets.location,
|
||||
Assets.villa,
|
||||
Assets.gym,
|
||||
Assets.sauna,
|
||||
Assets.bbq,
|
||||
Assets.building,
|
||||
Assets.desk,
|
||||
Assets.door,
|
||||
Assets.parking,
|
||||
Assets.pool,
|
||||
Assets.stair,
|
||||
Assets.steamRoom,
|
||||
Assets.street,
|
||||
Assets.unit,
|
||||
];
|
||||
}
|
47
lib/pages/spaces_management/view/space_widget.dart
Normal file
47
lib/pages/spaces_management/view/space_widget.dart
Normal file
@ -0,0 +1,47 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SpaceWidget extends StatelessWidget {
|
||||
final String name;
|
||||
final Offset position;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const SpaceWidget({
|
||||
super.key,
|
||||
required this.name,
|
||||
required this.position,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Positioned(
|
||||
left: position.dx,
|
||||
top: position.dy,
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
spreadRadius: 5,
|
||||
blurRadius: 7,
|
||||
offset: const Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.location_on, color: Colors.blue),
|
||||
const SizedBox(width: 8),
|
||||
Text(name, style: const TextStyle(fontSize: 16)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
203
lib/pages/spaces_management/view/spaces_management_page.dart
Normal file
203
lib/pages/spaces_management/view/spaces_management_page.dart
Normal file
@ -0,0 +1,203 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.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/utils/color_manager.dart';
|
||||
|
||||
class SpaceManagementPage extends StatefulWidget {
|
||||
@override
|
||||
SpaceManagementPageState createState() => SpaceManagementPageState();
|
||||
}
|
||||
|
||||
class SpaceManagementPageState extends State<SpaceManagementPage> {
|
||||
// Store created spaces
|
||||
List<SpaceData> spaces = [];
|
||||
|
||||
@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(
|
||||
children: [
|
||||
Center(
|
||||
child: spaces.isEmpty
|
||||
? AddSpaceButton(
|
||||
onTap: () {
|
||||
_showCreateSpaceDialog(screenSize);
|
||||
},
|
||||
)
|
||||
: Stack(
|
||||
children: spaces
|
||||
.asMap()
|
||||
.entries
|
||||
.map((entry) => _buildSpaceCard(entry.key, screenSize))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Function to open the Create Space dialog
|
||||
void _showCreateSpaceDialog(Size screenSize, {Offset? position}) {
|
||||
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
|
||||
);
|
||||
|
||||
spaces.add(SpaceData(name: name, icon: icon, position: centerPosition));
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 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, 55), 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;
|
||||
}
|
||||
|
||||
_showCreateSpaceDialog(screenSize, position: newPosition);
|
||||
},
|
||||
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});
|
||||
}
|
@ -3,6 +3,7 @@ import 'package:syncrow_web/pages/access_management/view/access_management.dart'
|
||||
import 'package:syncrow_web/pages/auth/view/login_page.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/view/device_managment_page.dart';
|
||||
import 'package:syncrow_web/pages/home/view/home_page.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/view/spaces_management_page.dart';
|
||||
import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart';
|
||||
import 'package:syncrow_web/utils/constants/routes_const.dart';
|
||||
|
||||
@ -11,7 +12,7 @@ class AppRoutes {
|
||||
return [
|
||||
GoRoute(
|
||||
path: RoutesConst.auth,
|
||||
builder: (context, state) => const LoginPage(),
|
||||
builder: (context, state) => SpaceManagementPage(),
|
||||
),
|
||||
GoRoute(
|
||||
path: RoutesConst.home,
|
||||
@ -29,6 +30,9 @@ class AppRoutes {
|
||||
path: RoutesConst.deviceManagementPage,
|
||||
builder: (context, state) => const DeviceManagementPage(),
|
||||
),
|
||||
GoRoute(
|
||||
path: RoutesConst.spacesManagementPage,
|
||||
builder: (context, state) => SpaceManagementPage()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
24
pubspec.lock
24
pubspec.lock
@ -268,18 +268,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.4"
|
||||
version: "10.0.5"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
version: "3.0.5"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -316,18 +316,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.0"
|
||||
version: "1.15.0"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -537,10 +537,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
version: "0.7.2"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -585,10 +585,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||
sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.1"
|
||||
version: "14.2.4"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
Reference in New Issue
Block a user