Refactor SpaceDetailsForm to use StatefulWidget for form validation and improve user experience with dynamic save button state, and added space name validation to not be longer than 50 characters.

This commit is contained in:
Faris Armoush
2025-07-27 11:18:08 +03:00
parent a4391aa73e
commit 1558806cc3
2 changed files with 74 additions and 47 deletions

View File

@ -10,7 +10,7 @@ import 'package:syncrow_web/pages/space_management_v2/modules/update_space/prese
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class SpaceDetailsForm extends StatelessWidget {
class SpaceDetailsForm extends StatefulWidget {
const SpaceDetailsForm({
required this.title,
required this.space,
@ -22,24 +22,52 @@ class SpaceDetailsForm extends StatelessWidget {
final SpaceDetailsModel space;
final void Function(SpaceDetailsModel space) onSave;
@override
State<SpaceDetailsForm> createState() => _SpaceDetailsFormState();
}
class _SpaceDetailsFormState extends State<SpaceDetailsForm> {
final _formKey = GlobalKey<FormState>();
bool _isFormValid = false;
@override
void initState() {
super.initState();
final initialName = widget.space.spaceName;
_isFormValid = initialName.isNotEmpty &&
initialName.length <= 50 &&
!widget.space.subspaces.any((subspace) => subspace.name == initialName);
}
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => SpaceDetailsModelBloc(initialState: space),
create: (context) => SpaceDetailsModelBloc(initialState: widget.space),
child: BlocBuilder<SpaceDetailsModelBloc, SpaceDetailsModel>(
buildWhen: (previous, current) => previous != current,
builder: (context, space) {
return AlertDialog(
title: DefaultTextStyle(
style: context.textTheme.titleLarge!.copyWith(
fontSize: 30,
fontWeight: FontWeight.w400,
color: ColorsManager.blackColor,
),
child: title,
buildWhen: (previous, current) => previous != current,
builder: (context, space) {
return AlertDialog(
title: DefaultTextStyle(
style: context.textTheme.titleLarge!.copyWith(
fontSize: 30,
fontWeight: FontWeight.w400,
color: ColorsManager.blackColor,
),
backgroundColor: ColorsManager.white,
content: SizedBox(
child: widget.title,
),
backgroundColor: ColorsManager.white,
content: Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
onChanged: () {
final isValid = _formKey.currentState?.validate() ?? false;
if (_isFormValid != isValid) {
setState(() {
_isFormValid = isValid;
});
}
},
child: SizedBox(
height: context.screenHeight * 0.3,
width: context.screenWidth * 0.4,
child: Row(
@ -70,14 +98,16 @@ class SpaceDetailsForm extends StatelessWidget {
],
),
),
actions: [
SpaceDetailsActionButtons(
onSave: () => onSave(space),
onCancel: Navigator.of(context).pop,
),
],
);
}),
),
actions: [
SpaceDetailsActionButtons(
onSave: _isFormValid ? () => widget.onSave(space) : null,
onCancel: Navigator.of(context).pop,
),
],
);
},
),
);
}
}

View File

@ -33,12 +33,13 @@ class _SpaceNameTextFieldState extends State<SpaceNameTextField> {
super.dispose();
}
final _formKey = GlobalKey<FormState>();
String? _validateName(String? value) {
if (value == null || value.isEmpty) {
return '*Space name should not be empty.';
}
if (value.length > 50) {
return '*Space name cannot be longer than 50 characters.';
}
if (widget.isNameFieldExist(value)) {
return '*Name already exists';
}
@ -47,30 +48,26 @@ class _SpaceNameTextFieldState extends State<SpaceNameTextField> {
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: TextFormField(
controller: _controller,
onChanged: (value) => context.read<SpaceDetailsModelBloc>().add(
UpdateSpaceDetailsName(value),
),
validator: _validateName,
style: context.textTheme.bodyMedium,
decoration: InputDecoration(
hintText: 'Please enter the name',
hintStyle: context.textTheme.bodyMedium!.copyWith(
color: ColorsManager.lightGrayColor,
),
filled: true,
fillColor: ColorsManager.boxColor,
enabledBorder: _buildBorder(context, ColorsManager.vividBlue),
focusedBorder: _buildBorder(context, ColorsManager.primaryColor),
errorBorder: _buildBorder(context, context.theme.colorScheme.error),
focusedErrorBorder: _buildBorder(context, context.theme.colorScheme.error),
errorStyle: context.textTheme.bodySmall?.copyWith(
color: context.theme.colorScheme.error,
return TextFormField(
controller: _controller,
onChanged: (value) => context.read<SpaceDetailsModelBloc>().add(
UpdateSpaceDetailsName(value),
),
validator: _validateName,
style: context.textTheme.bodyMedium,
decoration: InputDecoration(
hintText: 'Please enter the name',
hintStyle: context.textTheme.bodyMedium!.copyWith(
color: ColorsManager.lightGrayColor,
),
filled: true,
fillColor: ColorsManager.boxColor,
enabledBorder: _buildBorder(context, ColorsManager.vividBlue),
focusedBorder: _buildBorder(context, ColorsManager.primaryColor),
errorBorder: _buildBorder(context, context.theme.colorScheme.error),
focusedErrorBorder: _buildBorder(context, context.theme.colorScheme.error),
errorStyle: context.textTheme.bodySmall?.copyWith(
color: context.theme.colorScheme.error,
),
),
);