chitfund/luckychit/lib/shared/widgets/empty_state_widget.dart

323 lines
9.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../l10n/l10n_x.dart';
enum EmptyStateType {
noGroups,
noMembers,
noPayments,
noActivities,
noResults,
error,
noInternet,
}
class EmptyStateWidget extends StatelessWidget {
final EmptyStateType type;
final String? customTitle;
final String? customMessage;
final String? actionLabel;
final VoidCallback? onActionPressed;
final Widget? customIllustration;
const EmptyStateWidget({
super.key,
required this.type,
this.customTitle,
this.customMessage,
this.actionLabel,
this.onActionPressed,
this.customIllustration,
});
@override
Widget build(BuildContext context) {
final config = _getEmptyStateConfig(context, type);
return Center(
child: SingleChildScrollView(
padding: EdgeInsets.all(32.w),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Illustration or Icon
customIllustration ??
TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: const Duration(milliseconds: 600),
curve: Curves.easeOutBack,
builder: (context, value, child) {
return Transform.scale(
scale: value,
child: child,
);
},
child: Container(
width: 140.w,
height: 140.h,
decoration: BoxDecoration(
color: config.backgroundColor,
shape: BoxShape.circle,
),
child: Icon(
config.icon,
size: 70.w,
color: config.iconColor,
),
),
),
SizedBox(height: 32.h),
// Title
TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: const Duration(milliseconds: 800),
curve: Curves.easeOut,
builder: (context, value, child) {
return Opacity(
opacity: value,
child: Transform.translate(
offset: Offset(0, 20 * (1 - value)),
child: child,
),
);
},
child: Text(
customTitle ?? config.title,
style: TextStyle(
fontSize: 24.sp,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
textAlign: TextAlign.center,
),
),
SizedBox(height: 12.h),
// Message
TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: const Duration(milliseconds: 1000),
curve: Curves.easeOut,
builder: (context, value, child) {
return Opacity(
opacity: value,
child: Transform.translate(
offset: Offset(0, 20 * (1 - value)),
child: child,
),
);
},
child: Text(
customMessage ?? config.message,
style: TextStyle(
fontSize: 16.sp,
color: Colors.grey.shade600,
height: 1.5,
),
textAlign: TextAlign.center,
),
),
SizedBox(height: 32.h),
// Action Button
if (onActionPressed != null)
TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: const Duration(milliseconds: 1200),
curve: Curves.easeOut,
builder: (context, value, child) {
return Opacity(
opacity: value,
child: Transform.translate(
offset: Offset(0, 20 * (1 - value)),
child: child,
),
);
},
child: ElevatedButton.icon(
onPressed: onActionPressed,
icon: Icon(config.actionIcon, size: 20.w),
label: Text(
actionLabel ?? config.actionLabel,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
),
style: ElevatedButton.styleFrom(
backgroundColor: config.buttonColor,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 32.w,
vertical: 16.h,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
elevation: 2,
),
),
),
],
),
),
);
}
_EmptyStateConfig _getEmptyStateConfig(BuildContext context, EmptyStateType type) {
final l = context.l10n;
switch (type) {
case EmptyStateType.noGroups:
return _EmptyStateConfig(
icon: Icons.group_add_rounded,
iconColor: Colors.green.shade600,
backgroundColor: Colors.green.shade50,
title: l.emptyNoGroupsTitle,
message: l.emptyNoGroupsMessage,
actionLabel: l.emptyNoGroupsAction,
actionIcon: Icons.add_circle_outline,
buttonColor: Colors.green.shade600,
);
case EmptyStateType.noMembers:
return _EmptyStateConfig(
icon: Icons.people_outline_rounded,
iconColor: Colors.blue.shade600,
backgroundColor: Colors.blue.shade50,
title: l.emptyNoMembersTitle,
message: l.emptyNoMembersMessage,
actionLabel: l.emptyNoMembersAction,
actionIcon: Icons.person_add,
buttonColor: Colors.blue.shade600,
);
case EmptyStateType.noPayments:
return _EmptyStateConfig(
icon: Icons.payment_rounded,
iconColor: Colors.orange.shade600,
backgroundColor: Colors.orange.shade50,
title: l.emptyNoPaymentsTitle,
message: l.emptyNoPaymentsMessage,
actionLabel: l.emptyNoPaymentsAction,
actionIcon: Icons.add,
buttonColor: Colors.orange.shade600,
);
case EmptyStateType.noActivities:
return _EmptyStateConfig(
icon: Icons.history_rounded,
iconColor: Colors.purple.shade600,
backgroundColor: Colors.purple.shade50,
title: l.emptyNoActivitiesTitle,
message: l.emptyNoActivitiesMessage,
actionLabel: l.emptyNoActivitiesAction,
actionIcon: Icons.refresh,
buttonColor: Colors.purple.shade600,
);
case EmptyStateType.noResults:
return _EmptyStateConfig(
icon: Icons.search_off_rounded,
iconColor: Colors.grey.shade600,
backgroundColor: Colors.grey.shade100,
title: l.emptyNoResultsTitle,
message: l.emptyNoResultsMessage,
actionLabel: l.emptyNoResultsAction,
actionIcon: Icons.clear_all,
buttonColor: Colors.grey.shade600,
);
case EmptyStateType.error:
return _EmptyStateConfig(
icon: Icons.error_outline_rounded,
iconColor: Colors.red.shade600,
backgroundColor: Colors.red.shade50,
title: l.emptyErrorTitle,
message: l.emptyErrorMessage,
actionLabel: l.emptyErrorAction,
actionIcon: Icons.refresh,
buttonColor: Colors.red.shade600,
);
case EmptyStateType.noInternet:
return _EmptyStateConfig(
icon: Icons.wifi_off_rounded,
iconColor: Colors.red.shade600,
backgroundColor: Colors.red.shade50,
title: l.emptyNoInternetTitle,
message: l.emptyNoInternetMessage,
actionLabel: l.emptyNoInternetAction,
actionIcon: Icons.refresh,
buttonColor: Colors.red.shade600,
);
}
}
}
class _EmptyStateConfig {
final IconData icon;
final Color iconColor;
final Color backgroundColor;
final String title;
final String message;
final String actionLabel;
final IconData actionIcon;
final Color buttonColor;
_EmptyStateConfig({
required this.icon,
required this.iconColor,
required this.backgroundColor,
required this.title,
required this.message,
required this.actionLabel,
required this.actionIcon,
required this.buttonColor,
});
}
/// Compact version for smaller spaces
class CompactEmptyState extends StatelessWidget {
final IconData icon;
final String message;
final Color? color;
const CompactEmptyState({
super.key,
required this.icon,
required this.message,
this.color,
});
@override
Widget build(BuildContext context) {
final stateColor = color ?? Colors.grey.shade600;
return Padding(
padding: EdgeInsets.all(24.w),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
size: 48.w,
color: stateColor.withOpacity(0.5),
),
SizedBox(height: 12.h),
Text(
message,
style: TextStyle(
fontSize: 14.sp,
color: Colors.grey.shade600,
),
textAlign: TextAlign.center,
),
],
),
);
}
}