Refactor CustomExpansionTile and SpaceDetailsDialogHelper for improved functionality and UI consistency

- Updated CustomExpansionTile to enhance selection state handling and UI responsiveness.
- Integrated success and error snackbars in SpaceDetailsDialogHelper for better user feedback during space creation and updates.
- Removed redundant comments and improved code readability.
This commit is contained in:
Faris Armoush
2025-07-24 14:51:07 +03:00
parent 6943ee07ac
commit 7d77809750
2 changed files with 27 additions and 40 deletions

View File

@ -5,19 +5,20 @@ class CustomExpansionTile extends StatefulWidget {
final String title; final String title;
final List<Widget>? children; final List<Widget>? children;
final bool initiallyExpanded; final bool initiallyExpanded;
final bool isSelected; // Add this to track selection final bool isSelected;
final bool? isExpanded; // External control over expansion final bool? isExpanded;
final ValueChanged<bool>? onExpansionChanged; // Notify when expansion changes final ValueChanged<bool>? onExpansionChanged;
final VoidCallback? onItemSelected; // Callback for selecting the item final VoidCallback? onItemSelected;
CustomExpansionTile({ const CustomExpansionTile({
super.key,
required this.title, required this.title,
this.children, this.children,
this.initiallyExpanded = false, this.initiallyExpanded = false,
this.isExpanded, // Allow external control over expansion this.isExpanded,
this.onExpansionChanged, // Notify when expansion changes this.onExpansionChanged,
this.onItemSelected, // Trigger item selection when name is tapped this.onItemSelected,
required this.isSelected, // Add this to initialize selection state required this.isSelected,
}); });
@override @override
@ -25,7 +26,7 @@ class CustomExpansionTile extends StatefulWidget {
} }
class CustomExpansionTileState extends State<CustomExpansionTile> { class CustomExpansionTileState extends State<CustomExpansionTile> {
bool _isExpanded = false; // Local expansion state bool _isExpanded = false;
@override @override
void initState() { void initState() {
@ -36,7 +37,6 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
@override @override
void didUpdateWidget(CustomExpansionTile oldWidget) { void didUpdateWidget(CustomExpansionTile oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
// Sync local state with external control of expansion state
if (widget.isExpanded != null && widget.isExpanded != _isExpanded) { if (widget.isExpanded != null && widget.isExpanded != _isExpanded) {
setState(() { setState(() {
_isExpanded = widget.isExpanded!; _isExpanded = widget.isExpanded!;
@ -44,7 +44,6 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
} }
} }
// Utility function to capitalize the first letter of the title
String _capitalizeFirstLetter(String text) { String _capitalizeFirstLetter(String text) {
if (text.isEmpty) return text; if (text.isEmpty) return text;
return text[0].toUpperCase() + text.substring(1); return text[0].toUpperCase() + text.substring(1);
@ -56,7 +55,6 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
children: [ children: [
Row( Row(
children: [ children: [
// Expand/collapse icon, now wrapped in a GestureDetector for specific onTap
if (widget.children != null && widget.children!.isNotEmpty) if (widget.children != null && widget.children!.isNotEmpty)
GestureDetector( GestureDetector(
onTap: () { onTap: () {
@ -70,38 +68,33 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
? Icons.keyboard_arrow_down ? Icons.keyboard_arrow_down
: Icons.keyboard_arrow_right, : Icons.keyboard_arrow_right,
color: ColorsManager.lightGrayColor, color: ColorsManager.lightGrayColor,
size: 16.0, // Adjusted size for better alignment size: 16,
), ),
), ),
// The title text, wrapped in GestureDetector to handle selection
Expanded( Expanded(
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
if (widget.onItemSelected != null) { if (widget.onItemSelected != null) {
widget.onItemSelected!(); widget.onItemSelected!.call();
} }
}, },
child: Text( child: Text(
_capitalizeFirstLetter(widget.title), _capitalizeFirstLetter(widget.title),
style: TextStyle( style: TextStyle(
color: widget.isSelected color: widget.isSelected
? ColorsManager ? ColorsManager.blackColor
.blackColor // Change color to black when selected : ColorsManager.lightGrayColor,
: ColorsManager fontWeight:
.lightGrayColor, // Gray when not selected widget.isSelected ? FontWeight.w600 : FontWeight.w400,
fontWeight: FontWeight.w400,
), ),
), ),
), ),
), ),
], ],
), ),
// The expanded section (children) that shows when the tile is expanded if (_isExpanded && widget.children != null && widget.children!.isNotEmpty)
if (_isExpanded &&
widget.children != null &&
widget.children!.isNotEmpty)
Padding( Padding(
padding: const EdgeInsets.only(left: 24.0), // Indented children padding: const EdgeInsets.only(left: 24),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: widget.children!, children: widget.children!,

View File

@ -12,6 +12,7 @@ import 'package:syncrow_web/pages/space_management_v2/modules/update_space/data/
import 'package:syncrow_web/pages/space_management_v2/modules/update_space/domain/params/update_space_param.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/update_space/domain/params/update_space_param.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/update_space_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/update_space_bloc.dart';
import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/extension/app_snack_bar.dart';
abstract final class SpaceDetailsDialogHelper { abstract final class SpaceDetailsDialogHelper {
static void showCreate( static void showCreate(
@ -140,6 +141,9 @@ abstract final class SpaceDetailsDialogHelper {
Navigator.of(context).pop(); Navigator.of(context).pop();
Navigator.of(context).pop(); Navigator.of(context).pop();
onSuccess?.call(space); onSuccess?.call(space);
context.showSuccessSnackbar(
'${space.spaceName} space was updated successfully',
);
} }
static void _onLoading(BuildContext context) { static void _onLoading(BuildContext context) {
@ -152,20 +156,7 @@ abstract final class SpaceDetailsDialogHelper {
static void _onError(BuildContext context, String errorMessage) { static void _onError(BuildContext context, String errorMessage) {
Navigator.of(context).pop(); Navigator.of(context).pop();
showDialog<void>( context.showFailureSnackbar(errorMessage);
context: context,
barrierDismissible: false,
builder: (_) => AlertDialog(
title: const Text('Error'),
content: Text(errorMessage),
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: const Text('OK'),
),
],
),
);
} }
static void _onCreateSuccess( static void _onCreateSuccess(
@ -176,5 +167,8 @@ abstract final class SpaceDetailsDialogHelper {
Navigator.of(context).pop(); Navigator.of(context).pop();
Navigator.of(context).pop(); Navigator.of(context).pop();
onSuccess?.call(space); onSuccess?.call(space);
context.showSuccessSnackbar(
'${space.spaceName} space created successfully',
);
} }
} }