chitfund/luckychit/lib/features/settings/settings_page.dart

910 lines
30 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../core/controllers/theme_controller.dart';
import '../../core/services/auth_service.dart';
import '../../core/services/api_service.dart';
import '../../core/utils/snackbar_util.dart';
class SettingsPage extends StatelessWidget {
const SettingsPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Settings'),
),
body: ListView(
padding: EdgeInsets.all(16.w),
children: [
_buildSectionHeader(context, 'Appearance'),
_buildThemeSettings(context),
SizedBox(height: 24.h),
_buildSectionHeader(context, 'Account'),
_buildAccountSettings(context),
SizedBox(height: 24.h),
Obx(() {
final user = AuthService.to.currentUser.value;
if (user?.role == 'manager') {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionHeader(context, 'Payment Settings'),
_buildPaymentSettings(context),
SizedBox(height: 24.h),
],
);
}
return const SizedBox.shrink();
}),
_buildSectionHeader(context, 'Notifications'),
_buildNotificationSettings(context),
SizedBox(height: 24.h),
_buildSectionHeader(context, 'About'),
_buildAboutSettings(context),
SizedBox(height: 32.h),
_buildLogoutButton(context),
],
),
);
}
Widget _buildSectionHeader(BuildContext context, String title) {
final scheme = Theme.of(context).colorScheme;
return Padding(
padding: EdgeInsets.only(left: 8.w, bottom: 12.h),
child: Text(
title,
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: scheme.onSurfaceVariant,
letterSpacing: 0.5,
),
),
);
}
Widget _buildThemeSettings(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Card(
child: Column(
children: [
Obx(() {
return ListTile(
leading: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: scheme.primaryContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
ThemeController.to.getThemeIcon(),
color: scheme.onPrimaryContainer,
size: 24.w,
),
),
title: Text(
'Theme',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
subtitle: Text(
ThemeController.to.getThemeModeString(),
style: TextStyle(fontSize: 14.sp),
),
trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w),
onTap: () => _showThemeDialog(context),
);
}),
Divider(height: 1.h, indent: 72.w),
Obx(() {
return SwitchListTile(
secondary: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: scheme.tertiaryContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
Icons.brightness_6_rounded,
color: scheme.onTertiaryContainer,
size: 24.w,
),
),
title: Text(
'Dark Mode',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
subtitle: Text(
'Override system settings',
style: TextStyle(fontSize: 14.sp),
),
value: ThemeController.to.isDarkMode,
onChanged: (value) {
ThemeController.to.toggleTheme();
},
activeColor: scheme.primary,
);
}),
],
),
);
}
Widget _buildAccountSettings(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Card(
child: Column(
children: [
Obx(() {
final user = AuthService.to.currentUser.value;
return ListTile(
leading: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: scheme.primaryContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
Icons.person_rounded,
color: scheme.onPrimaryContainer,
size: 24.w,
),
),
title: Text(
user?.fullName ?? 'User',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
subtitle: Text(
user?.mobileNumber ?? '',
style: TextStyle(fontSize: 14.sp),
),
trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w),
onTap: () {
SnackbarUtil.showInfo('Profile page coming soon');
},
);
}),
Divider(height: 1.h, indent: 72.w),
// Change Password
ListTile(
leading: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: scheme.secondaryContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
Icons.lock_rounded,
color: scheme.onSecondaryContainer,
size: 24.w,
),
),
title: Text(
'Change Password',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
subtitle: Text(
'Update your password',
style: TextStyle(fontSize: 14.sp),
),
trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w),
onTap: () {
SnackbarUtil.showInfo('Change password feature coming soon');
},
),
],
),
);
}
Widget _buildNotificationSettings(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Card(
child: Column(
children: [
SwitchListTile(
secondary: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: scheme.errorContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
Icons.notifications_rounded,
color: scheme.onErrorContainer,
size: 24.w,
),
),
title: Text(
'Push Notifications',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
subtitle: Text(
'Receive push notifications',
style: TextStyle(fontSize: 14.sp),
),
value: true,
onChanged: (value) {
SnackbarUtil.showInfo('Notification settings coming soon');
},
activeColor: scheme.primary,
),
Divider(height: 1.h, indent: 72.w),
SwitchListTile(
secondary: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: scheme.tertiaryContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
Icons.payment_rounded,
color: scheme.onTertiaryContainer,
size: 24.w,
),
),
title: Text(
'Payment Reminders',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
subtitle: Text(
'Reminders for upcoming payments',
style: TextStyle(fontSize: 14.sp),
),
value: true,
onChanged: (value) {
SnackbarUtil.showInfo('Notification settings coming soon');
},
activeColor: scheme.primary,
),
Divider(height: 1.h, indent: 72.w),
SwitchListTile(
secondary: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: scheme.primaryContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
Icons.casino_rounded,
color: scheme.onPrimaryContainer,
size: 24.w,
),
),
title: Text(
'Draw Notifications',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
subtitle: Text(
'Alerts for lottery draws',
style: TextStyle(fontSize: 14.sp),
),
value: true,
onChanged: (value) {
SnackbarUtil.showInfo('Notification settings coming soon');
},
activeColor: scheme.primary,
),
],
),
);
}
Widget _buildPaymentSettings(BuildContext context) {
final apiService = ApiService();
final scheme = Theme.of(context).colorScheme;
return Card(
child: Column(
children: [
FutureBuilder<Map<String, dynamic>>(
future: apiService.get('/payments/phonepe/settings/upi'),
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return ListTile(
leading: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: scheme.secondaryContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
Icons.account_balance_rounded,
color: scheme.onSecondaryContainer,
size: 24.w,
),
),
title: Text(
'UPI ID',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
subtitle: Text(
'Loading...',
style: TextStyle(fontSize: 14.sp),
),
);
}
final upiId = snapshot.data?['data']?['upi_id'] ?? 'Not configured';
final isConfigured = snapshot.data?['data']?['is_configured'] ?? false;
return ListTile(
leading: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: isConfigured
? scheme.secondaryContainer
: scheme.tertiaryContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
Icons.account_balance_rounded,
color: isConfigured
? scheme.onSecondaryContainer
: scheme.onTertiaryContainer,
size: 24.w,
),
),
title: Row(
children: [
Text(
'UPI ID',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
if (isConfigured) ...[
SizedBox(width: 8.w),
Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.h),
decoration: BoxDecoration(
color: scheme.primaryContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Text(
'Active',
style: TextStyle(
fontSize: 10.sp,
fontWeight: FontWeight.w600,
color: scheme.onPrimaryContainer,
),
),
),
],
],
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 4.h),
SelectableText(
upiId,
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: isConfigured
? scheme.onSecondaryContainer
: scheme.onTertiaryContainer,
fontFamily: 'monospace',
),
),
if (!isConfigured) ...[
SizedBox(height: 4.h),
Text(
'Configure in backend/.env',
style: TextStyle(
fontSize: 12.sp,
color: scheme.tertiary,
fontStyle: FontStyle.italic,
),
),
],
],
),
trailing: isConfigured
? IconButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: upiId));
SnackbarUtil.showSuccess('UPI ID copied to clipboard');
},
icon: Icon(
Icons.copy_rounded,
size: 20.w,
color: scheme.secondary,
),
tooltip: 'Copy UPI ID',
)
: Icon(
Icons.warning_rounded,
size: 20.w,
color: scheme.tertiary,
),
onTap: () => _showUPIInfoDialog(context, upiId, isConfigured),
);
},
),
Divider(height: 1.h, indent: 72.w),
// Payment Statistics
ListTile(
leading: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: scheme.primaryContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
Icons.payment_rounded,
color: scheme.onPrimaryContainer,
size: 24.w,
),
),
title: Text(
'Payment Statistics',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
subtitle: Text(
'View payment insights',
style: TextStyle(fontSize: 14.sp),
),
trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w),
onTap: () {
SnackbarUtil.showInfo('Payment statistics coming soon');
},
),
Divider(height: 1.h, indent: 72.w),
ListTile(
leading: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: scheme.primaryContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
Icons.currency_rupee_rounded,
color: scheme.onPrimaryContainer,
size: 24.w,
),
),
title: Text(
'Transaction Fees',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
subtitle: Text(
'0% fees • Save lakhs per year!',
style: TextStyle(
fontSize: 14.sp,
color: scheme.primary,
fontWeight: FontWeight.w600,
),
),
trailing: Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
decoration: BoxDecoration(
color: scheme.primaryContainer,
borderRadius: BorderRadius.circular(8.r),
),
child: Text(
'FREE',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w800,
color: scheme.onPrimaryContainer,
),
),
),
),
],
),
);
}
void _showUPIInfoDialog(BuildContext context, String upiId, bool isConfigured) {
final scheme = Theme.of(context).colorScheme;
Get.dialog(
AlertDialog(
title: Row(
children: [
Icon(
Icons.account_balance_rounded,
color: scheme.secondary,
size: 24.w,
),
SizedBox(width: 12.w),
const Text('UPI Payment Settings'),
],
),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Current UPI ID',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: scheme.onSurfaceVariant,
),
),
SizedBox(height: 8.h),
Container(
width: double.infinity,
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: isConfigured
? scheme.secondaryContainer
: scheme.tertiaryContainer,
borderRadius: BorderRadius.circular(8.r),
border: Border.all(
color: isConfigured
? scheme.outlineVariant
: scheme.tertiary.withOpacity(0.4),
),
),
child: Row(
children: [
Expanded(
child: SelectableText(
upiId,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w700,
color: isConfigured
? scheme.onSecondaryContainer
: scheme.onTertiaryContainer,
fontFamily: 'monospace',
),
),
),
if (isConfigured)
IconButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: upiId));
SnackbarUtil.showSuccess('Copied!');
},
icon: Icon(
Icons.copy_rounded,
size: 20.w,
color: scheme.secondary,
),
),
],
),
),
if (!isConfigured) ...[
SizedBox(height: 16.h),
Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: scheme.tertiaryContainer,
borderRadius: BorderRadius.circular(8.r),
),
child: Row(
children: [
Icon(
Icons.warning_rounded,
color: scheme.onTertiaryContainer,
size: 20.w,
),
SizedBox(width: 8.w),
Expanded(
child: Text(
'UPI ID not configured. Update backend/.env file.',
style: TextStyle(
fontSize: 12.sp,
color: scheme.onTertiaryContainer,
),
),
),
],
),
),
],
SizedBox(height: 16.h),
const Divider(),
SizedBox(height: 16.h),
Text(
'How to Update UPI ID',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: scheme.onSurfaceVariant,
),
),
SizedBox(height: 12.h),
_buildInfoStep(context, '1', 'Open backend/.env file'),
_buildInfoStep(context, '2', 'Update PHONEPE_UPI_ID=your_upi@paytm'),
_buildInfoStep(context, '3', 'Restart backend server'),
_buildInfoStep(context, '4', 'Refresh this screen'),
SizedBox(height: 16.h),
Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: scheme.primaryContainer,
borderRadius: BorderRadius.circular(8.r),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.lightbulb_rounded,
color: scheme.onPrimaryContainer,
size: 18.w,
),
SizedBox(width: 8.w),
Text(
'Pro Tip',
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w600,
color: scheme.onPrimaryContainer,
),
),
],
),
SizedBox(height: 8.h),
Text(
'Members can pay using ANY UPI app (PhonePe, GPay, Paytm) directly to your personal UPI ID with 0% transaction fees!',
style: TextStyle(
fontSize: 12.sp,
color: scheme.onPrimaryContainer.withOpacity(0.92),
height: 1.4,
),
),
],
),
),
],
),
),
actions: [
TextButton(
onPressed: () => Get.back(),
child: const Text('Close'),
),
],
),
);
}
Widget _buildInfoStep(BuildContext context, String number, String text) {
final scheme = Theme.of(context).colorScheme;
return Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 24.w,
height: 24.w,
decoration: BoxDecoration(
color: scheme.secondaryContainer,
shape: BoxShape.circle,
),
child: Center(
child: Text(
number,
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w700,
color: scheme.onSecondaryContainer,
),
),
),
),
SizedBox(width: 12.w),
Expanded(
child: Padding(
padding: EdgeInsets.only(top: 2.h),
child: Text(
text,
style: TextStyle(
fontSize: 13.sp,
color: scheme.onSurfaceVariant,
height: 1.4,
),
),
),
),
],
),
);
}
Widget _buildAboutSettings(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Card(
child: Column(
children: [
ListTile(
leading: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: scheme.primaryContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
Icons.info_rounded,
color: scheme.onPrimaryContainer,
size: 24.w,
),
),
title: Text(
'App Version',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
subtitle: Text(
'1.0.0',
style: TextStyle(fontSize: 14.sp),
),
),
Divider(height: 1.h, indent: 72.w),
ListTile(
leading: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: scheme.primaryContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
Icons.privacy_tip_rounded,
color: scheme.onPrimaryContainer,
size: 24.w,
),
),
title: Text(
'Privacy Policy',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w),
onTap: () {
SnackbarUtil.showInfo('Privacy policy page coming soon');
},
),
Divider(height: 1.h, indent: 72.w),
ListTile(
leading: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: scheme.secondaryContainer,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
Icons.description_rounded,
color: scheme.onSecondaryContainer,
size: 24.w,
),
),
title: Text(
'Terms of Service',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w),
onTap: () {
SnackbarUtil.showInfo('Terms of service page coming soon');
},
),
],
),
);
}
Widget _buildLogoutButton(BuildContext context) {
return SizedBox(
width: double.infinity,
height: 52.h,
child: ElevatedButton.icon(
onPressed: () => _showLogoutDialog(context),
icon: Icon(Icons.logout_rounded, size: 20.w),
label: Text(
'Logout',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.error,
foregroundColor: Theme.of(context).colorScheme.onError,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
),
),
);
}
void _showThemeDialog(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
Get.dialog(
AlertDialog(
title: const Text('Choose Theme'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
RadioListTile<ThemeMode>(
title: const Text('Light'),
value: ThemeMode.light,
groupValue: ThemeController.to.themeMode,
onChanged: (value) {
ThemeController.to.setLightTheme();
Get.back();
},
activeColor: scheme.primary,
),
RadioListTile<ThemeMode>(
title: const Text('Dark'),
value: ThemeMode.dark,
groupValue: ThemeController.to.themeMode,
onChanged: (value) {
ThemeController.to.setDarkTheme();
Get.back();
},
activeColor: scheme.primary,
),
RadioListTile<ThemeMode>(
title: const Text('System Default'),
value: ThemeMode.system,
groupValue: ThemeController.to.themeMode,
onChanged: (value) {
ThemeController.to.setSystemTheme();
Get.back();
},
activeColor: scheme.primary,
),
],
),
),
);
}
void _showLogoutDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Logout'),
content: const Text('Are you sure you want to logout?'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
AuthService.to.logout();
SnackbarUtil.showSuccess('Logged out successfully');
},
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.error,
foregroundColor: Theme.of(context).colorScheme.onError,
),
child: const Text('Logout'),
),
],
),
);
}
}