Commit 9ebfaeab authored by Mahmoud Aglan's avatar Mahmoud Aglan

Remove Firebase/Lottie deps, switch OTP to 4 digits

- Commented out firebase_core, firebase_messaging (not configured yet)
- Commented out lottie, flutter_local_notifications (unused)
- OTP input changed from 6 boxes to 4 boxes
- App now works with zero external services
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent d81f3151
PODS: PODS:
- Firebase/CoreOnly (11.15.0):
- FirebaseCore (~> 11.15.0)
- Firebase/Messaging (11.15.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 11.15.0)
- firebase_core (3.15.2):
- Firebase/CoreOnly (= 11.15.0)
- Flutter
- firebase_messaging (15.2.10):
- Firebase/Messaging (= 11.15.0)
- firebase_core
- Flutter
- FirebaseCore (11.15.0):
- FirebaseCoreInternal (~> 11.15.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Logger (~> 8.1)
- FirebaseCoreInternal (11.15.0):
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- FirebaseInstallations (11.15.0):
- FirebaseCore (~> 11.15.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
- PromisesObjC (~> 2.4)
- FirebaseMessaging (11.15.0):
- FirebaseCore (~> 11.15.0)
- FirebaseInstallations (~> 11.0)
- GoogleDataTransport (~> 10.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Reachability (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
- nanopb (~> 3.30910.0)
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_local_notifications (0.0.1):
- Flutter
- flutter_secure_storage (6.0.0): - flutter_secure_storage (6.0.0):
- Flutter - Flutter
- GoogleDataTransport (10.1.0):
- nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4)
- GoogleUtilities/AppDelegateSwizzler (8.1.2):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Privacy
- GoogleUtilities/Environment (8.1.2):
- GoogleUtilities/Privacy
- GoogleUtilities/Logger (8.1.2):
- GoogleUtilities/Environment
- GoogleUtilities/Privacy
- GoogleUtilities/Network (8.1.2):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Privacy
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (8.1.2)":
- GoogleUtilities/Privacy
- GoogleUtilities/Privacy (8.1.2)
- GoogleUtilities/Reachability (8.1.2):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- GoogleUtilities/UserDefaults (8.1.2):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- image_picker_ios (0.0.1): - image_picker_ios (0.0.1):
- Flutter - Flutter
- nanopb (3.30910.0):
- nanopb/decode (= 3.30910.0)
- nanopb/encode (= 3.30910.0)
- nanopb/decode (3.30910.0)
- nanopb/encode (3.30910.0)
- PromisesObjC (2.4.1)
- shared_preferences_foundation (0.0.1): - shared_preferences_foundation (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
...@@ -81,37 +14,16 @@ PODS: ...@@ -81,37 +14,16 @@ PODS:
- Flutter - Flutter
DEPENDENCIES: DEPENDENCIES:
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
SPEC REPOS:
trunk:
- Firebase
- FirebaseCore
- FirebaseCoreInternal
- FirebaseInstallations
- FirebaseMessaging
- GoogleDataTransport
- GoogleUtilities
- nanopb
- PromisesObjC
EXTERNAL SOURCES: EXTERNAL SOURCES:
firebase_core:
:path: ".symlinks/plugins/firebase_core/ios"
firebase_messaging:
:path: ".symlinks/plugins/firebase_messaging/ios"
Flutter: Flutter:
:path: Flutter :path: Flutter
flutter_local_notifications:
:path: ".symlinks/plugins/flutter_local_notifications/ios"
flutter_secure_storage: flutter_secure_storage:
:path: ".symlinks/plugins/flutter_secure_storage/ios" :path: ".symlinks/plugins/flutter_secure_storage/ios"
image_picker_ios: image_picker_ios:
...@@ -124,21 +36,9 @@ EXTERNAL SOURCES: ...@@ -124,21 +36,9 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/url_launcher_ios/ios" :path: ".symlinks/plugins/url_launcher_ios/ios"
SPEC CHECKSUMS: SPEC CHECKSUMS:
Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
firebase_core: 995454a784ff288be5689b796deb9e9fa3601818
firebase_messaging: f4a41dd102ac18b840eba3f39d67e77922d3f707
FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e
FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4
FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843
FirebaseMessaging: 3b26e2cee503815e01c3701236b020aa9b576f09
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_local_notifications: 395056b3175ba4f08480a7c5de30cd36d69827e4
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13 flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 766ace00c6b10d8148408f329d10c4f051931850
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326 image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
PromisesObjC: 752c3227f599e3467650e47ea36f433eeb10c273
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
......
...@@ -202,7 +202,6 @@ ...@@ -202,7 +202,6 @@
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
41BB84C613006DFE94367216 /* [CP] Embed Pods Frameworks */, 41BB84C613006DFE94367216 /* [CP] Embed Pods Frameworks */,
D48FD101326A3B8D976EC3DA /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
...@@ -366,23 +365,6 @@ ...@@ -366,23 +365,6 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
D48FD101326A3B8D976EC3DA /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
......
...@@ -18,7 +18,10 @@ class ApiService { ...@@ -18,7 +18,10 @@ class ApiService {
} }
Future<Map<String, dynamic>> verifyOtp(String phone, String otp) async { Future<Map<String, dynamic>> verifyOtp(String phone, String otp) async {
final res = await _dio.post('/auth/otp/verify', data: {'phone': phone, 'otp': otp}); final res = await _dio.post(
'/auth/otp/verify',
data: {'phone': phone, 'otp': otp},
);
return res.data; return res.data;
} }
...@@ -64,13 +67,22 @@ class ApiService { ...@@ -64,13 +67,22 @@ class ApiService {
return res.data['data']; return res.data['data'];
} }
Future<Map<String, dynamic>> getAttendance(String uuid, {int page = 1}) async { Future<Map<String, dynamic>> getAttendance(
final res = await _dio.get('/participants/$uuid/attendance', queryParameters: {'page': page}); String uuid, {
int page = 1,
}) async {
final res = await _dio.get(
'/participants/$uuid/attendance',
queryParameters: {'page': page},
);
return res.data; return res.data;
} }
Future<Map<String, dynamic>> getInvoices(String uuid, {int page = 1}) async { Future<Map<String, dynamic>> getInvoices(String uuid, {int page = 1}) async {
final res = await _dio.get('/participants/$uuid/invoices', queryParameters: {'page': page}); final res = await _dio.get(
'/participants/$uuid/invoices',
queryParameters: {'page': page},
);
return res.data; return res.data;
} }
...@@ -89,8 +101,14 @@ class ApiService { ...@@ -89,8 +101,14 @@ class ApiService {
return res.data['data']; return res.data['data'];
} }
Future<Map<String, dynamic>> getWalletTransactions(String uuid, {int page = 1}) async { Future<Map<String, dynamic>> getWalletTransactions(
final res = await _dio.get('/participants/$uuid/wallet/transactions', queryParameters: {'page': page}); String uuid, {
int page = 1,
}) async {
final res = await _dio.get(
'/participants/$uuid/wallet/transactions',
queryParameters: {'page': page},
);
return res.data; return res.data;
} }
...@@ -104,14 +122,20 @@ class ApiService { ...@@ -104,14 +122,20 @@ class ApiService {
return res.data['data']; return res.data['data'];
} }
Future<Map<String, dynamic>> getEvaluationDetail(String uuid, String evalUuid) async { Future<Map<String, dynamic>> getEvaluationDetail(
String uuid,
String evalUuid,
) async {
final res = await _dio.get('/participants/$uuid/evaluations/$evalUuid'); final res = await _dio.get('/participants/$uuid/evaluations/$evalUuid');
return res.data['data']; return res.data['data'];
} }
// === Notifications === // === Notifications ===
Future<Map<String, dynamic>> getNotifications({int page = 1}) async { Future<Map<String, dynamic>> getNotifications({int page = 1}) async {
final res = await _dio.get('/notifications', queryParameters: {'page': page}); final res = await _dio.get(
'/notifications',
queryParameters: {'page': page},
);
return res.data; return res.data;
} }
...@@ -128,7 +152,9 @@ class ApiService { ...@@ -128,7 +152,9 @@ class ApiService {
return res.data; return res.data;
} }
Future<void> updateNotificationPreferences(List<Map<String, dynamic>> prefs) async { Future<void> updateNotificationPreferences(
List<Map<String, dynamic>> prefs,
) async {
await _dio.post('/notifications/preferences', data: {'preferences': prefs}); await _dio.post('/notifications/preferences', data: {'preferences': prefs});
} }
...@@ -161,7 +187,10 @@ class ApiService { ...@@ -161,7 +187,10 @@ class ApiService {
// === Payments === // === Payments ===
Future<Map<String, dynamic>> initiatePayment(String invoiceUuid) async { Future<Map<String, dynamic>> initiatePayment(String invoiceUuid) async {
final res = await _dio.post('/payments/initiate', data: {'invoice_uuid': invoiceUuid}); final res = await _dio.post(
'/payments/initiate',
data: {'invoice_uuid': invoiceUuid},
);
return res.data; return res.data;
} }
...@@ -177,7 +206,10 @@ class ApiService { ...@@ -177,7 +206,10 @@ class ApiService {
// === Events === // === Events ===
Future<Map<String, dynamic>> getEvents({int page = 1}) async { Future<Map<String, dynamic>> getEvents({int page = 1}) async {
final res = await _dio.get('/academy/events', queryParameters: {'page': page}); final res = await _dio.get(
'/academy/events',
queryParameters: {'page': page},
);
return res.data; return res.data;
} }
...@@ -186,11 +218,15 @@ class ApiService { ...@@ -186,11 +218,15 @@ class ApiService {
return res.data['data']; return res.data['data'];
} }
Future<Map<String, dynamic>> registerForEvent(String uuid, String participantUuid, {String? notes}) async { Future<Map<String, dynamic>> registerForEvent(
final res = await _dio.post('/events/$uuid/register', data: { String uuid,
'participant_uuid': participantUuid, String participantUuid, {
if (notes != null) 'notes': notes, String? notes,
}); }) async {
final res = await _dio.post(
'/events/$uuid/register',
data: {'participant_uuid': participantUuid, 'notes': ?notes},
);
return res.data; return res.data;
} }
...@@ -200,7 +236,9 @@ class ApiService { ...@@ -200,7 +236,9 @@ class ApiService {
} }
// === Service Requests === // === Service Requests ===
Future<Map<String, dynamic>> createServiceRequest(Map<String, dynamic> data) async { Future<Map<String, dynamic>> createServiceRequest(
Map<String, dynamic> data,
) async {
final res = await _dio.post('/service-requests', data: data); final res = await _dio.post('/service-requests', data: data);
return res.data; return res.data;
} }
...@@ -240,21 +278,28 @@ class ApiService { ...@@ -240,21 +278,28 @@ class ApiService {
required int participantAge, required int participantAge,
required String gender, required String gender,
}) async { }) async {
final res = await _dio.post('/register/pre-register', data: { final res = await _dio.post(
'/register/pre-register',
data: {
'guardian_name_ar': guardianNameAr, 'guardian_name_ar': guardianNameAr,
if (guardianName != null && guardianName.isNotEmpty) 'guardian_name': guardianName, if (guardianName != null && guardianName.isNotEmpty)
'guardian_name': guardianName,
'phone': phone, 'phone': phone,
if (email != null && email.isNotEmpty) 'email': email, if (email != null && email.isNotEmpty) 'email': email,
'participant_name': participantName, 'participant_name': participantName,
'participant_age': participantAge, 'participant_age': participantAge,
'gender': gender, 'gender': gender,
}); },
);
return res.data; return res.data;
} }
// === Academy (public) === // === Academy (public) ===
Future<Map<String, dynamic>> getNews({int page = 1}) async { Future<Map<String, dynamic>> getNews({int page = 1}) async {
final res = await _dio.get('/academy/news', queryParameters: {'page': page}); final res = await _dio.get(
'/academy/news',
queryParameters: {'page': page},
);
return res.data; return res.data;
} }
...@@ -274,18 +319,29 @@ class ApiService { ...@@ -274,18 +319,29 @@ class ApiService {
} }
Future<Map<String, dynamic>> getGallery({int page = 1}) async { Future<Map<String, dynamic>> getGallery({int page = 1}) async {
final res = await _dio.get('/academy/gallery', queryParameters: {'page': page}); final res = await _dio.get(
'/academy/gallery',
queryParameters: {'page': page},
);
return res.data; return res.data;
} }
// === Devices === // === Devices ===
Future<void> registerDevice(String token, String platform, {String? deviceName, String? appVersion}) async { Future<void> registerDevice(
await _dio.post('/devices/register', data: { String token,
String platform, {
String? deviceName,
String? appVersion,
}) async {
await _dio.post(
'/devices/register',
data: {
'token': token, 'token': token,
'platform': platform, 'platform': platform,
if (deviceName != null) 'device_name': deviceName, 'device_name': ?deviceName,
if (appVersion != null) 'app_version': appVersion, 'app_version': ?appVersion,
}); },
);
} }
Future<void> unregisterDevice(String token) async { Future<void> unregisterDevice(String token) async {
......
...@@ -20,8 +20,8 @@ class OtpScreen extends ConsumerStatefulWidget { ...@@ -20,8 +20,8 @@ class OtpScreen extends ConsumerStatefulWidget {
} }
class _OtpScreenState extends ConsumerState<OtpScreen> { class _OtpScreenState extends ConsumerState<OtpScreen> {
final List<TextEditingController> _controllers = List.generate(6, (_) => TextEditingController()); final List<TextEditingController> _controllers = List.generate(4, (_) => TextEditingController());
final List<FocusNode> _focusNodes = List.generate(6, (_) => FocusNode()); final List<FocusNode> _focusNodes = List.generate(4, (_) => FocusNode());
bool _isLoading = false; bool _isLoading = false;
bool _otpSent = false; bool _otpSent = false;
String? _error; String? _error;
...@@ -74,7 +74,7 @@ class _OtpScreenState extends ConsumerState<OtpScreen> { ...@@ -74,7 +74,7 @@ class _OtpScreenState extends ConsumerState<OtpScreen> {
Future<void> _verify() async { Future<void> _verify() async {
final otp = _controllers.map((c) => c.text).join(); final otp = _controllers.map((c) => c.text).join();
if (otp.length != 6) return; if (otp.length != 4) return;
setState(() { setState(() {
_isLoading = true; _isLoading = true;
...@@ -137,7 +137,7 @@ class _OtpScreenState extends ConsumerState<OtpScreen> { ...@@ -137,7 +137,7 @@ class _OtpScreenState extends ConsumerState<OtpScreen> {
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(6, (i) => _buildOtpBox(i)), children: List.generate(4, (i) => _buildOtpBox(i)),
), ),
).animate(delay: 200.ms).fadeIn(duration: 500.ms).slideY(begin: 0.1, end: 0), ).animate(delay: 200.ms).fadeIn(duration: 500.ms).slideY(begin: 0.1, end: 0),
if (_error != null) ...[ if (_error != null) ...[
...@@ -216,14 +216,14 @@ class _OtpScreenState extends ConsumerState<OtpScreen> { ...@@ -216,14 +216,14 @@ class _OtpScreenState extends ConsumerState<OtpScreen> {
), ),
), ),
onChanged: (v) { onChanged: (v) {
if (v.isNotEmpty && index < 5) { if (v.isNotEmpty && index < 3) {
_focusNodes[index + 1].requestFocus(); _focusNodes[index + 1].requestFocus();
} }
if (v.isEmpty && index > 0) { if (v.isEmpty && index > 0) {
_focusNodes[index - 1].requestFocus(); _focusNodes[index - 1].requestFocus();
} }
setState(() {}); setState(() {});
if (index == 5 && v.isNotEmpty) { if (index == 3 && v.isNotEmpty) {
_verify(); _verify();
} }
}, },
......
...@@ -9,7 +9,9 @@ import '../../core/theme/app_theme.dart'; ...@@ -9,7 +9,9 @@ import '../../core/theme/app_theme.dart';
import '../../core/utils/money.dart'; import '../../core/utils/money.dart';
import '../../core/widgets/animated_card.dart'; import '../../core/widgets/animated_card.dart';
final dashboardProvider = FutureProvider.autoDispose<Map<String, dynamic>>((ref) async { final dashboardProvider = FutureProvider.autoDispose<Map<String, dynamic>>((
ref,
) async {
return ref.watch(apiServiceProvider).getDashboard(); return ref.watch(apiServiceProvider).getDashboard();
}); });
...@@ -27,7 +29,9 @@ class DashboardScreen extends ConsumerWidget { ...@@ -27,7 +29,9 @@ class DashboardScreen extends ConsumerWidget {
body: RefreshIndicator( body: RefreshIndicator(
onRefresh: () async => ref.invalidate(dashboardProvider), onRefresh: () async => ref.invalidate(dashboardProvider),
child: CustomScrollView( child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(parent: BouncingScrollPhysics()), physics: const AlwaysScrollableScrollPhysics(
parent: BouncingScrollPhysics(),
),
slivers: [ slivers: [
// Header // Header
SliverToBoxAdapter( SliverToBoxAdapter(
...@@ -40,9 +44,15 @@ class DashboardScreen extends ConsumerWidget { ...@@ -40,9 +44,15 @@ class DashboardScreen extends ConsumerWidget {
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: const BorderRadius.vertical(bottom: Radius.circular(24)), borderRadius: const BorderRadius.vertical(
bottom: Radius.circular(24),
),
boxShadow: [ boxShadow: [
BoxShadow(color: Colors.black.withValues(alpha: 0.03), blurRadius: 10, offset: const Offset(0, 4)), BoxShadow(
color: Colors.black.withValues(alpha: 0.03),
blurRadius: 10,
offset: const Offset(0, 4),
),
], ],
), ),
child: Column( child: Column(
...@@ -56,12 +66,18 @@ class DashboardScreen extends ConsumerWidget { ...@@ -56,12 +66,18 @@ class DashboardScreen extends ConsumerWidget {
children: [ children: [
Text( Text(
_greeting(l), _greeting(l),
style: TextStyle(fontSize: 14, color: Colors.grey.shade500), style: TextStyle(
fontSize: 14,
color: Colors.grey.shade500,
),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
auth.userName ?? '', auth.userName ?? '',
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold), style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
),
), ),
], ],
), ),
...@@ -70,13 +86,21 @@ class DashboardScreen extends ConsumerWidget { ...@@ -70,13 +86,21 @@ class DashboardScreen extends ConsumerWidget {
width: 48, width: 48,
height: 48, height: 48,
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppTheme.primaryColor.withValues(alpha: 0.1), color: AppTheme.primaryColor.withValues(
alpha: 0.1,
),
borderRadius: BorderRadius.circular(14), borderRadius: BorderRadius.circular(14),
), ),
child: Icon(Icons.person, color: AppTheme.primaryColor), child: Icon(
Icons.person,
color: AppTheme.primaryColor,
),
), ),
], ],
).animate().fadeIn(duration: 500.ms).slideY(begin: -0.1, end: 0), )
.animate()
.fadeIn(duration: 500.ms)
.slideY(begin: -0.1, end: 0),
], ],
), ),
), ),
...@@ -91,9 +115,16 @@ class DashboardScreen extends ConsumerWidget { ...@@ -91,9 +115,16 @@ class DashboardScreen extends ConsumerWidget {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Icon(Icons.error_outline, size: 48, color: Colors.grey.shade300), Icon(
Icons.error_outline,
size: 48,
color: Colors.grey.shade300,
),
const SizedBox(height: 12), const SizedBox(height: 12),
Text(l.t('error_occurred'), style: TextStyle(color: Colors.grey.shade500)), Text(
l.t('error_occurred'),
style: TextStyle(color: Colors.grey.shade500),
),
const SizedBox(height: 12), const SizedBox(height: 12),
TextButton( TextButton(
onPressed: () => ref.invalidate(dashboardProvider), onPressed: () => ref.invalidate(dashboardProvider),
...@@ -114,11 +145,16 @@ class DashboardScreen extends ConsumerWidget { ...@@ -114,11 +145,16 @@ class DashboardScreen extends ConsumerWidget {
if ((data['children'] as List?)?.isNotEmpty ?? false) ...[ if ((data['children'] as List?)?.isNotEmpty ?? false) ...[
_SectionHeader(title: l.t('my_children')), _SectionHeader(title: l.t('my_children')),
const SizedBox(height: 12), const SizedBox(height: 12),
..._buildChildrenCards(context, data['children'] as List, l), ..._buildChildrenCards(
context,
data['children'] as List,
l,
),
const SizedBox(height: 20), const SizedBox(height: 20),
], ],
// Today sessions // Today sessions
if ((data['today_sessions'] as List?)?.isNotEmpty ?? false) ...[ if ((data['today_sessions'] as List?)?.isNotEmpty ??
false) ...[
_SectionHeader(title: l.t('today_sessions')), _SectionHeader(title: l.t('today_sessions')),
const SizedBox(height: 12), const SizedBox(height: 12),
..._buildSessionCards(data['today_sessions'] as List), ..._buildSessionCards(data['today_sessions'] as List),
...@@ -142,7 +178,11 @@ class DashboardScreen extends ConsumerWidget { ...@@ -142,7 +178,11 @@ class DashboardScreen extends ConsumerWidget {
return hour < 12 ? l.t('good_morning') : l.t('good_evening'); return hour < 12 ? l.t('good_morning') : l.t('good_evening');
} }
List<Widget> _buildChildrenCards(BuildContext context, List children, AppLocalizations l) { List<Widget> _buildChildrenCards(
BuildContext context,
List children,
AppLocalizations l,
) {
return children.asMap().entries.map((entry) { return children.asMap().entries.map((entry) {
final i = entry.key; final i = entry.key;
final child = entry.value as Map<String, dynamic>; final child = entry.value as Map<String, dynamic>;
...@@ -162,7 +202,10 @@ class DashboardScreen extends ConsumerWidget { ...@@ -162,7 +202,10 @@ class DashboardScreen extends ConsumerWidget {
child: child['photo_url'] != null child: child['photo_url'] != null
? ClipRRect( ? ClipRRect(
borderRadius: BorderRadius.circular(14), borderRadius: BorderRadius.circular(14),
child: Image.network(child['photo_url'], fit: BoxFit.cover), child: Image.network(
child['photo_url'],
fit: BoxFit.cover,
),
) )
: Icon(Icons.person, color: AppTheme.primaryColor), : Icon(Icons.person, color: AppTheme.primaryColor),
), ),
...@@ -173,27 +216,40 @@ class DashboardScreen extends ConsumerWidget { ...@@ -173,27 +216,40 @@ class DashboardScreen extends ConsumerWidget {
children: [ children: [
Text( Text(
child['name_ar'] ?? '', child['name_ar'] ?? '',
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600), style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
if (child['next_session'] != null) if (child['next_session'] != null)
Text( Text(
'${l.t('next_session')}: ${child['next_session']['group_name']}', '${l.t('next_session')}: ${child['next_session']['group_name']}',
style: TextStyle(fontSize: 12, color: Colors.grey.shade500), style: TextStyle(
fontSize: 12,
color: Colors.grey.shade500,
),
), ),
], ],
), ),
), ),
if ((child['outstanding_balance'] ?? 0) > 0) if ((child['outstanding_balance'] ?? 0) > 0)
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 6,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.red.shade50, color: Colors.red.shade50,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Text( child: Text(
child['outstanding_display'] ?? '', child['outstanding_display'] ?? '',
style: TextStyle(fontSize: 12, color: Colors.red.shade700, fontWeight: FontWeight.w600), style: TextStyle(
fontSize: 12,
color: Colors.red.shade700,
fontWeight: FontWeight.w600,
),
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
...@@ -230,7 +286,11 @@ class DashboardScreen extends ConsumerWidget { ...@@ -230,7 +286,11 @@ class DashboardScreen extends ConsumerWidget {
color: AppTheme.accentColor.withValues(alpha: 0.1), color: AppTheme.accentColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Icon(Icons.sports, color: AppTheme.accentColor, size: 22), child: Icon(
Icons.sports,
color: AppTheme.accentColor,
size: 22,
),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded( Expanded(
...@@ -239,11 +299,17 @@ class DashboardScreen extends ConsumerWidget { ...@@ -239,11 +299,17 @@ class DashboardScreen extends ConsumerWidget {
children: [ children: [
Text( Text(
s['group_name'] ?? '', s['group_name'] ?? '',
style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 14), style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14,
),
), ),
Text( Text(
'${s['start_time']} - ${s['end_time']}', '${s['start_time']} - ${s['end_time']}',
style: TextStyle(fontSize: 12, color: Colors.grey.shade500), style: TextStyle(
fontSize: 12,
color: Colors.grey.shade500,
),
), ),
], ],
), ),
...@@ -288,7 +354,10 @@ class _QuickStats extends StatelessWidget { ...@@ -288,7 +354,10 @@ class _QuickStats extends StatelessWidget {
), ),
), ),
], ],
).animate(delay: 200.ms).fadeIn(duration: 500.ms).slideY(begin: 0.08, end: 0); )
.animate(delay: 200.ms)
.fadeIn(duration: 500.ms)
.slideY(begin: 0.08, end: 0);
} }
} }
...@@ -298,7 +367,12 @@ class _StatCard extends StatelessWidget { ...@@ -298,7 +367,12 @@ class _StatCard extends StatelessWidget {
final String value; final String value;
final Color color; final Color color;
const _StatCard({required this.icon, required this.label, required this.value, required this.color}); const _StatCard({
required this.icon,
required this.label,
required this.value,
required this.color,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -314,9 +388,19 @@ class _StatCard extends StatelessWidget { ...@@ -314,9 +388,19 @@ class _StatCard extends StatelessWidget {
children: [ children: [
Icon(icon, color: color, size: 22), Icon(icon, color: color, size: 22),
const SizedBox(height: 10), const SizedBox(height: 10),
Text(value, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: color)), Text(
value,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: color,
),
),
const SizedBox(height: 4), const SizedBox(height: 4),
Text(label, style: TextStyle(fontSize: 12, color: Colors.grey.shade500)), Text(
label,
style: TextStyle(fontSize: 12, color: Colors.grey.shade500),
),
], ],
), ),
); );
...@@ -327,13 +411,16 @@ class _SectionHeader extends StatelessWidget { ...@@ -327,13 +411,16 @@ class _SectionHeader extends StatelessWidget {
final String title; final String title;
final VoidCallback? onViewAll; final VoidCallback? onViewAll;
const _SectionHeader({required this.title, this.onViewAll}); const _SectionHeader({required this.title}) : onViewAll = null;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
children: [ children: [
Text(title, style: const TextStyle(fontSize: 17, fontWeight: FontWeight.bold)), Text(
title,
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.bold),
),
const Spacer(), const Spacer(),
if (onViewAll != null) if (onViewAll != null)
TextButton( TextButton(
...@@ -366,7 +453,14 @@ class _StatusChip extends StatelessWidget { ...@@ -366,7 +453,14 @@ class _StatusChip extends StatelessWidget {
color: color.withValues(alpha: 0.1), color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Text(label, style: TextStyle(fontSize: 11, color: color, fontWeight: FontWeight.w600)), child: Text(
label,
style: TextStyle(
fontSize: 11,
color: color,
fontWeight: FontWeight.w600,
),
),
); );
} }
} }
...@@ -382,7 +476,12 @@ class _QuickActions extends StatelessWidget { ...@@ -382,7 +476,12 @@ class _QuickActions extends StatelessWidget {
(Icons.event, l.t('events'), '/events', AppTheme.primaryColor), (Icons.event, l.t('events'), '/events', AppTheme.primaryColor),
(Icons.shopping_bag_rounded, l.t('shop'), '/shop', AppTheme.accentColor), (Icons.shopping_bag_rounded, l.t('shop'), '/shop', AppTheme.accentColor),
(Icons.mail_rounded, l.t('messages'), '/messages', Colors.teal), (Icons.mail_rounded, l.t('messages'), '/messages', Colors.teal),
(Icons.description_rounded, l.t('service_requests'), '/service-requests', Colors.purple), (
Icons.description_rounded,
l.t('service_requests'),
'/service-requests',
Colors.purple,
),
]; ];
return Column( return Column(
...@@ -416,7 +515,10 @@ class _QuickActions extends StatelessWidget { ...@@ -416,7 +515,10 @@ class _QuickActions extends StatelessWidget {
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
label, label,
style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w500), style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center, textAlign: TextAlign.center,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
...@@ -426,7 +528,11 @@ class _QuickActions extends StatelessWidget { ...@@ -426,7 +528,11 @@ class _QuickActions extends StatelessWidget {
) )
.animate(delay: Duration(milliseconds: 400 + (i * 80))) .animate(delay: Duration(milliseconds: 400 + (i * 80)))
.fadeIn(duration: 400.ms) .fadeIn(duration: 400.ms)
.scale(begin: const Offset(0.8, 0.8), end: const Offset(1, 1), curve: Curves.easeOutBack); .scale(
begin: const Offset(0.8, 0.8),
end: const Offset(1, 1),
curve: Curves.easeOutBack,
);
}).toList(), }).toList(),
), ),
], ],
......
...@@ -7,15 +7,15 @@ import '../../core/l10n/app_localizations.dart'; ...@@ -7,15 +7,15 @@ import '../../core/l10n/app_localizations.dart';
import '../../core/theme/app_theme.dart'; import '../../core/theme/app_theme.dart';
import '../../core/utils/money.dart'; import '../../core/utils/money.dart';
final participantDetailProvider = final participantDetailProvider = FutureProvider.autoDispose
FutureProvider.autoDispose.family<Map<String, dynamic>, String>((ref, uuid) async { .family<Map<String, dynamic>, String>((ref, uuid) async {
return ref.watch(apiServiceProvider).getParticipant(uuid); return ref.watch(apiServiceProvider).getParticipant(uuid);
}); });
final participantSummaryProvider = final participantSummaryProvider = FutureProvider.autoDispose
FutureProvider.autoDispose.family<Map<String, dynamic>, String>((ref, uuid) async { .family<Map<String, dynamic>, String>((ref, uuid) async {
return ref.watch(apiServiceProvider).getParticipantSummary(uuid); return ref.watch(apiServiceProvider).getParticipantSummary(uuid);
}); });
class ParticipantDetailScreen extends ConsumerWidget { class ParticipantDetailScreen extends ConsumerWidget {
final String uuid; final String uuid;
...@@ -47,7 +47,9 @@ class ParticipantDetailScreen extends ConsumerWidget { ...@@ -47,7 +47,9 @@ class ParticipantDetailScreen extends ConsumerWidget {
), ),
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.vertical(bottom: Radius.circular(24)), borderRadius: BorderRadius.vertical(
bottom: Radius.circular(24),
),
), ),
child: Column( child: Column(
children: [ children: [
...@@ -63,7 +65,10 @@ class ParticipantDetailScreen extends ConsumerWidget { ...@@ -63,7 +65,10 @@ class ParticipantDetailScreen extends ConsumerWidget {
color: Colors.grey.shade100, color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: const Icon(Icons.arrow_back_ios_new, size: 18), child: const Icon(
Icons.arrow_back_ios_new,
size: 18,
),
), ),
), ),
], ],
...@@ -78,8 +83,17 @@ class ParticipantDetailScreen extends ConsumerWidget { ...@@ -78,8 +83,17 @@ class ParticipantDetailScreen extends ConsumerWidget {
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: participant['photo_url'] != null child: participant['photo_url'] != null
? ClipOval(child: Image.network(participant['photo_url'], fit: BoxFit.cover)) ? ClipOval(
: Icon(Icons.person, size: 40, color: AppTheme.primaryColor), child: Image.network(
participant['photo_url'],
fit: BoxFit.cover,
),
)
: Icon(
Icons.person,
size: 40,
color: AppTheme.primaryColor,
),
).animate().scale( ).animate().scale(
begin: const Offset(0.7, 0.7), begin: const Offset(0.7, 0.7),
end: const Offset(1, 1), end: const Offset(1, 1),
...@@ -89,7 +103,10 @@ class ParticipantDetailScreen extends ConsumerWidget { ...@@ -89,7 +103,10 @@ class ParticipantDetailScreen extends ConsumerWidget {
const SizedBox(height: 12), const SizedBox(height: 12),
Text( Text(
participant['name_ar'] ?? participant['name'] ?? '', participant['name_ar'] ?? participant['name'] ?? '',
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
).animate(delay: 100.ms).fadeIn(duration: 400.ms), ).animate(delay: 100.ms).fadeIn(duration: 400.ms),
const SizedBox(height: 4), const SizedBox(height: 4),
_StatusBadge(status: participant['status'] ?? 'active'), _StatusBadge(status: participant['status'] ?? 'active'),
...@@ -100,11 +117,15 @@ class ParticipantDetailScreen extends ConsumerWidget { ...@@ -100,11 +117,15 @@ class ParticipantDetailScreen extends ConsumerWidget {
// Summary stats // Summary stats
SliverToBoxAdapter( SliverToBoxAdapter(
child: summary.when( child: summary.when(
loading: () => const SizedBox(height: 80, child: Center(child: CircularProgressIndicator())), loading: () => const SizedBox(
error: (_, __) => const SizedBox.shrink(), height: 80,
child: Center(child: CircularProgressIndicator()),
),
error: (_, _) => const SizedBox.shrink(),
data: (s) => Padding( data: (s) => Padding(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
child: Row( child:
Row(
children: [ children: [
_MiniStat( _MiniStat(
label: l.t('attendance_rate'), label: l.t('attendance_rate'),
...@@ -115,7 +136,9 @@ class ParticipantDetailScreen extends ConsumerWidget { ...@@ -115,7 +136,9 @@ class ParticipantDetailScreen extends ConsumerWidget {
const SizedBox(width: 12), const SizedBox(width: 12),
_MiniStat( _MiniStat(
label: l.t('total_balance'), label: l.t('total_balance'),
value: formatMoney(s['outstanding_balance'] ?? 0), value: formatMoney(
s['outstanding_balance'] ?? 0,
),
icon: Icons.account_balance_wallet, icon: Icons.account_balance_wallet,
color: Colors.orange, color: Colors.orange,
), ),
...@@ -127,7 +150,10 @@ class ParticipantDetailScreen extends ConsumerWidget { ...@@ -127,7 +150,10 @@ class ParticipantDetailScreen extends ConsumerWidget {
color: Colors.blue, color: Colors.blue,
), ),
], ],
).animate(delay: 200.ms).fadeIn(duration: 500.ms).slideY(begin: 0.05, end: 0), )
.animate(delay: 200.ms)
.fadeIn(duration: 500.ms)
.slideY(begin: 0.05, end: 0),
), ),
), ),
), ),
...@@ -165,7 +191,8 @@ class ParticipantDetailScreen extends ConsumerWidget { ...@@ -165,7 +191,8 @@ class ParticipantDetailScreen extends ConsumerWidget {
icon: Icons.assessment, icon: Icons.assessment,
label: l.t('evaluations'), label: l.t('evaluations'),
color: Colors.teal, color: Colors.teal,
onTap: () => context.push('/participants/$uuid/evaluations'), onTap: () =>
context.push('/participants/$uuid/evaluations'),
index: 3, index: 3,
), ),
_ActionTile( _ActionTile(
...@@ -214,7 +241,14 @@ class _StatusBadge extends StatelessWidget { ...@@ -214,7 +241,14 @@ class _StatusBadge extends StatelessWidget {
color: color.withValues(alpha: 0.1), color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
), ),
child: Text(label, style: TextStyle(fontSize: 12, color: color, fontWeight: FontWeight.w600)), child: Text(
label,
style: TextStyle(
fontSize: 12,
color: color,
fontWeight: FontWeight.w600,
),
),
).animate(delay: 150.ms).fadeIn(duration: 300.ms); ).animate(delay: 150.ms).fadeIn(duration: 300.ms);
} }
} }
...@@ -225,7 +259,12 @@ class _MiniStat extends StatelessWidget { ...@@ -225,7 +259,12 @@ class _MiniStat extends StatelessWidget {
final IconData icon; final IconData icon;
final Color color; final Color color;
const _MiniStat({required this.label, required this.value, required this.icon, required this.color}); const _MiniStat({
required this.label,
required this.value,
required this.icon,
required this.color,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -241,9 +280,20 @@ class _MiniStat extends StatelessWidget { ...@@ -241,9 +280,20 @@ class _MiniStat extends StatelessWidget {
children: [ children: [
Icon(icon, size: 18, color: color), Icon(icon, size: 18, color: color),
const SizedBox(height: 6), const SizedBox(height: 6),
Text(value, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: color)), Text(
value,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: color,
),
),
const SizedBox(height: 2), const SizedBox(height: 2),
Text(label, style: TextStyle(fontSize: 10, color: Colors.grey.shade500), textAlign: TextAlign.center), Text(
label,
style: TextStyle(fontSize: 10, color: Colors.grey.shade500),
textAlign: TextAlign.center,
),
], ],
), ),
), ),
...@@ -291,7 +341,10 @@ class _ActionTile extends StatelessWidget { ...@@ -291,7 +341,10 @@ class _ActionTile extends StatelessWidget {
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
label, label,
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500), style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
], ],
...@@ -300,6 +353,10 @@ class _ActionTile extends StatelessWidget { ...@@ -300,6 +353,10 @@ class _ActionTile extends StatelessWidget {
) )
.animate(delay: Duration(milliseconds: 250 + (index * 60))) .animate(delay: Duration(milliseconds: 250 + (index * 60)))
.fadeIn(duration: 400.ms) .fadeIn(duration: 400.ms)
.scale(begin: const Offset(0.85, 0.85), end: const Offset(1, 1), curve: Curves.easeOutBack); .scale(
begin: const Offset(0.85, 0.85),
end: const Offset(1, 1),
curve: Curves.easeOutBack,
);
} }
} }
...@@ -8,7 +8,9 @@ import '../../core/l10n/app_localizations.dart'; ...@@ -8,7 +8,9 @@ import '../../core/l10n/app_localizations.dart';
import '../../core/l10n/locale_provider.dart'; import '../../core/l10n/locale_provider.dart';
import '../../core/theme/app_theme.dart'; import '../../core/theme/app_theme.dart';
final profileProvider = FutureProvider.autoDispose<Map<String, dynamic>>((ref) async { final profileProvider = FutureProvider.autoDispose<Map<String, dynamic>>((
ref,
) async {
return ref.watch(apiServiceProvider).getProfile(); return ref.watch(apiServiceProvider).getProfile();
}); });
...@@ -36,7 +38,9 @@ class ProfileScreen extends ConsumerWidget { ...@@ -36,7 +38,9 @@ class ProfileScreen extends ConsumerWidget {
), ),
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.vertical(bottom: Radius.circular(24)), borderRadius: BorderRadius.vertical(
bottom: Radius.circular(24),
),
), ),
child: Column( child: Column(
children: [ children: [
...@@ -47,14 +51,24 @@ class ProfileScreen extends ConsumerWidget { ...@@ -47,14 +51,24 @@ class ProfileScreen extends ConsumerWidget {
color: AppTheme.primaryColor.withValues(alpha: 0.1), color: AppTheme.primaryColor.withValues(alpha: 0.1),
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: Icon(Icons.person, size: 40, color: AppTheme.primaryColor), child: Icon(
) Icons.person,
.animate() size: 40,
.scale(begin: const Offset(0.7, 0.7), end: const Offset(1, 1), duration: 500.ms, curve: Curves.easeOutBack), color: AppTheme.primaryColor,
),
).animate().scale(
begin: const Offset(0.7, 0.7),
end: const Offset(1, 1),
duration: 500.ms,
curve: Curves.easeOutBack,
),
const SizedBox(height: 14), const SizedBox(height: 14),
Text( Text(
auth.userName ?? '', auth.userName ?? '',
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
).animate(delay: 100.ms).fadeIn(duration: 400.ms), ).animate(delay: 100.ms).fadeIn(duration: 400.ms),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
...@@ -77,32 +91,47 @@ class ProfileScreen extends ConsumerWidget { ...@@ -77,32 +91,47 @@ class ProfileScreen extends ConsumerWidget {
title: l.t('language'), title: l.t('language'),
trailing: Switch( trailing: Switch(
value: locale.languageCode == 'en', value: locale.languageCode == 'en',
onChanged: (_) => ref.read(localeProvider.notifier).toggle(), onChanged: (_) =>
activeColor: AppTheme.primaryColor, ref.read(localeProvider.notifier).toggle(),
activeThumbColor: AppTheme.primaryColor,
), ),
subtitle: locale.languageCode == 'ar' ? 'العربية' : 'English', subtitle: locale.languageCode == 'ar'
).animate(delay: 200.ms).fadeIn(duration: 400.ms).slideX(begin: 0.03, end: 0), ? 'العربية'
: 'English',
)
.animate(delay: 200.ms)
.fadeIn(duration: 400.ms)
.slideX(begin: 0.03, end: 0),
const SizedBox(height: 10), const SizedBox(height: 10),
// Edit profile // Edit profile
_SettingsTile( _SettingsTile(
icon: Icons.edit, icon: Icons.edit,
title: l.t('edit_profile'), title: l.t('edit_profile'),
onTap: () {}, onTap: () {},
).animate(delay: 250.ms).fadeIn(duration: 400.ms).slideX(begin: 0.03, end: 0), )
.animate(delay: 250.ms)
.fadeIn(duration: 400.ms)
.slideX(begin: 0.03, end: 0),
const SizedBox(height: 10), const SizedBox(height: 10),
// Notification settings // Notification settings
_SettingsTile( _SettingsTile(
icon: Icons.notifications_outlined, icon: Icons.notifications_outlined,
title: l.t('notification_settings'), title: l.t('notification_settings'),
onTap: () {}, onTap: () {},
).animate(delay: 300.ms).fadeIn(duration: 400.ms).slideX(begin: 0.03, end: 0), )
.animate(delay: 300.ms)
.fadeIn(duration: 400.ms)
.slideX(begin: 0.03, end: 0),
const SizedBox(height: 10), const SizedBox(height: 10),
// About // About
_SettingsTile( _SettingsTile(
icon: Icons.info_outline, icon: Icons.info_outline,
title: l.t('about'), title: l.t('about'),
onTap: () {}, onTap: () {},
).animate(delay: 350.ms).fadeIn(duration: 400.ms).slideX(begin: 0.03, end: 0), )
.animate(delay: 350.ms)
.fadeIn(duration: 400.ms)
.slideX(begin: 0.03, end: 0),
const SizedBox(height: 30), const SizedBox(height: 30),
// Logout // Logout
SizedBox( SizedBox(
...@@ -116,14 +145,22 @@ class ProfileScreen extends ConsumerWidget { ...@@ -116,14 +145,22 @@ class ProfileScreen extends ConsumerWidget {
icon: const Icon(Icons.logout, color: Colors.red), icon: const Icon(Icons.logout, color: Colors.red),
label: Text( label: Text(
l.t('logout'), l.t('logout'),
style: const TextStyle(color: Colors.red, fontSize: 16), style: const TextStyle(
color: Colors.red,
fontSize: 16,
),
), ),
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.red), side: const BorderSide(color: Colors.red),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
), ),
), ),
).animate(delay: 400.ms).fadeIn(duration: 400.ms).slideY(begin: 0.05, end: 0), )
.animate(delay: 400.ms)
.fadeIn(duration: 400.ms)
.slideY(begin: 0.05, end: 0),
]), ]),
), ),
), ),
...@@ -175,15 +212,27 @@ class _SettingsTile extends StatelessWidget { ...@@ -175,15 +212,27 @@ class _SettingsTile extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(title, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500)), Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
if (subtitle != null) ...[ if (subtitle != null) ...[
const SizedBox(height: 2), const SizedBox(height: 2),
Text(subtitle!, style: TextStyle(fontSize: 12, color: Colors.grey.shade500)), Text(
subtitle!,
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade500,
),
),
], ],
], ],
), ),
), ),
if (trailing != null) trailing!, ?trailing,
if (trailing == null && onTap != null) if (trailing == null && onTap != null)
Icon(Icons.chevron_right, size: 20, color: Colors.grey.shade400), Icon(Icons.chevron_right, size: 20, color: Colors.grey.shade400),
], ],
......
...@@ -9,14 +9,6 @@ packages: ...@@ -9,14 +9,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "85.0.0" version: "85.0.0"
_flutterfire_internals:
dependency: transitive
description:
name: _flutterfire_internals
sha256: ff0a84a2734d9e1089f8aedd5c0af0061b82fb94e95260d943404e0ef2134b11
url: "https://pub.dev"
source: hosted
version: "1.3.59"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
...@@ -33,14 +25,6 @@ packages: ...@@ -33,14 +25,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.13.4" version: "0.13.4"
archive:
dependency: transitive
description:
name: archive
sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff
url: "https://pub.dev"
source: hosted
version: "4.0.9"
args: args:
dependency: transitive dependency: transitive
description: description:
...@@ -257,14 +241,6 @@ packages: ...@@ -257,14 +241,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.1" version: "3.1.1"
dbus:
dependency: transitive
description:
name: dbus
sha256: "0ce9b0a839e6dee59a37a623d2fc26a35bbbe6404213e419b0d6411023d62645"
url: "https://pub.dev"
source: hosted
version: "0.7.14"
dio: dio:
dependency: "direct main" dependency: "direct main"
description: description:
...@@ -337,54 +313,6 @@ packages: ...@@ -337,54 +313,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.9.3+5" version: "0.9.3+5"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
sha256: "7be63a3f841fc9663342f7f3a011a42aef6a61066943c90b1c434d79d5c995c5"
url: "https://pub.dev"
source: hosted
version: "3.15.2"
firebase_core_platform_interface:
dependency: transitive
description:
name: firebase_core_platform_interface
sha256: "0ecda14c1bfc9ed8cac303dd0f8d04a320811b479362a9a4efb14fd331a473ce"
url: "https://pub.dev"
source: hosted
version: "6.0.3"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
sha256: "0ed0dc292e8f9ac50992e2394e9d336a0275b6ae400d64163fdf0a8a8b556c37"
url: "https://pub.dev"
source: hosted
version: "2.24.1"
firebase_messaging:
dependency: "direct main"
description:
name: firebase_messaging
sha256: "60be38574f8b5658e2f22b7e311ff2064bea835c248424a383783464e8e02fcc"
url: "https://pub.dev"
source: hosted
version: "15.2.10"
firebase_messaging_platform_interface:
dependency: transitive
description:
name: firebase_messaging_platform_interface
sha256: "685e1771b3d1f9c8502771ccc9f91485b376ffe16d553533f335b9183ea99754"
url: "https://pub.dev"
source: hosted
version: "4.6.10"
firebase_messaging_web:
dependency: transitive
description:
name: firebase_messaging_web
sha256: "0d1be17bc89ed3ff5001789c92df678b2e963a51b6fa2bdb467532cc9dbed390"
url: "https://pub.dev"
source: hosted
version: "3.10.10"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
...@@ -422,30 +350,6 @@ packages: ...@@ -422,30 +350,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.0" version: "6.0.0"
flutter_local_notifications:
dependency: "direct main"
description:
name: flutter_local_notifications
sha256: ef41ae901e7529e52934feba19ed82827b11baa67336829564aeab3129460610
url: "https://pub.dev"
source: hosted
version: "18.0.1"
flutter_local_notifications_linux:
dependency: transitive
description:
name: flutter_local_notifications_linux
sha256: "8f685642876742c941b29c32030f6f4f6dacd0e4eaecb3efbb187d6a3812ca01"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
name: flutter_local_notifications_platform_interface
sha256: "6c5b83c86bf819cdb177a9247a3722067dd8cc6313827ce7c77a4b238a26fd52"
url: "https://pub.dev"
source: hosted
version: "8.0.0"
flutter_localizations: flutter_localizations:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
...@@ -797,14 +701,6 @@ packages: ...@@ -797,14 +701,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
lottie:
dependency: "direct main"
description:
name: lottie
sha256: "58dda9eee3f1b1fb9d490b0161635cffe93022b3b8ca0687e3df33a53620ead2"
url: "https://pub.dev"
source: hosted
version: "3.5.1"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
...@@ -965,14 +861,6 @@ packages: ...@@ -965,14 +861,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.2" version: "1.5.2"
posix:
dependency: transitive
description:
name: posix
sha256: bc1bad54ad2b735816e31f8d4600cfde6c7839975085ddfbca48b6c9f7c4044e
url: "https://pub.dev"
source: hosted
version: "6.5.2"
protobuf: protobuf:
dependency: transitive dependency: transitive
description: description:
...@@ -1290,14 +1178,6 @@ packages: ...@@ -1290,14 +1178,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.11" version: "0.7.11"
timezone:
dependency: transitive
description:
name: timezone
sha256: dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1
url: "https://pub.dev"
source: hosted
version: "0.10.1"
timing: timing:
dependency: transitive dependency: transitive
description: description:
......
...@@ -33,19 +33,19 @@ dependencies: ...@@ -33,19 +33,19 @@ dependencies:
shimmer: ^3.0.0 shimmer: ^3.0.0
cached_network_image: ^3.4.1 cached_network_image: ^3.4.1
flutter_svg: ^2.0.16 flutter_svg: ^2.0.16
lottie: ^3.3.1 # lottie: ^3.3.1 # disabled until animation assets are added
smooth_page_indicator: ^1.2.0+3 smooth_page_indicator: ^1.2.0+3
# Firebase # Push (placeholder — will add firebase when configured)
firebase_core: ^3.8.1 # firebase_core: ^3.8.1
firebase_messaging: ^15.1.6 # firebase_messaging: ^15.1.6
# Utilities # Utilities
intl: ^0.20.2 intl: ^0.20.2
url_launcher: ^6.3.1 url_launcher: ^6.3.1
image_picker: ^1.1.2 image_picker: ^1.1.2
pull_to_refresh_flutter3: ^2.0.2 pull_to_refresh_flutter3: ^2.0.2
flutter_local_notifications: ^18.0.1 # flutter_local_notifications: ^18.0.1 # disabled until push is configured
percent_indicator: ^4.2.3 percent_indicator: ^4.2.3
gap: ^3.0.1 gap: ^3.0.1
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment