mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 07:07:19 +00:00
added edit space option
This commit is contained in:
35
assets/icons/door_sensor.svg
Normal file
35
assets/icons/door_sensor.svg
Normal file
@ -0,0 +1,35 @@
|
||||
<svg width="38" height="47" viewBox="0 0 38 47" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d_5442_18595)">
|
||||
<g filter="url(#filter1_i_5442_18595)">
|
||||
<path d="M5 7.15639C5 4.66815 6.82886 2.53419 9.30515 2.29062C13.3497 1.89279 16.6065 1.91012 20.6058 2.30139C23.094 2.54481 24.9399 4.68366 24.9399 7.18371V33.6749C24.9399 36.1374 23.1483 38.2593 20.7006 38.529C16.6673 38.9735 13.3539 38.9769 9.24395 38.5259C6.79422 38.2571 5 36.1349 5 33.6705V7.15639Z" fill="#EAF6FF"/>
|
||||
</g>
|
||||
<path d="M22.049 24.6587C22.1741 24.6587 22.2992 24.611 22.3947 24.5155C22.5856 24.3246 22.5856 24.0151 22.3947 23.8242C20.7605 22.1901 20.7605 19.5311 22.3947 17.897C22.5856 17.706 22.5856 17.3965 22.3947 17.2056C22.2037 17.0148 21.8943 17.0148 21.7034 17.2056C19.688 19.221 19.688 22.5002 21.7034 24.5156C21.7988 24.611 21.9239 24.6587 22.049 24.6587Z" fill="#8AC9FE"/>
|
||||
<path d="M23.0772 22.7595C23.1822 22.7595 23.2872 22.7195 23.3673 22.6394C23.5274 22.4792 23.5274 22.2195 23.3672 22.0594C23.047 21.7392 22.8707 21.3134 22.8707 20.8604C22.8707 20.4076 23.047 19.9818 23.3672 19.6616C23.5274 19.5014 23.5274 19.2417 23.3673 19.0816C23.207 18.9214 22.9473 18.9214 22.7873 19.0815C22.3121 19.5567 22.0504 20.1885 22.0504 20.8604C22.0504 21.5325 22.312 22.1642 22.7873 22.6394C22.8673 22.7194 22.9723 22.7595 23.0772 22.7595Z" fill="#8AC9FE"/>
|
||||
<path d="M15.837 31.6211L15.837 33.4829C15.837 33.8534 15.5367 34.1538 15.1661 34.1538C14.7955 34.1538 14.4952 33.8534 14.4952 33.4829L14.4952 31.6211C14.4952 31.2505 14.7955 30.9502 15.1661 30.9502C15.5367 30.9502 15.837 31.2505 15.837 31.6211Z" fill="#B3DAFE"/>
|
||||
<path d="M25.8903 10.5772C25.8903 9.69265 26.4679 8.89349 27.3345 8.71634C28.6715 8.44303 29.7509 8.4541 31.0716 8.72152C31.9473 8.89886 32.5369 9.70267 32.5369 10.5962V32.0694C32.5369 32.9344 31.9848 33.7213 31.1421 33.9165C29.784 34.231 28.6723 34.2337 27.2885 33.9148C26.4442 33.7202 25.8903 32.9329 25.8903 32.0664V10.5772Z" fill="#EAF6FF"/>
|
||||
<path d="M28.2773 24.6579C28.1523 24.6579 28.0271 24.6102 27.9316 24.5148C27.7407 24.3238 27.7407 24.0144 27.9316 23.8235C29.5658 22.1893 29.5658 19.5304 27.9316 17.8962C27.7407 17.7053 27.7407 17.3958 27.9316 17.2049C28.1226 17.0141 28.4321 17.0141 28.623 17.2049C30.6383 19.2203 30.6383 22.4995 28.623 24.5149C28.5275 24.6102 28.4024 24.6579 28.2773 24.6579Z" fill="#8AC9FE"/>
|
||||
<path d="M27.2491 22.7588C27.1441 22.7588 27.0392 22.7187 26.9591 22.6386C26.7989 22.4785 26.7989 22.2187 26.9591 22.0586C27.2793 21.7384 27.4557 21.3126 27.4557 20.8597C27.4557 20.4069 27.2793 19.9811 26.9591 19.6609C26.7989 19.5007 26.7989 19.241 26.9591 19.0809C27.1193 18.9206 27.379 18.9207 27.5391 19.0808C28.0142 19.556 28.276 20.1877 28.276 20.8597C28.276 21.5318 28.0143 22.1635 27.5391 22.6387C27.4591 22.7187 27.3541 22.7588 27.2491 22.7588Z" fill="#8AC9FE"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_5442_18595" x="0.5" y="0.5" width="36.5369" height="45.8633" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="3"/>
|
||||
<feGaussianBlur stdDeviation="2.25"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.07 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_5442_18595"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_5442_18595" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter1_i_5442_18595" x="4" y="2" width="20.9399" height="36.8633" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dx="-1"/>
|
||||
<feGaussianBlur stdDeviation="1.5"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.538295 0 0 0 0 0.538295 0 0 0 0 0.538295 0 0 0 0.3 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_5442_18595"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 4.2 KiB |
@ -119,7 +119,6 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
||||
|
||||
try {
|
||||
final updatedSpaces = await saveSpacesHierarchically(event.spaces, event.communityUuid);
|
||||
|
||||
emit(SpaceCreationSuccess(spaces: updatedSpaces));
|
||||
add(LoadCommunityAndSpacesEvent());
|
||||
} catch (e) {
|
||||
@ -158,6 +157,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
||||
position: space.position,
|
||||
icon: space.icon,
|
||||
direction: space.incomingConnection?.direction,
|
||||
products: space.selectedProducts
|
||||
|
||||
);
|
||||
space.uuid = response?.uuid;
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
class SelectedProduct {
|
||||
final String productId;
|
||||
int count;
|
||||
|
||||
SelectedProduct({required this.productId, required this.count});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'productId': productId,
|
||||
'count': count,
|
||||
};
|
||||
}
|
||||
}
|
@ -1,14 +1,15 @@
|
||||
import 'dart:ui';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart';
|
||||
|
||||
enum SpaceStatus { newSpace, modified, unchanged }
|
||||
|
||||
class SpaceModel {
|
||||
String? uuid;
|
||||
final String? icon;
|
||||
String? icon;
|
||||
final String? spaceTuyaUuid;
|
||||
final String name;
|
||||
String name;
|
||||
final bool isPrivate;
|
||||
final String? invitationCode;
|
||||
SpaceModel? parent;
|
||||
@ -17,6 +18,7 @@ class SpaceModel {
|
||||
Offset position;
|
||||
bool isHovered;
|
||||
SpaceStatus status;
|
||||
List<SelectedProduct> selectedProducts;
|
||||
|
||||
List<Connection> outgoingConnections = []; // Connections from this space
|
||||
Connection? incomingConnection; // Connections to this space
|
||||
@ -35,6 +37,7 @@ class SpaceModel {
|
||||
this.isHovered = false,
|
||||
this.incomingConnection,
|
||||
this.status = SpaceStatus.unchanged,
|
||||
this.selectedProducts = const [],
|
||||
});
|
||||
|
||||
factory SpaceModel.fromJson(Map<String, dynamic> json) {
|
||||
@ -72,19 +75,6 @@ class SpaceModel {
|
||||
|
||||
return instance;
|
||||
}
|
||||
@override
|
||||
String toString() {
|
||||
return '''
|
||||
SpaceModel {
|
||||
uuid: $uuid,
|
||||
name: $name,
|
||||
isPrivate: $isPrivate,
|
||||
position: {dx: ${position.dx}, dy: ${position.dy}},
|
||||
parentUuid: ${parent?.uuid},
|
||||
children: ${children.map((child) => child.name).toList()},
|
||||
isHovered: $isHovered
|
||||
}''';
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
|
@ -3,15 +3,28 @@ 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/pages/spaces_management/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/widgets/add_device_type_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/widgets/hoverable_button.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class CreateSpaceDialog extends StatefulWidget {
|
||||
final Function(String, String) onCreateSpace;
|
||||
final Function(String, String, List<SelectedProduct> selectedProducts) onCreateSpace;
|
||||
final List<ProductModel>? products;
|
||||
final String? name;
|
||||
final String? icon;
|
||||
final bool isEdit;
|
||||
final List<SelectedProduct> selectedProducts;
|
||||
|
||||
const CreateSpaceDialog({super.key, required this.onCreateSpace, this.products});
|
||||
const CreateSpaceDialog(
|
||||
{super.key,
|
||||
required this.onCreateSpace,
|
||||
this.products,
|
||||
this.name,
|
||||
this.icon,
|
||||
this.isEdit = false,
|
||||
this.selectedProducts = const []});
|
||||
|
||||
@override
|
||||
CreateSpaceDialogState createState() => CreateSpaceDialogState();
|
||||
@ -20,12 +33,21 @@ class CreateSpaceDialog extends StatefulWidget {
|
||||
class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
String selectedIcon = Assets.location;
|
||||
String enteredName = '';
|
||||
Map<String, int> selectedProducts = {};
|
||||
List<SelectedProduct> selectedProducts = [];
|
||||
late TextEditingController nameController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
selectedIcon = widget.icon ?? Assets.location;
|
||||
nameController = TextEditingController(text: widget.name ?? '');
|
||||
selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Create New Space'),
|
||||
title: widget.isEdit ? Text('Edit Space') : Text('Create new Space'),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
content: SizedBox(
|
||||
width: 600,
|
||||
@ -76,6 +98,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
children: [
|
||||
// Name input field
|
||||
TextField(
|
||||
controller: nameController,
|
||||
onChanged: (value) {
|
||||
enteredName = value;
|
||||
},
|
||||
@ -208,8 +231,10 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
onPressed: () {
|
||||
if (enteredName.isNotEmpty) {
|
||||
widget.onCreateSpace(enteredName, selectedIcon); // Pass the name and icon back
|
||||
late String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? '');
|
||||
if (newName.isNotEmpty) {
|
||||
widget.onCreateSpace(
|
||||
newName, selectedIcon, selectedProducts); // Pass the name and icon back
|
||||
Navigator.of(context).pop(); // Close the dialog
|
||||
}
|
||||
},
|
||||
@ -270,49 +295,31 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
|
||||
Widget _buildSelectedProductsButtons(List<ProductModel> products) {
|
||||
return Container(
|
||||
width: 600,
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: ColorsManager.neutralGray,
|
||||
width: 2, // Set the border width
|
||||
),
|
||||
),
|
||||
child: Wrap(
|
||||
spacing: 8, // Horizontal spacing between buttons
|
||||
runSpacing: 8, // Vertical spacing between rows
|
||||
children: [
|
||||
// Dynamically create a button for each selected product
|
||||
for (var entry in selectedProducts.entries)
|
||||
GestureDetector(
|
||||
for (var product in selectedProducts)
|
||||
HoverableButton(
|
||||
iconPath: _mapIconToProduct(product.productId, products),
|
||||
text: 'x${product.count}',
|
||||
onTap: () {
|
||||
|
||||
setState(() {
|
||||
selectedProducts.remove(product);
|
||||
});
|
||||
// Handle button tap
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Display the product icon
|
||||
SvgPicture.asset(
|
||||
_mapIconToProduct(entry.key, products),
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// Display the product count
|
||||
Text(
|
||||
'x${entry.value}',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: ColorsManager.spaceColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// Add Button
|
||||
GestureDetector(
|
||||
@ -331,10 +338,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
color: ColorsManager.whiteColors,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: const Icon(
|
||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/widgets/counter_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
@ -9,8 +10,8 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class AddDeviceWidget extends StatefulWidget {
|
||||
final List<ProductModel>? products;
|
||||
final ValueChanged<Map<String, int>>? onProductsSelected;
|
||||
final Map<String, int>? initialSelectedProducts;
|
||||
final ValueChanged<List<SelectedProduct>>? onProductsSelected;
|
||||
final List<SelectedProduct>? initialSelectedProducts;
|
||||
|
||||
const AddDeviceWidget(
|
||||
{super.key, this.products, this.initialSelectedProducts, this.onProductsSelected});
|
||||
@ -21,15 +22,14 @@ class AddDeviceWidget extends StatefulWidget {
|
||||
|
||||
class _AddDeviceWidgetState extends State<AddDeviceWidget> {
|
||||
late ScrollController _scrollController;
|
||||
Map<String, int> productCounts = {};
|
||||
List<SelectedProduct> productCounts = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scrollController = ScrollController();
|
||||
print(widget.initialSelectedProducts);
|
||||
if (widget.initialSelectedProducts != null && widget.initialSelectedProducts!.isNotEmpty) {
|
||||
productCounts = widget.initialSelectedProducts!;
|
||||
productCounts = List.from(widget.initialSelectedProducts!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,6 +112,12 @@ class _AddDeviceWidgetState extends State<AddDeviceWidget> {
|
||||
}
|
||||
|
||||
Widget _buildDeviceTypeTile(ProductModel? deviceType) {
|
||||
|
||||
SelectedProduct? existingProduct = productCounts.firstWhere(
|
||||
(product) => product.productId == deviceType?.uuid,
|
||||
orElse: () => SelectedProduct(productId: deviceType!.uuid, count: 0),
|
||||
);
|
||||
|
||||
return SizedBox(
|
||||
width: 75,
|
||||
height: 90, // Increase height if needed
|
||||
@ -161,11 +167,15 @@ class _AddDeviceWidgetState extends State<AddDeviceWidget> {
|
||||
const SizedBox(height: 8),
|
||||
// The custom counter widget aligned at the bottom
|
||||
CounterWidget(
|
||||
initialCount: productCounts[deviceType!.uuid] ?? 0,
|
||||
initialCount: existingProduct.count,
|
||||
onCountChanged: (newCount) {
|
||||
setState(() {
|
||||
if (newCount > 0) {
|
||||
productCounts[deviceType!.uuid] = newCount;
|
||||
if (!productCounts.contains(existingProduct)) {
|
||||
productCounts.add(SelectedProduct(productId: deviceType!.uuid, count: newCount));
|
||||
} else {
|
||||
existingProduct.count = newCount;
|
||||
}
|
||||
} else {
|
||||
productCounts.remove(deviceType!.uuid);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import 'package:syncrow_web/pages/common/buttons/add_space_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart';
|
||||
@ -114,6 +115,9 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
buildSpaceContainer: (int index) {
|
||||
return SpaceContainerWidget(
|
||||
index: index,
|
||||
onDoubleTap: () {
|
||||
_showEditSpaceDialog(spaces[index]);
|
||||
},
|
||||
icon: spaces[index].icon ?? '',
|
||||
name: spaces[index].name,
|
||||
);
|
||||
@ -240,7 +244,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
builder: (BuildContext context) {
|
||||
return CreateSpaceDialog(
|
||||
products: widget.products,
|
||||
onCreateSpace: (String name, String icon) {
|
||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts) {
|
||||
setState(() {
|
||||
// Set the first space in the center or use passed position
|
||||
Offset centerPosition = position ??
|
||||
@ -255,7 +259,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
isPrivate: false,
|
||||
children: [],
|
||||
status: SpaceStatus.newSpace,
|
||||
);
|
||||
selectedProducts: selectedProducts);
|
||||
|
||||
if (parentIndex != null && direction != null) {
|
||||
SpaceModel parentSpace = spaces[parentIndex];
|
||||
@ -280,6 +284,35 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void _showEditSpaceDialog(SpaceModel space) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return CreateSpaceDialog(
|
||||
products: widget.products,
|
||||
name: space.name,
|
||||
icon: space.icon,
|
||||
isEdit: true,
|
||||
selectedProducts: space.selectedProducts,
|
||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts) {
|
||||
setState(() {
|
||||
// Update the space's properties
|
||||
space.name = name;
|
||||
space.icon = icon;
|
||||
space.selectedProducts = selectedProducts;
|
||||
|
||||
if (space.status != SpaceStatus.newSpace) {
|
||||
space.status = SpaceStatus.modified; // Mark as modified
|
||||
}
|
||||
});
|
||||
},
|
||||
// Pre-fill the dialog with current space data
|
||||
key: Key(space.name), // Add a unique key to ensure dialog refresh
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
void _handleHoverChanged(int index, bool isHovered) {
|
||||
setState(() {
|
||||
spaces[index].isHovered = isHovered;
|
||||
|
77
lib/pages/spaces_management/widgets/hoverable_button.dart
Normal file
77
lib/pages/spaces_management/widgets/hoverable_button.dart
Normal file
@ -0,0 +1,77 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class HoverableButton extends StatefulWidget {
|
||||
final String iconPath;
|
||||
final String text;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const HoverableButton({
|
||||
Key? key,
|
||||
required this.iconPath,
|
||||
required this.text,
|
||||
required this.onTap,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_HoverableButtonState createState() => _HoverableButtonState();
|
||||
}
|
||||
|
||||
class _HoverableButtonState extends State<HoverableButton> {
|
||||
bool isHovered = false; // Track hover state
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: widget.onTap,
|
||||
child: MouseRegion(
|
||||
onEnter: (_) => setState(() => isHovered = true),
|
||||
onExit: (_) => setState(() => isHovered = false),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: isHovered ? ColorsManager.warningRed : Colors.white, // Change color on hover
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
if (isHovered)
|
||||
BoxShadow(
|
||||
color: ColorsManager.warningRed,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (!isHovered)
|
||||
SvgPicture.asset(
|
||||
widget.iconPath,
|
||||
width: 24,
|
||||
height: 24,
|
||||
)
|
||||
else
|
||||
Center(
|
||||
child: const Icon(
|
||||
Icons.close, // Display "X" on hover
|
||||
color: Colors.white,
|
||||
size: 24,
|
||||
)),
|
||||
const SizedBox(width: 8),
|
||||
if (!isHovered) ...[
|
||||
Text(
|
||||
widget.text,
|
||||
style: TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: ColorsManager.spaceColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -2,22 +2,25 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
|
||||
class SpaceContainerWidget extends StatelessWidget {
|
||||
final int index;
|
||||
final String icon;
|
||||
final String name;
|
||||
final VoidCallback? onDoubleTap;
|
||||
|
||||
const SpaceContainerWidget({
|
||||
super.key,
|
||||
required this.index,
|
||||
required this.icon,
|
||||
required this.name,
|
||||
this.onDoubleTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
return GestureDetector(
|
||||
onDoubleTap: onDoubleTap,
|
||||
child: Container(
|
||||
width: 150,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
@ -63,7 +66,6 @@ class SpaceContainerWidget extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/space_response_model.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
@ -144,6 +145,7 @@ class CommunitySpaceManagementApi {
|
||||
bool isPrivate = false,
|
||||
required Offset position,
|
||||
String? icon,
|
||||
required List<SelectedProduct> products,
|
||||
}) async {
|
||||
try {
|
||||
final body = {
|
||||
@ -152,7 +154,8 @@ class CommunitySpaceManagementApi {
|
||||
'x': position.dx,
|
||||
'y': position.dy,
|
||||
'direction': direction,
|
||||
'icon': icon
|
||||
'icon': icon,
|
||||
'products': products.map((product) => product.toJson()).toList(),
|
||||
};
|
||||
if (parentId != null) {
|
||||
body['parentUuid'] = parentId;
|
||||
|
@ -51,6 +51,7 @@ abstract class ColorsManager {
|
||||
static const Color spaceColor = Color(0xB2023DFE);
|
||||
static const Color counterBackgroundColor = Color(0xCCF4F4F4);
|
||||
static const Color neutralGray = Color(0xFFE5E5E5);
|
||||
static const Color warningRed = Color(0xFFFF6465);
|
||||
}
|
||||
//background: #background: #5D5D5D;
|
||||
|
||||
|
Reference in New Issue
Block a user