From c221c8499f69336c22d5ccae07fdd77a9f0a3f0d Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 2 Jul 2025 17:05:56 +0300 Subject: [PATCH] Add factory method `empty` to `SpaceModel` for default instance creation. Refactor SpaceDetailsDialog and related widgets to utilize SpaceModel, enhancing parameter handling and state management in space creation and editing flows. --- .../domain/models/space_model.dart | 10 +++ .../helpers/space_details_dialog_helper.dart | 13 +--- .../widgets/space_details_dialog.dart | 13 ++-- .../widgets/space_details_form.dart | 78 +++++++++---------- .../widgets/space_sub_spaces_box.dart | 27 +++++-- .../widgets/space_sub_spaces_dialog.dart | 7 +- .../widgets/subspace_name_display_widget.dart | 73 ++++++++++------- 7 files changed, 130 insertions(+), 91 deletions(-) diff --git a/lib/pages/space_management_v2/modules/communities/domain/models/space_model.dart b/lib/pages/space_management_v2/modules/communities/domain/models/space_model.dart index 36943adb..ddcc6a86 100644 --- a/lib/pages/space_management_v2/modules/communities/domain/models/space_model.dart +++ b/lib/pages/space_management_v2/modules/communities/domain/models/space_model.dart @@ -19,6 +19,16 @@ class SpaceModel extends Equatable { required this.parent, }); + factory SpaceModel.empty() => const SpaceModel( + uuid: '', + createdAt: null, + updatedAt: null, + spaceName: '', + icon: '', + children: [], + parent: null, + ); + factory SpaceModel.fromJson(Map json) { return SpaceModel( uuid: json['uuid'] as String? ?? '', diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart b/lib/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart index 5d6ffee7..723a5bc1 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_dialog.dart'; abstract final class SpaceDetailsDialogHelper { @@ -9,8 +8,8 @@ abstract final class SpaceDetailsDialogHelper { context: context, builder: (_) => SpaceDetailsDialog( title: const Text('Create Space'), - space: SpaceDetailsModel.empty(), - onSave: (space) {}, + spaceModel: SpaceModel.empty(), + onSave: (space) => print(space), ), ); } @@ -23,13 +22,7 @@ abstract final class SpaceDetailsDialogHelper { context: context, builder: (_) => SpaceDetailsDialog( title: const Text('Edit Space'), - space: SpaceDetailsModel( - uuid: spaceModel.uuid, - spaceName: spaceModel.spaceName, - icon: spaceModel.icon, - productAllocations: const [], - subspaces: const [], - ), + spaceModel: spaceModel, onSave: (space) {}, ), ); diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_dialog.dart b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_dialog.dart index 69b54b4f..85e9f009 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_dialog.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_dialog.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart'; @@ -10,13 +11,13 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart'; class SpaceDetailsDialog extends StatefulWidget { const SpaceDetailsDialog({ required this.title, - required this.space, + required this.spaceModel, required this.onSave, super.key, }); final Widget title; - final SpaceDetailsModel space; + final SpaceModel spaceModel; final void Function(SpaceDetailsModel space) onSave; @override @@ -26,10 +27,10 @@ class SpaceDetailsDialog extends StatefulWidget { class _SpaceDetailsDialogState extends State { @override void initState() { - final isCreateMode = widget.space.uuid.isEmpty; + final isCreateMode = widget.spaceModel.uuid.isEmpty; if (!isCreateMode) { - final param = LoadSpaceDetailsParam(spaceUuid: widget.space.uuid); + final param = LoadSpaceDetailsParam(spaceUuid: widget.spaceModel.uuid); context.read().add(LoadSpaceDetails(param)); } super.initState(); @@ -37,11 +38,11 @@ class _SpaceDetailsDialogState extends State { @override Widget build(BuildContext context) { - final isCreateMode = widget.space.uuid.isEmpty; + final isCreateMode = widget.spaceModel.uuid.isEmpty; if (isCreateMode) { return SpaceDetailsForm( title: widget.title, - space: widget.space, + space: SpaceDetailsModel.empty(), onSave: widget.onSave, ); } diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_form.dart b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_form.dart index aa08d7fe..24bae9a3 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_form.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_form.dart @@ -26,51 +26,51 @@ class SpaceDetailsForm extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (context) => SpaceDetailsModelBloc(initialState: space), - child: AlertDialog( - title: title, - backgroundColor: ColorsManager.whiteColors, - content: SizedBox( - height: context.screenHeight * 0.25, - child: Row( - spacing: 20, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded(child: SpaceIconPicker(iconPath: space.icon)), - Expanded( - flex: 2, - child: Column( - mainAxisSize: MainAxisSize.min, + child: BlocBuilder( + buildWhen: (previous, current) => previous != current, + builder: (context, state) { + return AlertDialog( + title: title, + backgroundColor: ColorsManager.whiteColors, + content: SizedBox( + height: context.screenHeight * 0.3, + child: Row( + spacing: 20, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SpaceNameTextField( - initialValue: space.spaceName, - isNameFieldExist: (value) { - final subspaces = space.subspaces; - if (subspaces.isEmpty) return false; - return subspaces.any( - (subspace) => subspace.name == value, - ); - }, + Expanded(child: SpaceIconPicker(iconPath: state.icon)), + Expanded( + flex: 2, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SpaceNameTextField( + initialValue: state.spaceName, + isNameFieldExist: (value) => state.subspaces.any( + (subspace) => subspace.name == value, + ), + ), + const Spacer(), + SpaceSubSpacesBox( + subspaces: state.subspaces, + ), + const SizedBox(height: 16), + SpaceDetailsDevicesBox(space: state), + ], + ), ), - const Spacer(), - SpaceSubSpacesBox( - subspaces: space.subspaces, - ), - const SizedBox(height: 16), - SpaceDetailsDevicesBox(space: space), ], ), ), - ], - ), - ), - actions: [ - SpaceDetailsActionButtons( - onSave: () => onSave(space), - onCancel: Navigator.of(context).pop, - ), - ], - ), + actions: [ + SpaceDetailsActionButtons( + onSave: () => onSave(state), + onCancel: Navigator.of(context).pop, + ), + ], + ); + }), ); } } diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_box.dart b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_box.dart index 30677e1e..5c3cef25 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_box.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_box.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/common/edit_chip.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/button_content_widget.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_dialog.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_name_display_widget.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; @@ -23,10 +25,7 @@ class SpaceSubSpacesBox extends StatelessWidget { padding: EdgeInsets.zero, overlayColor: ColorsManager.transparentColor, ), - onPressed: () => showDialog( - context: context, - builder: (_) => SpaceSubSpacesDialog(subspaces: subspaces), - ), + onPressed: () => _showSubSpacesDialog(context), child: const ButtonContentWidget( svgAssets: Assets.addIcon, label: 'Create Sub Spaces', @@ -54,10 +53,7 @@ class SpaceSubSpacesBox extends StatelessWidget { (e) => SubspaceNameDisplayWidget(subSpace: e), ), EditChip( - onTap: () => showDialog( - context: context, - builder: (_) => const SpaceSubSpacesDialog(subspaces: []), - ), + onTap: () => _showSubSpacesDialog(context), ), ], ), @@ -66,4 +62,19 @@ class SpaceSubSpacesBox extends StatelessWidget { ], ); } + + void _showSubSpacesDialog(BuildContext context) { + showDialog( + context: context, + barrierDismissible: false, + builder: (_) => SpaceSubSpacesDialog( + subspaces: subspaces, + onSave: (subspaces) { + context.read().add( + UpdateSpaceDetailsSubspaces(subspaces), + ); + }, + ), + ); + } } diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_dialog.dart b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_dialog.dart index 4230f7f4..4c537a8a 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_dialog.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_dialog.dart @@ -7,10 +7,12 @@ import 'package:uuid/uuid.dart'; class SpaceSubSpacesDialog extends StatefulWidget { const SpaceSubSpacesDialog({ required this.subspaces, + required this.onSave, super.key, }); final List subspaces; + final void Function(List subspaces) onSave; @override State createState() => _SpaceSubSpacesDialogState(); @@ -46,7 +48,10 @@ class _SpaceSubSpacesDialogState extends State { () => _subspaces = _subspaces.where((s) => s.uuid != uuid).toList(), ); - void _handleSave() => Navigator.of(context).pop(_subspaces); + void _handleSave() { + widget.onSave(_subspaces); + Navigator.of(context).pop(); + } @override Widget build(BuildContext context) { diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_name_display_widget.dart b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_name_display_widget.dart index 3d7ab59b..9169efda 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_name_display_widget.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_name_display_widget.dart @@ -17,16 +17,19 @@ class SubspaceNameDisplayWidget extends StatefulWidget { class _SubspaceNameDisplayWidgetState extends State { late final TextEditingController _controller; + late final FocusNode _focusNode; bool isEditing = false; @override void initState() { _controller = TextEditingController(text: widget.subSpace.name); + _focusNode = FocusNode(); super.initState(); } @override void dispose() { _controller.dispose(); + _focusNode.dispose(); super.dispose(); } @@ -36,38 +39,54 @@ class _SubspaceNameDisplayWidgetState extends State { color: ColorsManager.spaceColor, ); return InkWell( - onTap: () => setState(() => isEditing = true), - child: Visibility( - visible: isEditing, - replacement: Text( - widget.subSpace.name, - style: textStyle, + onTap: () { + setState(() => isEditing = true); + _focusNode.requestFocus(); + }, + child: Chip( + backgroundColor: ColorsManager.whiteColors, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), ), - child: TextField( - controller: _controller, - style: textStyle, - decoration: const InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsetsDirectional.symmetric( - horizontal: 8, + label: Visibility( + visible: isEditing, + replacement: Text( + widget.subSpace.name, + style: textStyle, + ), + child: SizedBox( + width: context.screenWidth * 0.065, + height: context.screenHeight * 0.025, + child: TextField( + focusNode: _focusNode, + controller: _controller, + style: textStyle, + decoration: const InputDecoration.collapsed(hintText: ''), + onTapOutside: (_) => _onFinishEditing(), + onSubmitted: (value) { + final bloc = context.read(); + bloc.add( + UpdateSpaceDetailsSubspaces( + bloc.state.subspaces + .map( + (e) => e.uuid == widget.subSpace.uuid + ? e.copyWith(name: value) + : e, + ) + .toList(), + ), + ); + _onFinishEditing(); + }, ), ), - onSubmitted: (value) { - final bloc = context.read(); - bloc.add( - UpdateSpaceDetailsSubspaces( - bloc.state.subspaces - .map( - (e) => e.uuid == widget.subSpace.uuid - ? e.copyWith(name: value) - : e, - ) - .toList(), - ), - ); - }, ), ), ); } + + void _onFinishEditing() { + setState(() => isEditing = false); + _focusNode.unfocus(); + } }