diff --git a/siro_driver/lib/constant/finance_design_system.dart b/siro_driver/lib/constant/finance_design_system.dart index 57f56af4..8eea82b5 100644 --- a/siro_driver/lib/constant/finance_design_system.dart +++ b/siro_driver/lib/constant/finance_design_system.dart @@ -1,44 +1,59 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'colors.dart'; +/// كان هذا نظام ألوان **ثانياً منافساً** لـ [AppColor]: نيفي `#0A0E21` وأزرق +/// `#3D5AFE` بدل نيفي العلامة `#1A2340` والـ Periwinkle `#8C9CF8`. النتيجة أن +/// صفحات المحفظة والأرباح والتحديات كانت تبدو من تطبيق آخر. +/// +/// أُبقي الصنف باسمه حتى لا تتكسّر عشرات مواضع الاستدعاء، لكنه صار **واجهة +/// رقيقة فوق ألوان العلامة**. الجديد يُكتب بـ `SiroUi` / `Theme.of(context)`. class FinanceDesignSystem { // --- Colors --- - static Color get primaryDark => Get.isDarkMode ? const Color(0xFFE0E0E0) : const Color(0xFF0A0E21); - static Color get accentBlue => const Color(0xFF3D5AFE); - static Color get successGreen => const Color(0xFF00C853); - static Color get dangerRed => const Color(0xFFD50000); - static Color get backgroundColor => Get.isDarkMode ? const Color(0xFF0A0E21) : const Color(0xFFF6F8FA); - static Color get cardColor => Get.isDarkMode ? const Color(0xFF1E1E2E) : const Color(0xFFFFFFFF); - static Color get textSecondary => Get.isDarkMode ? Colors.white70 : const Color(0xFF757575); - static Color get textMuted => Get.isDarkMode ? Colors.white38 : const Color(0xFFBDBDBD); - static Color get borderColor => Get.isDarkMode ? Colors.white10 : Colors.grey.withOpacity(0.1); + static Color get primaryDark => + Get.isDarkMode ? const Color(0xFFF1F5F9) : AppColor.primaryColor; + static Color get accentBlue => + Get.isDarkMode ? AppColor.secondaryColorStatic : AppColor.primaryColor; + static Color get successGreen => AppColor.greenColor; + static Color get dangerRed => AppColor.redColor; + static Color get backgroundColor => + Get.isDarkMode ? const Color(0xFF0F1424) : const Color(0xFFF6F8FA); + static Color get cardColor => + Get.isDarkMode ? const Color(0xFF171D33) : const Color(0xFFFFFFFF); + static Color get textSecondary => + Get.isDarkMode ? const Color(0xFF94A3B8) : const Color(0xFF64748B); + static Color get textMuted => + Get.isDarkMode ? const Color(0xFF64748B) : const Color(0xFF94A3B8); + static Color get borderColor => + Get.isDarkMode ? const Color(0xFF262E4A) : const Color(0xFFE6EAF0); // --- Shadows --- static BoxShadow get softShadow => BoxShadow( - color: Colors.black.withOpacity(Get.isDarkMode ? 0.3 : 0.05), - blurRadius: 10, - offset: const Offset(0, 4), - ); + color: Colors.black.withValues(alpha: Get.isDarkMode ? 0.35 : 0.05), + blurRadius: 18, + offset: const Offset(0, 6), + ); // --- Gradients --- + /// تدرّج العلامة: نيفي ← Periwinkle. يُستعمل في بطاقات الرصيد والرؤوس. static LinearGradient get balanceGradient => LinearGradient( - colors: Get.isDarkMode - ? [const Color(0xFF1E1E2E), const Color(0xFF2A2A3E)] - : [const Color(0xFF0A0E21), const Color(0xFF1A237E)], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ); + colors: Get.isDarkMode + ? [const Color(0xFF1E2745), const Color(0xFF2E3A63)] + : [AppColor.primaryColor, const Color(0xFF3A4674)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ); static const LinearGradient dangerGradient = LinearGradient( - colors: [Color(0xFFD50000), Color(0xFFFF5252)], + colors: [AppColor.redColor, Color(0xFFF87171)], begin: Alignment.topLeft, end: Alignment.bottomRight, ); // --- Radius --- static const double mainRadius = 16.0; - static const double cardRadius = 20.0; - static const double buttonRadius = 12.0; + static const double cardRadius = 18.0; + static const double buttonRadius = 14.0; // --- Spacing --- static const double horizontalPadding = 16.0; @@ -46,19 +61,19 @@ class FinanceDesignSystem { // --- Text Styles --- static TextStyle get balanceStyle => const TextStyle( - fontSize: 32, - fontWeight: FontWeight.bold, - color: Colors.white, - ); + fontSize: 32, + fontWeight: FontWeight.bold, + color: Colors.white, + ); static TextStyle get headingStyle => TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: primaryDark, - ); + fontSize: 17, + fontWeight: FontWeight.bold, + color: primaryDark, + ); static TextStyle get subHeadingStyle => TextStyle( - fontSize: 14, - color: textSecondary, - ); + fontSize: 13, + color: textSecondary, + ); } diff --git a/siro_driver/lib/controller/local/ar_eg.dart b/siro_driver/lib/controller/local/ar_eg.dart index 24cb8435..14690923 100644 --- a/siro_driver/lib/controller/local/ar_eg.dart +++ b/siro_driver/lib/controller/local/ar_eg.dart @@ -2693,4 +2693,13 @@ final Map ar_eg = { "Destination cleared!": "تم مسح الوجهة الشخصية!", "Move map to select destination": "حرك الخريطة لتحديد الوجهة الشخصية", "service_unavailable_area": "هذا التطبيق لا يعمل في منطقتك حالياً", + // ── أقسام القائمة الجانبية (UI) ── + "Services": "الخدمات", + "Wallet & Earnings": "المحفظة والأرباح", + "My Trips": "رحلاتي", + "Rewards": "المكافآت", + "My Account": "حسابي", + "My Earnings": "أرباحي", + "Upcoming Scheduled Rides": "الرحلات المجدولة القادمة", + "Retry": "إعادة المحاولة", }; diff --git a/siro_driver/lib/controller/local/ar_jo.dart b/siro_driver/lib/controller/local/ar_jo.dart index 86b35aef..b00ffc34 100644 --- a/siro_driver/lib/controller/local/ar_jo.dart +++ b/siro_driver/lib/controller/local/ar_jo.dart @@ -2708,4 +2708,13 @@ final Map ar_jo = { "فشل تسجيل الحضور": "فشل تسجيل الحضور", "لا يوجد ركاب مسجلين في هذه المحطة": "لا يوجد ركاب مسجلين في هذه المحطة", "حاضر": "حاضر", + // ── أقسام القائمة الجانبية (UI) ── + "Services": "الخدمات", + "Wallet & Earnings": "المحفظة والأرباح", + "My Trips": "رحلاتي", + "Rewards": "المكافآت", + "My Account": "حسابي", + "My Earnings": "أرباحي", + "Upcoming Scheduled Rides": "الرحلات المجدولة القادمة", + "Retry": "إعادة المحاولة", }; diff --git a/siro_driver/lib/controller/local/ar_sy.dart b/siro_driver/lib/controller/local/ar_sy.dart index 684e4382..5e28884a 100644 --- a/siro_driver/lib/controller/local/ar_sy.dart +++ b/siro_driver/lib/controller/local/ar_sy.dart @@ -2695,4 +2695,13 @@ final Map ar_sy = { "💰 Pay with Wallet": "💰 ادفع من المحفظة", "💳 Pay with Credit Card": "💳 ادفع ببطاقة ائتمان", "service_unavailable_area": "هذا التطبيق لا يعمل في منطقتك حالياً", + // ── أقسام القائمة الجانبية (UI) ── + "Services": "الخدمات", + "Wallet & Earnings": "المحفظة والأرباح", + "My Trips": "رحلاتي", + "Rewards": "المكافآت", + "My Account": "حسابي", + "My Earnings": "أرباحي", + "Upcoming Scheduled Rides": "الرحلات المجدولة القادمة", + "Retry": "إعادة المحاولة", }; diff --git a/siro_driver/lib/controller/local/en.dart b/siro_driver/lib/controller/local/en.dart index ef3f47c1..803b4617 100644 --- a/siro_driver/lib/controller/local/en.dart +++ b/siro_driver/lib/controller/local/en.dart @@ -2693,4 +2693,13 @@ final Map en = { "Destination cleared!": "Destination cleared!", "Move map to select destination": "Move map to select destination", "service_unavailable_area": "This service is not currently available in your area", + // ── أقسام القائمة الجانبية (UI) ── + "Services": "Services", + "Wallet & Earnings": "Wallet & Earnings", + "My Trips": "My Trips", + "Rewards": "Rewards", + "My Account": "My Account", + "My Earnings": "My Earnings", + "Upcoming Scheduled Rides": "Upcoming Scheduled Rides", + "Retry": "Retry", }; diff --git a/siro_driver/lib/controller/themes/themes.dart b/siro_driver/lib/controller/themes/themes.dart index f8763c73..bab2bd8d 100755 --- a/siro_driver/lib/controller/themes/themes.dart +++ b/siro_driver/lib/controller/themes/themes.dart @@ -2,13 +2,25 @@ import 'package:flutter/material.dart'; import 'package:siro_driver/constant/style.dart'; import '../../constant/colors.dart'; +/// ملاحظة مهمة: لا تستعمل هنا getters من `AppColor` التي تقرأ `Get.isDarkMode` +/// (مثل `writeColor` و`cardColor`). هذا الملف يُنفَّذ **مرة واحدة عند التحميل**، +/// فتُخبَّأ قيمة الوضع الحالي داخل الثيمين معاً ⇒ الثيم الفاتح يرث ألوان الداكن +/// (أو العكس) حسب ما كان الوضع وقت الإقلاع. الألوان هنا تُمرَّر صراحةً لكل ثيم. ThemeData _createTheme({ required Brightness brightness, required String fontFamily, required Color scaffoldBackgroundColor, required Color cardColor, required Color dividerColor, + required Color textColor, + required Color secondaryTextColor, }) { + final isDark = brightness == Brightness.dark; + // في الوضع الداكن يصبح النيفي الداكن غير مقروء على خلفية داكنة، + // فنستعمل لون العلامة الفاتح (Periwinkle) للعناصر التفاعلية. + final interactive = + isDark ? AppColor.secondaryColorStatic : AppColor.primaryColor; + return ThemeData( brightness: brightness, fontFamily: fontFamily, @@ -16,75 +28,189 @@ ThemeData _createTheme({ cardColor: cardColor, dividerColor: dividerColor, primaryColor: AppColor.primaryColor, + splashFactory: InkSparkle.splashFactory, colorScheme: ColorScheme.fromSeed( seedColor: AppColor.primaryColor, brightness: brightness, - primary: AppColor.primaryColor, + primary: interactive, + onPrimary: isDark ? AppColor.primaryColor : Colors.white, + secondary: AppColor.secondaryColorStatic, surface: cardColor, - background: scaffoldBackgroundColor, + onSurface: textColor, error: AppColor.redColor, ), textTheme: TextTheme( - displaySmall: AppStyle.title.copyWith(color: AppColor.writeColor), - displayLarge: AppStyle.headTitle.copyWith(color: AppColor.writeColor), - displayMedium: AppStyle.headTitle2.copyWith(color: AppColor.writeColor), - bodyLarge: AppStyle.title.copyWith(color: AppColor.writeColor), - bodyMedium: AppStyle.subtitle.copyWith(color: AppColor.writeColor), - titleLarge: AppStyle.headTitle2.copyWith(color: AppColor.writeColor), + displayLarge: AppStyle.headTitle.copyWith(color: textColor), + displayMedium: AppStyle.headTitle2.copyWith(color: textColor), + displaySmall: AppStyle.title.copyWith(color: textColor), + titleLarge: AppStyle.headTitle2.copyWith(color: textColor, fontSize: 20), + titleMedium: AppStyle.title + .copyWith(color: textColor, fontWeight: FontWeight.w600), + bodyLarge: AppStyle.title.copyWith(color: textColor), + bodyMedium: AppStyle.title.copyWith(color: textColor, fontSize: 14), + bodySmall: AppStyle.title.copyWith(color: secondaryTextColor, fontSize: 13), + labelLarge: AppStyle.title + .copyWith(color: textColor, fontWeight: FontWeight.w600, fontSize: 15), + labelMedium: AppStyle.subtitle.copyWith(color: secondaryTextColor), ), appBarTheme: AppBarTheme( elevation: 0, + scrolledUnderElevation: 0, backgroundColor: scaffoldBackgroundColor, + foregroundColor: textColor, centerTitle: true, - iconTheme: const IconThemeData(color: AppColor.primaryColor), - titleTextStyle: AppStyle.headTitle2.copyWith(color: AppColor.writeColor), + iconTheme: IconThemeData(color: textColor), + actionsIconTheme: IconThemeData(color: textColor), + titleTextStyle: AppStyle.headTitle2.copyWith( + color: textColor, + fontSize: 19, + ), ), dialogTheme: DialogThemeData( backgroundColor: cardColor, - contentTextStyle: AppStyle.title.copyWith(color: AppColor.writeColor), - titleTextStyle: AppStyle.headTitle2.copyWith(color: AppColor.writeColor), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + contentTextStyle: AppStyle.title.copyWith(color: textColor), + titleTextStyle: AppStyle.headTitle2.copyWith(color: textColor, fontSize: 20), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), ), cardTheme: CardThemeData( color: cardColor, - elevation: 2, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + elevation: 0, + margin: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18), + side: BorderSide(color: dividerColor), + ), + ), + listTileTheme: ListTileThemeData( + iconColor: secondaryTextColor, + textColor: textColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), ), dividerTheme: DividerThemeData( color: dividerColor, thickness: 1, + space: 1, + ), + chipTheme: ChipThemeData( + backgroundColor: interactive.withValues(alpha: 0.10), + side: BorderSide.none, + labelStyle: AppStyle.subtitle.copyWith(color: textColor), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(999)), + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: interactive, + foregroundColor: isDark ? AppColor.primaryColor : Colors.white, + elevation: 0, + minimumSize: const Size(0, 50), + padding: const EdgeInsets.symmetric(horizontal: 20), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), + textStyle: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15), + ), + ), + outlinedButtonTheme: OutlinedButtonThemeData( + style: OutlinedButton.styleFrom( + foregroundColor: interactive, + minimumSize: const Size(0, 50), + side: BorderSide(color: interactive.withValues(alpha: 0.5)), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), + textStyle: const TextStyle(fontWeight: FontWeight.w600, fontSize: 15), + ), + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: interactive, + textStyle: const TextStyle(fontWeight: FontWeight.w600), + ), + ), + snackBarTheme: SnackBarThemeData( + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), + ), + progressIndicatorTheme: ProgressIndicatorThemeData(color: interactive), + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: isDark ? Colors.white.withValues(alpha: 0.04) : cardColor, + hintStyle: AppStyle.title.copyWith(color: secondaryTextColor, fontSize: 14), + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: BorderSide(color: dividerColor), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: BorderSide(color: dividerColor), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: BorderSide(color: interactive, width: 1.5), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: const BorderSide(color: AppColor.redColor), + ), + ), + bottomSheetTheme: BottomSheetThemeData( + backgroundColor: cardColor, + surfaceTintColor: Colors.transparent, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(24)), + ), ), ); } +// --- ألوان الأسطح: فاتح --- +const Color _lightBg = Color(0xFFF6F8FA); +const Color _lightCard = Color(0xFFFFFFFF); +const Color _lightDivider = Color(0xFFE6EAF0); +const Color _lightText = Color(0xFF0F172A); +const Color _lightTextSecondary = Color(0xFF64748B); + +// --- ألوان الأسطح: داكن --- +const Color _darkBg = Color(0xFF0F1424); +const Color _darkCard = Color(0xFF171D33); +const Color _darkDivider = Color(0xFF262E4A); +const Color _darkText = Color(0xFFF1F5F9); +const Color _darkTextSecondary = Color(0xFF94A3B8); + ThemeData lightThemeEnglish = _createTheme( brightness: Brightness.light, fontFamily: 'CupertinoSystemText', // Native fallback - scaffoldBackgroundColor: Colors.white, - cardColor: Colors.white, - dividerColor: Colors.black12, + scaffoldBackgroundColor: _lightBg, + cardColor: _lightCard, + dividerColor: _lightDivider, + textColor: _lightText, + secondaryTextColor: _lightTextSecondary, ); ThemeData darkThemeEnglish = _createTheme( brightness: Brightness.dark, fontFamily: 'CupertinoSystemText', - scaffoldBackgroundColor: const Color(0xFF121212), - cardColor: const Color(0xFF1E1E1E), - dividerColor: Colors.white10, + scaffoldBackgroundColor: _darkBg, + cardColor: _darkCard, + dividerColor: _darkDivider, + textColor: _darkText, + secondaryTextColor: _darkTextSecondary, ); ThemeData lightThemeArabic = _createTheme( brightness: Brightness.light, fontFamily: 'CupertinoSystemText', - scaffoldBackgroundColor: Colors.white, - cardColor: Colors.white, - dividerColor: Colors.black12, + scaffoldBackgroundColor: _lightBg, + cardColor: _lightCard, + dividerColor: _lightDivider, + textColor: _lightText, + secondaryTextColor: _lightTextSecondary, ); ThemeData darkThemeArabic = _createTheme( brightness: Brightness.dark, fontFamily: 'CupertinoSystemText', - scaffoldBackgroundColor: const Color(0xFF121212), - cardColor: const Color(0xFF1E1E1E), - dividerColor: Colors.white10, + scaffoldBackgroundColor: _darkBg, + cardColor: _darkCard, + dividerColor: _darkDivider, + textColor: _darkText, + secondaryTextColor: _darkTextSecondary, ); diff --git a/siro_driver/lib/views/Rate/rate_app_page.dart b/siro_driver/lib/views/Rate/rate_app_page.dart index 23ef7bd9..4c143136 100755 --- a/siro_driver/lib/views/Rate/rate_app_page.dart +++ b/siro_driver/lib/views/Rate/rate_app_page.dart @@ -1,119 +1,90 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import '../../constant/colors.dart'; import '../../controller/rate/rate_app_controller.dart'; +import '../widgets/ui/siro_ui.dart'; class RatingScreen extends StatelessWidget { + RatingScreen({super.key}); + final RatingController ratingController = Get.put(RatingController()); @override Widget build(BuildContext context) { - return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - middle: Text( - "Rate Our App".tr, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - ), - child: SafeArea( - child: SingleChildScrollView( - child: Center( - child: Obx(() { - return Padding( - padding: - const EdgeInsets.symmetric(horizontal: 20, vertical: 30), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - // App logo or visual - ClipRRect( - borderRadius: BorderRadius.circular(16), - child: Image.asset( - 'assets/images/logo.gif', - height: 120, - ), - ), - const SizedBox(height: 20), - - // Rating Prompt - Text( - "How would you rate our app?".tr, - textAlign: TextAlign.center, - style: CupertinoTheme.of(context) - .textTheme - .navTitleTextStyle - .copyWith(fontSize: 18, fontWeight: FontWeight.w600), - ), - const SizedBox(height: 20), - - // Comment Box - CupertinoTextField( - controller: ratingController.comment, - placeholder: 'Write your comment here'.tr, - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - border: Border.all(color: CupertinoColors.systemGrey4), - borderRadius: BorderRadius.circular(12), - color: CupertinoColors.white, - ), - prefix: Padding( - padding: const EdgeInsets.only(left: 8.0), - child: Icon(CupertinoIcons.pencil_ellipsis_rectangle, - color: CupertinoColors.systemGrey), - ), - ), - const SizedBox(height: 20), - - // Star Rating Section - _buildRatingStars(), - - const SizedBox(height: 30), - - // Submit Button - CupertinoButton( - padding: const EdgeInsets.symmetric( - horizontal: 24, vertical: 14), - borderRadius: BorderRadius.circular(12), - color: CupertinoColors.activeGreen, - onPressed: () { - if (ratingController.userRating.value > 0) { - ratingController - .submitRating(ratingController.userRating.value); - Get.snackbar( - "Thank You!".tr, - "Your rating has been submitted.".tr, - backgroundColor: CupertinoColors.systemGrey6, - snackPosition: SnackPosition.BOTTOM, - margin: const EdgeInsets.all(16), - borderRadius: 12, - ); - } else { - Get.snackbar( - "Error".tr, - "Please select a rating before submitting.".tr, - backgroundColor: CupertinoColors.systemRed, - snackPosition: SnackPosition.BOTTOM, - margin: const EdgeInsets.all(16), - borderRadius: 12, - ); - } - }, - child: Text( - "Submit Rating".tr, - style: const TextStyle( - color: CupertinoColors.white, - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - ), - ], + return SiroPage( + title: 'Rate Our App'.tr, + padding: SiroUi.pageInsets, + body: Obx(() { + return ListView( + children: [ + const SizedBox(height: 8), + Center( + child: ClipRRect( + borderRadius: BorderRadius.circular(16), + child: Image.asset('assets/images/logo.gif', height: 120), + ), + ), + const SizedBox(height: 20), + Text( + 'How would you rate our app?'.tr, + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith(fontWeight: FontWeight.bold), + ), + const SizedBox(height: 18), + _buildRatingStars(), + const SizedBox(height: 22), + SiroCard( + padding: const EdgeInsets.all(6), + child: TextField( + controller: ratingController.comment, + maxLines: 4, + minLines: 3, + decoration: InputDecoration( + hintText: 'Write your comment here'.tr, + filled: false, + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, ), - ); - }), - ), - ), - ), + ), + ), + const SizedBox(height: 22), + ElevatedButton.icon( + onPressed: () { + if (ratingController.userRating.value > 0) { + ratingController + .submitRating(ratingController.userRating.value); + Get.snackbar( + 'Thank You!'.tr, + 'Your rating has been submitted.'.tr, + backgroundColor: AppColor.greenColor, + colorText: Colors.white, + snackPosition: SnackPosition.BOTTOM, + margin: const EdgeInsets.all(16), + borderRadius: 14, + ); + } else { + Get.snackbar( + 'Error'.tr, + 'Please select a rating before submitting.'.tr, + backgroundColor: AppColor.redColor, + colorText: Colors.white, + snackPosition: SnackPosition.BOTTOM, + margin: const EdgeInsets.all(16), + borderRadius: 14, + ); + } + }, + icon: const Icon(Icons.send_rounded, size: 18), + label: Text('Submit Rating'.tr), + ), + const SizedBox(height: 20), + ], + ); + }), ); } @@ -122,19 +93,19 @@ class RatingScreen extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(5, (index) { + final selected = index < ratingController.userRating.value; return GestureDetector( - onTap: () { - ratingController.userRating.value = index + 1; - }, - child: AnimatedContainer( - duration: const Duration(milliseconds: 300), - margin: const EdgeInsets.symmetric(horizontal: 4), - child: Icon( - CupertinoIcons.star_fill, - size: 40, - color: index < ratingController.userRating.value - ? CupertinoColors.systemYellow - : CupertinoColors.systemGrey3, + onTap: () => ratingController.userRating.value = index + 1, + child: AnimatedScale( + scale: selected ? 1.0 : 0.88, + duration: const Duration(milliseconds: 200), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 5), + child: Icon( + selected ? Icons.star_rounded : Icons.star_outline_rounded, + size: 42, + color: selected ? AppColor.gold : Colors.grey, + ), ), ), ); diff --git a/siro_driver/lib/views/Rate/rate_passenger.dart b/siro_driver/lib/views/Rate/rate_passenger.dart index 15f8a931..8e214b8b 100755 --- a/siro_driver/lib/views/Rate/rate_passenger.dart +++ b/siro_driver/lib/views/Rate/rate_passenger.dart @@ -10,6 +10,7 @@ import 'package:siro_driver/views/widgets/elevated_btn.dart'; import '../../constant/style.dart'; import '../../controller/rate/rate_conroller.dart'; +import '../widgets/ui/siro_ui.dart'; class RatePassenger extends StatelessWidget { final RateController controller = Get.put(RateController()); @@ -21,19 +22,14 @@ class RatePassenger extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, - appBar: AppBar( - title: Text('Trip Completed'.tr), - centerTitle: true, - automaticallyImplyLeading: false, - elevation: 0, - ), + return SiroPage( + title: 'Trip Completed'.tr, + showBack: false, body: GetBuilder( builder: (controller) { return SingleChildScrollView( child: Padding( - padding: const EdgeInsets.all(20.0), + padding: SiroUi.pageInsets, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -159,20 +155,13 @@ class RatePassenger extends StatelessWidget { } Widget _buildWalletSection(BuildContext context, RateController controller) { - return Container( - decoration: BoxDecoration( - color: Theme.of(context).cardColor, - borderRadius: BorderRadius.circular(16), - border: Border.all(color: Theme.of(context).dividerColor), - ), - child: Padding( - padding: const EdgeInsets.all(20.0), - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: controller.ispassengerWantWalletFromDriver - ? _buildAmountInput(context, controller) - : _buildWalletQuery(context, controller), - ), + return SiroCard( + padding: const EdgeInsets.all(20.0), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: controller.ispassengerWantWalletFromDriver + ? _buildAmountInput(context, controller) + : _buildWalletQuery(context, controller), ), ); } diff --git a/siro_driver/lib/views/auth/captin/contact_us_page.dart b/siro_driver/lib/views/auth/captin/contact_us_page.dart index 4992df09..9d7968ed 100755 --- a/siro_driver/lib/views/auth/captin/contact_us_page.dart +++ b/siro_driver/lib/views/auth/captin/contact_us_page.dart @@ -1,100 +1,92 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_font_icons/flutter_font_icons.dart'; import 'package:get/get.dart'; import '../../../constant/colors.dart'; -import '../../../constant/style.dart'; import '../../../controller/functions/tts.dart'; import '../../../controller/home/captin/contact_us_controller.dart'; -import '../../widgets/my_scafold.dart'; +import '../../widgets/ui/siro_ui.dart'; class ContactUsPage extends StatelessWidget { - ContactUsPage({super.key}); + const ContactUsPage({super.key}); + + static const String _aboutText = + 'Siro is the safest ride-sharing app that introduces many features for both captains and passengers. We offer the lowest commission rate of just 8%, ensuring you get the best value for your rides. Our app includes insurance for the best captains, regular maintenance of cars with top engineers, and on-road services to ensure a respectful and high-quality experience for all users.'; @override Widget build(BuildContext context) { Get.put(ContactUsController()); return GetBuilder(builder: (controller) { - return MyScafolld( - title: "Contact Us".tr, - body: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: ListView( - // crossAxisAlignment: CrossAxisAlignment.center, - // mainAxisAlignment: MainAxisAlignment.spaceBetween, + return SiroPage( + title: 'Contact Us'.tr, + padding: SiroUi.pageInsets, + body: ListView( + children: [ + SiroCard( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 8), + child: Column( children: [ - Container( - decoration: AppStyle.boxDecoration1, - child: Column( - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(15), - child: Image.asset('assets/images/logo.gif')), - IconButton( - onPressed: () async { - Get.put(TextToSpeechController()).speakText( - 'Siro is the safest ride-sharing app that introduces many features for both captains and passengers. We offer the lowest commission rate of just 8%, ensuring you get the best value for your rides. Our app includes insurance for the best captains, regular maintenance of cars with top engineers, and on-road services to ensure a respectful and high-quality experience for all users.' - .tr); - }, - icon: const Icon(Icons.headphones), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'Siro is the safest ride-sharing app that introduces many features for both captains and passengers. We offer the lowest commission rate of just 8%, ensuring you get the best value for your rides. Our app includes insurance for the best captains, regular maintenance of cars with top engineers, and on-road services to ensure a respectful and high-quality experience for all users.' - .tr, - style: AppStyle.title, - textAlign: TextAlign.center, - ), - ), - ], + ClipRRect( + borderRadius: BorderRadius.circular(14), + child: Image.asset( + 'assets/images/logo.gif', + height: 150, + fit: BoxFit.contain, ), ), - const SizedBox( - height: 30, + const SizedBox(height: 12), + Text( + _aboutText.tr, + style: Theme.of(context).textTheme.bodyMedium, + textAlign: TextAlign.center, ), - Container( - decoration: AppStyle.boxDecoration1, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - "You can contact us during working hours from 10:00 - 17:00." - .tr, - style: AppStyle.title, - textAlign: TextAlign.center, - ), - ), + const SizedBox(height: 4), + // زرّ القراءة الصوتية كان أيقونة عارية بلا تسمية، + // فلا يُفهم غرضه قبل الضغط. + TextButton.icon( + onPressed: () => Get.put(TextToSpeechController()) + .speakText(_aboutText.tr), + icon: const Icon(Icons.headphones_rounded, size: 18), + label: Text('Listen'.tr), ), - InkWell( - onTap: () => controller.showContactDialog(context), - child: const Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Icon( - Icons.phone, - color: AppColor.blueColor, - ), - Icon( - FontAwesome.whatsapp, - color: AppColor.greenColor, - ), - Icon( - Icons.email, - color: AppColor.redColor, - ), - ], - ), - ), - const SizedBox( - height: 30, - ) ], ), - ) + ), + SiroCard( + child: Row( + children: [ + SiroIconBadge(Icons.schedule_rounded, + color: SiroUi.interactive(context)), + const SizedBox(width: 12), + Expanded( + child: Text( + 'You can contact us during working hours from 10:00 - 17:00.' + .tr, + style: Theme.of(context).textTheme.bodyMedium, + ), + ), + ], + ), + ), + SiroSectionTitle('Contact Us'.tr), + SiroCard( + padding: const EdgeInsets.symmetric(vertical: 18), + onTap: () => controller.showContactDialog(context), + child: const Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + SiroIconBadge(Icons.phone_rounded, + color: AppColor.primaryColor, size: 24), + SiroIconBadge(FontAwesome.whatsapp, + color: AppColor.greenColor, size: 24), + SiroIconBadge(Icons.email_rounded, + color: AppColor.redColor, size: 24), + ], + ), + ), ], - isleading: true); + ), + ); }); } } diff --git a/siro_driver/lib/views/gamification/challenges_page.dart b/siro_driver/lib/views/gamification/challenges_page.dart index 694b01b0..6841f159 100644 --- a/siro_driver/lib/views/gamification/challenges_page.dart +++ b/siro_driver/lib/views/gamification/challenges_page.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import '../../../constant/finance_design_system.dart'; +import '../../constant/colors.dart'; import '../../../controller/gamification/challenges_controller.dart'; +import '../widgets/ui/siro_ui.dart'; class ChallengesPage extends StatelessWidget { ChallengesPage({super.key}); @@ -10,24 +11,18 @@ class ChallengesPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: FinanceDesignSystem.backgroundColor, body: GetBuilder(builder: (cc) { if (cc.isLoading) { - return Center( - child: CircularProgressIndicator( - color: FinanceDesignSystem.primaryDark)); + return const SiroLoading(); } return CustomScrollView( physics: const BouncingScrollPhysics(), slivers: [ SliverAppBar( - expandedHeight: 160, + expandedHeight: 150, pinned: true, - backgroundColor: Get.isDarkMode ? FinanceDesignSystem.primaryDark : const Color(0xFF1A237E), - leading: IconButton( - icon: const Icon(Icons.arrow_back_ios_new_rounded, - color: Colors.white, size: 20), - onPressed: () => Get.back()), + foregroundColor: Colors.white, + backgroundColor: AppColor.primaryColor, actions: [ IconButton( icon: const Icon(Icons.refresh_rounded, @@ -36,136 +31,98 @@ class ChallengesPage extends StatelessWidget { ], flexibleSpace: FlexibleSpaceBar( centerTitle: true, + titlePadding: const EdgeInsets.symmetric( + horizontal: 48, vertical: 14), title: Text('Challenges'.tr, - style: TextStyle( + overflow: TextOverflow.ellipsis, + style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, - fontSize: 18)), + fontSize: 17)), background: Stack(fit: StackFit.expand, children: [ Container( decoration: BoxDecoration( - gradient: LinearGradient(colors: Get.isDarkMode - ? [FinanceDesignSystem.primaryDark, const Color(0xFF1E1E2E)] - : [const Color(0xFF0A0E21), const Color(0xFF1A237E)] - ))), - Positioned( - right: -30, + gradient: SiroUi.brandGradient(context))), + PositionedDirectional( + end: -30, top: -10, child: Icon(Icons.bolt_rounded, - size: 160, color: Colors.white.withValues(alpha: 0.04))), + size: 160, + color: Colors.white.withValues(alpha: 0.05))), ]), ), ), SliverPadding( - padding: const EdgeInsets.fromLTRB(16, 24, 16, 40), + padding: const EdgeInsets.fromLTRB(16, 8, 16, 32), sliver: SliverList( delegate: SliverChildListDelegate([ // Daily Challenges _sectionHeader( + context, 'Daily Challenges'.tr, - '🔥', cc.dailyChallenges.where((c) => c.isCompleted).length, cc.dailyChallenges.length), - const SizedBox(height: 12), - ...cc.dailyChallenges.map((c) => _challengeCard(c, cc)), - const SizedBox(height: 28), + ...cc.dailyChallenges + .map((c) => _challengeCard(context, c, cc)), + const SizedBox(height: 16), // Weekly Challenges _sectionHeader( + context, 'Weekly Challenges'.tr, - '🏆', cc.weeklyChallenges.where((c) => c.isCompleted).length, cc.weeklyChallenges.length), - const SizedBox(height: 12), - ...cc.weeklyChallenges.map((c) => _challengeCard(c, cc)), + ...cc.weeklyChallenges + .map((c) => _challengeCard(context, c, cc)), ]))), ]); }), ); } - Widget _sectionHeader(String title, String emoji, int done, int total) { - return Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row(children: [ - Text(emoji, style: const TextStyle(fontSize: 20)), - const SizedBox(width: 8), - Text(title, style: FinanceDesignSystem.headingStyle), - ]), - Container( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - decoration: BoxDecoration( - color: FinanceDesignSystem.accentBlue.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(20)), - child: Text('$done/$total', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: FinanceDesignSystem.accentBlue)), - ), - ]); + Widget _sectionHeader( + BuildContext context, String title, int done, int total) { + return SiroSectionTitle( + title, + trailing: SiroTag('$done/$total', color: SiroUi.interactive(context)), + ); } - Widget _challengeCard(Challenge c, ChallengesController cc) { + Widget _challengeCard( + BuildContext context, Challenge c, ChallengesController cc) { final isAr = Get.locale?.languageCode == 'ar'; - return Container( - margin: const EdgeInsets.only(bottom: 12), - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(FinanceDesignSystem.cardRadius), - border: c.isCompleted - ? Border.all(color: c.color.withValues(alpha: 0.3), width: 1.5) - : null, - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.03), - blurRadius: 10, - offset: const Offset(0, 4)) - ], - ), + return SiroCard( + accent: c.isCompleted ? c.color : null, child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Row(children: [ - Container( - width: 44, - height: 44, - decoration: BoxDecoration( - color: c.color.withValues(alpha: 0.12), - borderRadius: BorderRadius.circular(12)), - child: Icon(c.icon, color: c.color, size: 22), - ), + SiroIconBadge(c.icon, color: c.color, size: 22), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(isAr ? c.titleAr : c.titleEn, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: FinanceDesignSystem.primaryDark)), + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.bold)), Text(isAr ? c.descriptionAr : c.descriptionEn, - style: - TextStyle(fontSize: 11, color: FinanceDesignSystem.textSecondary)), + style: Theme.of(context) + .textTheme + .bodySmall + ?.copyWith(fontSize: 11)), ])), - Container( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), - decoration: BoxDecoration( - color: const Color(0xFFFFD700).withValues(alpha: 0.15), - borderRadius: BorderRadius.circular(12)), - child: Text('+${c.reward}', - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: Color(0xFFFF8F00))), - ), + const SizedBox(width: 8), + SiroTag('+${c.reward}', + color: AppColor.yellowColor, icon: Icons.star_rounded), ]), const SizedBox(height: 14), // Progress bar Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('${c.currentProgress}/${c.target}', - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w600, - color: FinanceDesignSystem.textSecondary)), + style: Theme.of(context) + .textTheme + .bodySmall + ?.copyWith(fontSize: 11, fontWeight: FontWeight.w600)), Text('${(c.progress * 100).toStringAsFixed(0)}%', style: TextStyle( fontSize: 11, fontWeight: FontWeight.bold, color: c.color)), @@ -175,7 +132,7 @@ class ChallengesPage extends StatelessWidget { Container( height: 8, decoration: BoxDecoration( - color: FinanceDesignSystem.backgroundColor, + color: SiroUi.border(context), borderRadius: BorderRadius.circular(4))), LayoutBuilder( builder: (ctx, cons) => AnimatedContainer( @@ -207,14 +164,11 @@ class ChallengesPage extends StatelessWidget { ), ) : const Icon(Icons.card_giftcard_rounded, size: 18), - label: Text(cc.isClaiming(c.id) ? 'Claiming...'.tr : 'Claim Reward'.tr), + label: Text( + cc.isClaiming(c.id) ? 'Claiming...'.tr : 'Claim Reward'.tr), style: ElevatedButton.styleFrom( backgroundColor: c.color, foregroundColor: Colors.white, - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12)), - padding: const EdgeInsets.symmetric(vertical: 12), ), )), ], @@ -222,14 +176,14 @@ class ChallengesPage extends StatelessWidget { const SizedBox(height: 8), Center( child: Row(mainAxisSize: MainAxisSize.min, children: [ - Icon(Icons.check_circle_rounded, - color: FinanceDesignSystem.successGreen, size: 16), + const Icon(Icons.check_circle_rounded, + color: AppColor.greenColor, size: 16), const SizedBox(width: 4), Text('Claimed'.tr, - style: TextStyle( + style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, - color: FinanceDesignSystem.successGreen)), + color: AppColor.greenColor)), ])), ], ]), diff --git a/siro_driver/lib/views/gamification/leaderboard_page.dart b/siro_driver/lib/views/gamification/leaderboard_page.dart index 6db0f2a5..60dcb8a7 100644 --- a/siro_driver/lib/views/gamification/leaderboard_page.dart +++ b/siro_driver/lib/views/gamification/leaderboard_page.dart @@ -1,8 +1,9 @@ import 'package:siro_driver/constant/currency.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import '../../../constant/finance_design_system.dart'; +import '../../constant/colors.dart'; import '../../../controller/gamification/leaderboard_controller.dart'; +import '../widgets/ui/siro_ui.dart'; class LeaderboardPage extends StatelessWidget { LeaderboardPage({super.key}); @@ -11,12 +12,16 @@ class LeaderboardPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: FinanceDesignSystem.backgroundColor, body: GetBuilder(builder: (lc) { if (lc.isLoading) { - return Center( - child: CircularProgressIndicator( - color: FinanceDesignSystem.primaryDark)); + return const SiroLoading(); + } + if (lc.currentLeaderboard.isEmpty) { + return SiroEmptyState( + icon: Icons.leaderboard_rounded, + title: 'Leaderboard'.tr, + message: 'No data yet'.tr, + ); } return CustomScrollView( physics: const BouncingScrollPhysics(), @@ -24,32 +29,30 @@ class LeaderboardPage extends StatelessWidget { SliverAppBar( expandedHeight: 200, pinned: true, - backgroundColor: Get.isDarkMode - ? FinanceDesignSystem.primaryDark - : const Color(0xFF0A0E21), - leading: IconButton( - icon: const Icon(Icons.arrow_back_ios_new_rounded, - color: Colors.white, size: 20), - onPressed: () => Get.back()), + foregroundColor: Colors.white, + backgroundColor: AppColor.primaryColor, flexibleSpace: FlexibleSpaceBar( centerTitle: true, + titlePadding: const EdgeInsets.symmetric( + horizontal: 48, vertical: 14), title: Text('Leaderboard'.tr, + overflow: TextOverflow.ellipsis, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, - fontSize: 18)), + fontSize: 17)), background: Stack(fit: StackFit.expand, children: [ + // كان التدرّج نيفي ← أحمر قانٍ، وهو لون لا يوجد في + // هوية سيرو ويقرأه المستخدم إشارةَ خطر لا إنجاز. Container( - decoration: const BoxDecoration( - gradient: LinearGradient(colors: [ - Color(0xFF0A0E21), - Color(0xFFB71C1C) - ]))), - Positioned( - right: -40, + decoration: BoxDecoration( + gradient: SiroUi.brandGradient(context))), + PositionedDirectional( + end: -40, top: -20, child: Icon(Icons.leaderboard_rounded, - size: 180, color: Colors.white.withValues(alpha: 0.04))), + size: 180, + color: Colors.white.withValues(alpha: 0.05))), // Top 3 podium if (lc.currentLeaderboard.length >= 3) Positioned( @@ -63,25 +66,26 @@ class LeaderboardPage extends StatelessWidget { // Tab bar SliverToBoxAdapter( child: Container( - margin: const EdgeInsets.fromLTRB(16, 16, 16, 0), + margin: const EdgeInsets.fromLTRB(16, 14, 16, 0), padding: const EdgeInsets.all(4), decoration: BoxDecoration( - color: FinanceDesignSystem.backgroundColor.withValues(alpha: 0.5), + color: SiroUi.border(context).withValues(alpha: 0.5), borderRadius: BorderRadius.circular(14)), child: Row(children: [ - _tab('Trips'.tr, Icons.local_taxi_rounded, 0, lc), - _tab('Earnings'.tr, Icons.monetization_on_rounded, 1, lc), + _tab(context, 'Trips'.tr, Icons.local_taxi_rounded, 0, lc), + _tab(context, 'Earnings'.tr, Icons.monetization_on_rounded, 1, + lc), ]), )), // List SliverPadding( - padding: const EdgeInsets.fromLTRB(16, 16, 16, 40), + padding: const EdgeInsets.fromLTRB(16, 14, 16, 32), sliver: SliverList( delegate: SliverChildBuilderDelegate( (ctx, i) { if (i >= lc.currentLeaderboard.length) return null; return _buildRow( - lc.currentLeaderboard[i], lc.selectedTab == 1); + ctx, lc.currentLeaderboard[i], lc.selectedTab == 1); }, childCount: lc.currentLeaderboard.length, ), @@ -98,15 +102,13 @@ class LeaderboardPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ if (top3.length > 1) - _podiumItem(top3[1], 2, 50, const Color(0xFFC0C0C0)), - if (top3.isNotEmpty) - _podiumItem(top3[0], 1, 70, const Color(0xFFFFD700)), - if (top3.length > 2) - _podiumItem(top3[2], 3, 35, const Color(0xFFCD7F32)), + _podiumItem(top3[1], 2, const Color(0xFFC0C0C0)), + if (top3.isNotEmpty) _podiumItem(top3[0], 1, AppColor.gold), + if (top3.length > 2) _podiumItem(top3[2], 3, AppColor.bronze), ]); } - Widget _podiumItem(LeaderboardEntry e, int rank, double height, Color color) { + Widget _podiumItem(LeaderboardEntry e, int rank, Color color) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Column(mainAxisSize: MainAxisSize.min, children: [ @@ -125,8 +127,10 @@ class LeaderboardPage extends StatelessWidget { ])); } - Widget _tab(String label, IconData icon, int idx, LeaderboardController lc) { + Widget _tab(BuildContext context, String label, IconData icon, int idx, + LeaderboardController lc) { final selected = lc.selectedTab == idx; + final accent = SiroUi.interactive(context); return Expanded( child: GestureDetector( onTap: () => lc.changeTab(idx), @@ -134,60 +138,41 @@ class LeaderboardPage extends StatelessWidget { duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric(vertical: 10), decoration: BoxDecoration( - color: - selected ? FinanceDesignSystem.cardColor : Colors.transparent, + color: selected ? SiroUi.surface(context) : Colors.transparent, borderRadius: BorderRadius.circular(10), - boxShadow: selected - ? [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.05), blurRadius: 5) - ] + border: selected + ? Border.all(color: accent.withValues(alpha: 0.35)) : null), child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, - size: 16, - color: selected ? FinanceDesignSystem.primaryDark : Colors.grey), + size: 16, color: selected ? accent : SiroUi.subtle(context)), const SizedBox(width: 6), - Text(label, - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w600, - color: selected - ? FinanceDesignSystem.primaryDark - : Colors.grey)), + Flexible( + child: Text(label, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: selected ? accent : SiroUi.subtle(context))), + ), ]), ), )); } - Widget _buildRow(LeaderboardEntry e, bool isEarnings) { + Widget _buildRow(BuildContext context, LeaderboardEntry e, bool isEarnings) { + final accent = SiroUi.interactive(context); final rankColor = e.rank == 1 - ? const Color(0xFFFFD700) + ? AppColor.gold : e.rank == 2 ? const Color(0xFFC0C0C0) : e.rank == 3 - ? const Color(0xFFCD7F32) - : Colors.grey.shade400; - return Container( + ? AppColor.bronze + : SiroUi.subtle(context); + return SiroCard( margin: const EdgeInsets.only(bottom: 8), - padding: const EdgeInsets.all(14), - decoration: BoxDecoration( - color: e.isCurrentUser - ? FinanceDesignSystem.accentBlue.withValues(alpha: 0.08) - : FinanceDesignSystem.cardColor, - borderRadius: BorderRadius.circular(14), - border: e.isCurrentUser - ? Border.all( - color: FinanceDesignSystem.accentBlue.withValues(alpha: 0.3), - width: 1.5) - : null, - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.02), - blurRadius: 8, - offset: const Offset(0, 3)) - ], - ), + padding: const EdgeInsets.all(12), + accent: e.isCurrentUser ? accent : null, child: Row(children: [ // Rank Container( @@ -208,49 +193,41 @@ class LeaderboardPage extends StatelessWidget { const SizedBox(width: 12), // Avatar CircleAvatar( - radius: 18, - backgroundColor: FinanceDesignSystem.backgroundColor, + radius: 17, + backgroundColor: accent.withValues(alpha: 0.12), child: Text(e.name.isNotEmpty ? e.name[0] : '?', style: TextStyle( - fontWeight: FontWeight.bold, - color: FinanceDesignSystem.textSecondary))), + fontWeight: FontWeight.bold, color: accent))), const SizedBox(width: 12), // Name Expanded( child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Row(children: [ - Text(e.name, - style: TextStyle( - fontSize: 13, - fontWeight: - e.isCurrentUser ? FontWeight.bold : FontWeight.w500, - color: FinanceDesignSystem.primaryDark)), + Flexible( + child: Text(e.name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: e.isCurrentUser + ? FontWeight.bold + : FontWeight.w500)), + ), if (e.isCurrentUser) ...[ const SizedBox(width: 6), - Container( - padding: - const EdgeInsets.symmetric(horizontal: 6, vertical: 2), - decoration: BoxDecoration( - color: FinanceDesignSystem.accentBlue, - borderRadius: BorderRadius.circular(6)), - child: Text('You'.tr, - style: const TextStyle( - fontSize: 8, - fontWeight: FontWeight.bold, - color: Colors.white))) + SiroTag('You'.tr, color: accent), ], ]), ])), + const SizedBox(width: 8), // Value Text( isEarnings ? '${e.value.toStringAsFixed(0)} ${CurrencyHelper.currency}' : '${e.value.toInt()} ${'Rides'.tr}', - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w800, - color: FinanceDesignSystem.primaryDark)), + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.w800)), ]), ); } diff --git a/siro_driver/lib/views/home/Captin/About Us/settings_captain.dart b/siro_driver/lib/views/home/Captin/About Us/settings_captain.dart index 8faacff8..9de27319 100755 --- a/siro_driver/lib/views/home/Captin/About Us/settings_captain.dart +++ b/siro_driver/lib/views/home/Captin/About Us/settings_captain.dart @@ -3,12 +3,11 @@ import 'package:get/get.dart'; import 'package:siro_driver/constant/colors.dart'; import 'package:siro_driver/controller/profile/setting_controller.dart'; import 'package:siro_driver/views/lang/languages.dart'; -import 'package:siro_driver/views/widgets/my_scafold.dart'; import 'package:siro_driver/views/widgets/mydialoug.dart'; // تأكد من صحة مسارات الاستيراد هذه import '../../../../controller/functions/vibrate.dart'; // Controller with isVibrate -import '../../../auth/country_widget.dart'; +import '../../../widgets/ui/siro_ui.dart'; import 'about_us.dart'; import 'frequantly_question.dart'; import 'using_app_page.dart'; @@ -22,15 +21,13 @@ class SettingsCaptain extends StatelessWidget { final settingsController = Get.put(SettingController()); final homeController = Get.put(HomePageController()); - return MyScafolld( + return SiroPage( title: 'Settings'.tr, - isleading: true, - body: [ - ListView( - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0), + padding: SiroUi.pageInsets, + body: ListView( children: [ // --- القسم الأول: عام --- - _buildSectionHeader('General'.tr, context), + SiroSectionTitle('General'.tr), _buildSettingsCard( children: [ _buildListTile( @@ -53,10 +50,8 @@ class SettingsCaptain extends StatelessWidget { // ), ], ), - const SizedBox(height: 20), - // --- القسم الثاني: تفضيلات التطبيق --- - _buildSectionHeader('App Preferences'.tr, context), + SiroSectionTitle('App Preferences'.tr), _buildSettingsCard( children: [ _buildSwitchTile( @@ -96,10 +91,8 @@ class SettingsCaptain extends StatelessWidget { ), ], ), - const SizedBox(height: 20), - // --- القسم الثالث: المساعدة والدعم --- - _buildSectionHeader('Help & Support'.tr, context), + SiroSectionTitle('Help & Support'.tr), _buildSettingsCard( children: [ _buildListTile( @@ -119,18 +112,17 @@ class SettingsCaptain extends StatelessWidget { ), ], ), - const SizedBox(height: 20), - // --- القسم الرابع: تسجيل الخروج --- - _buildSectionHeader('Account'.tr, context), + SiroSectionTitle('Account'.tr), _buildSettingsCard( children: [ ListTile( - leading: const Icon(Icons.logout, color: Colors.red), + leading: const SiroIconBadge(Icons.logout_rounded, + color: AppColor.redColor), title: Text( 'Logout'.tr, style: const TextStyle( - color: Colors.red, fontWeight: FontWeight.w500), + color: AppColor.redColor, fontWeight: FontWeight.w600), ), onTap: () { MyDialog().getDialog( @@ -145,34 +137,17 @@ class SettingsCaptain extends StatelessWidget { }, ) ], - ) + ), + const SizedBox(height: 16), ], ), - ], ); } - // ويدجت لبناء عنوان كل قسم - Widget _buildSectionHeader(String title, BuildContext context) { - return Padding( - padding: const EdgeInsets.only(left: 8.0, bottom: 12.0), - child: Text( - title, - style: Theme.of(context).textTheme.titleSmall?.copyWith( - color: Colors.grey.shade600, - fontWeight: FontWeight.bold, - ), - ), - ); - } - - // ويدجت لبناء بطاقة الإعدادات + // ويدجت لبناء بطاقة الإعدادات — تجمع عناصر القسم داخل سطح واحد. Widget _buildSettingsCard({required List children}) { - return Card( - elevation: 2, - shadowColor: Colors.black.withOpacity(0.1), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), - clipBehavior: Clip.antiAlias, // مهم لجعل splash effect داخل حدود البطاقة + return SiroCard( + padding: const EdgeInsets.symmetric(vertical: 4), child: Column(children: children), ); } @@ -184,14 +159,18 @@ class SettingsCaptain extends StatelessWidget { String? subtitle, required VoidCallback onTap, }) { - return ListTile( - leading: Icon(icon, color: Colors.grey.shade700), - title: Text(title, style: const TextStyle(fontWeight: FontWeight.w500)), - subtitle: subtitle != null ? Text(subtitle) : null, - trailing: - const Icon(Icons.arrow_forward_ios, size: 16, color: Colors.grey), - onTap: onTap, - ); + return Builder(builder: (context) { + return ListTile( + leading: SiroIconBadge(icon, color: SiroUi.interactive(context)), + title: Text(title, style: const TextStyle(fontWeight: FontWeight.w600)), + subtitle: subtitle != null + ? Text(subtitle, style: Theme.of(context).textTheme.bodySmall) + : null, + trailing: Icon(Icons.arrow_forward_ios, + size: 14, color: SiroUi.subtle(context)), + onTap: onTap, + ); + }); } // ويدجت لبناء عنصر يحتوي على مفتاح تفعيل/إلغاء (Switch) @@ -207,15 +186,19 @@ class SettingsCaptain extends StatelessWidget { return GetBuilder( init: controller, builder: (ctrl) { - return SwitchListTile( - secondary: Icon(icon, color: color ?? Colors.grey.shade700), - title: - Text(title, style: const TextStyle(fontWeight: FontWeight.w500)), - subtitle: Text(subtitle), - value: valueGetter(ctrl), - onChanged: (value) => onChanged(ctrl), - activeColor: AppColor.primaryColor, - ); + return Builder(builder: (context) { + return SwitchListTile( + secondary: SiroIconBadge(icon, + color: color ?? SiroUi.interactive(context)), + title: + Text(title, style: const TextStyle(fontWeight: FontWeight.w600)), + subtitle: Text(subtitle, + style: Theme.of(context).textTheme.bodySmall), + value: valueGetter(ctrl), + onChanged: (value) => onChanged(ctrl), + activeThumbColor: SiroUi.interactive(context), + ); + }); }, ); } diff --git a/siro_driver/lib/views/home/Captin/About Us/video_page.dart b/siro_driver/lib/views/home/Captin/About Us/video_page.dart index d533b4ff..2a1526f6 100755 --- a/siro_driver/lib/views/home/Captin/About Us/video_page.dart +++ b/siro_driver/lib/views/home/Captin/About Us/video_page.dart @@ -5,77 +5,66 @@ import 'package:video_player/video_player.dart'; import 'package:youtube_player_flutter/youtube_player_flutter.dart'; import '../../../../controller/home/captin/help/video_controller.dart'; - -import 'package:flutter/cupertino.dart'; +import '../../../widgets/ui/siro_ui.dart'; class VideoListPage extends StatelessWidget { + VideoListPage({super.key}); + final VideoController videoController = Get.put(VideoController()); @override Widget build(BuildContext context) { - return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - middle: Text('Videos Tutorials'.tr), - ), - child: SafeArea( - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 10.0), // Outer padding around the list - child: GetBuilder( - builder: (videoController) { - return ListView.builder( - itemCount: videoController.videos.length, - itemBuilder: (context, index) { - final video = videoController.videos[index]; - return Container( - margin: const EdgeInsets.symmetric( - vertical: 8.0), // Spacing between each card - decoration: BoxDecoration( - color: CupertinoColors.white, - borderRadius: - BorderRadius.circular(12.0), // Rounded corners - boxShadow: [ - BoxShadow( - color: CupertinoColors.systemGrey.withOpacity(0.3), - offset: const Offset( - 0, 4), // Offset for shadow to appear below - blurRadius: 10.0, // Blur for softer shadow effect - ), - ], - border: Border.all( - color: CupertinoColors.systemGrey4, - width: 0.5, // Subtle border for a refined iOS-like look - ), - ), - child: CupertinoListTile( - title: Padding( - padding: const EdgeInsets.only(bottom: 4.0), - child: Text( - video['title'], - style: const TextStyle( - color: CupertinoColors.black, - fontWeight: FontWeight.bold, + return SiroPage( + title: 'Videos Tutorials'.tr, + padding: SiroUi.pageInsets, + body: GetBuilder( + builder: (videoController) { + if (videoController.videos.isEmpty) { + return SiroEmptyState( + icon: Icons.video_library_rounded, + title: 'No videos available'.tr, + ); + } + + return ListView.builder( + itemCount: videoController.videos.length, + itemBuilder: (context, index) { + final video = videoController.videos[index]; + return SiroCard( + onTap: () => + Get.to(() => VideoPlayerPage1(videoUrl: video['url'])), + child: Row( + children: [ + SiroIconBadge(Icons.play_arrow_rounded, + color: SiroUi.interactive(context), size: 24), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + video['title'] ?? '', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.bold), ), - ), + const SizedBox(height: 3), + Text( + video['description'] ?? '', + style: Theme.of(context).textTheme.bodySmall, + ), + ], ), - subtitle: Text( - video['description'], - style: const TextStyle( - color: CupertinoColors.systemGrey, - ), - ), - onTap: () { - // Navigate to video player page (iOS-style) - Get.to(() => VideoPlayerPage1(videoUrl: video['url'])); - }, ), - ); - }, + Icon(Icons.arrow_forward_ios_rounded, + size: 13, color: SiroUi.subtle(context)), + ], + ), ); }, - ), - ), + ); + }, ), ); } @@ -84,7 +73,7 @@ class VideoListPage extends StatelessWidget { class VideoPlayerPage extends StatelessWidget { final String videoUrl; - VideoPlayerPage({required this.videoUrl}); + VideoPlayerPage({super.key, required this.videoUrl}); final VideoController videoController = Get.put(VideoController()); @@ -93,51 +82,45 @@ class VideoPlayerPage extends StatelessWidget { // Initialize the video when the page is loaded videoController.initializeVideo(videoUrl); - return CupertinoPageScaffold( - navigationBar: const CupertinoNavigationBar( - middle: Text(AppInformation.appName), - ), - child: SafeArea( - child: Center( - child: GetBuilder( - builder: (controller) { - if (!controller.videoPlayerController.value.isInitialized) { - return const CircularProgressIndicator(); // Show loading indicator while initializing - } + return SiroPage( + title: AppInformation.appName, + body: Center( + child: GetBuilder( + builder: (controller) { + if (!controller.videoPlayerController.value.isInitialized) { + return const SiroLoading(); + } - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // Video player widget - AspectRatio( - aspectRatio: - controller.videoPlayerController.value.aspectRatio, - // child: VideoPlayer(controller.videoPlayerController), - child: VideoPlayer(controller.videoPlayerController), - ), - const SizedBox(height: 20), + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // Video player widget + AspectRatio( + aspectRatio: + controller.videoPlayerController.value.aspectRatio, + child: VideoPlayer(controller.videoPlayerController), + ), + const SizedBox(height: 20), - // Play/pause button - CupertinoButton( - onPressed: () { - if (controller.videoPlayerController.value.isPlaying) { - controller.pause(); - } else { - controller.play(); - } - }, - child: Icon( - controller.videoPlayerController.value.isPlaying - ? CupertinoIcons.pause - : CupertinoIcons.play_arrow, - color: CupertinoColors.activeBlue, - size: 30, - ), + // Play/pause button + IconButton.filled( + iconSize: 32, + onPressed: () { + if (controller.videoPlayerController.value.isPlaying) { + controller.pause(); + } else { + controller.play(); + } + }, + icon: Icon( + controller.videoPlayerController.value.isPlaying + ? Icons.pause_rounded + : Icons.play_arrow_rounded, ), - ], - ); - }, - ), + ), + ], + ); + }, ), ), ); @@ -147,15 +130,26 @@ class VideoPlayerPage extends StatelessWidget { class VideoPlayerPage1 extends StatelessWidget { final String videoUrl; - const VideoPlayerPage1({required this.videoUrl}); + const VideoPlayerPage1({super.key, required this.videoUrl}); @override Widget build(BuildContext context) { // Extract the video ID from the URL - String videoId = YoutubePlayer.convertUrlToId(videoUrl)!; + final String? videoId = YoutubePlayer.convertUrlToId(videoUrl); - // Create a YoutubePlayerController - final YoutubePlayerController _controller = YoutubePlayerController( + // رابط غير صالح كان يرمي `Null check operator used on a null value` + // ويُسقط الشاشة بيضاء بدل رسالة مفهومة. + if (videoId == null) { + return SiroPage( + title: AppInformation.appName, + body: SiroEmptyState( + icon: Icons.videocam_off_rounded, + title: 'Video unavailable'.tr, + ), + ); + } + + final YoutubePlayerController controller = YoutubePlayerController( initialVideoId: videoId, flags: const YoutubePlayerFlags( autoPlay: true, @@ -166,24 +160,28 @@ class VideoPlayerPage1 extends StatelessWidget { ); return Scaffold( + backgroundColor: Colors.black, body: Stack( children: [ // Full-screen YouTube player Positioned.fill( child: YoutubePlayer( - controller: _controller, + controller: controller, showVideoProgressIndicator: true, ), ), - // Overlay back button in the top left corner for exit - Positioned( - top: 40.0, - left: 16.0, - child: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.white), - onPressed: () { - Navigator.pop(context); - }, + // زر الخروج — كان `left` ثابتاً فيقع تحت الإبهام الخطأ في العربية + SafeArea( + child: PositionedDirectional( + top: 8, + start: 8, + child: CircleAvatar( + backgroundColor: Colors.black45, + child: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () => Navigator.pop(context), + ), + ), ), ), ], diff --git a/siro_driver/lib/views/home/Captin/history/history_captain.dart b/siro_driver/lib/views/home/Captin/history/history_captain.dart index 91fff076..1cbf4c87 100755 --- a/siro_driver/lib/views/home/Captin/history/history_captain.dart +++ b/siro_driver/lib/views/home/Captin/history/history_captain.dart @@ -3,9 +3,10 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; -import '../../../../constant/finance_design_system.dart'; +import '../../../../constant/colors.dart'; import '../../../../controller/auth/captin/history_captain.dart'; import 'package:siro_driver/views/widgets/mydialoug.dart'; +import '../../../widgets/ui/siro_ui.dart'; class HistoryCaptain extends StatelessWidget { const HistoryCaptain({super.key}); @@ -15,14 +16,10 @@ class HistoryCaptain extends StatelessWidget { Get.put(HistoryCaptainController()); return Scaffold( - backgroundColor: Colors.white, body: GetBuilder( builder: (controller) { if (controller.isloading) { - return Center( - child: CircularProgressIndicator( - color: FinanceDesignSystem.primaryDark), - ); + return const SiroLoading(); } final List trips = controller.historyData['message'] ?? []; @@ -30,20 +27,24 @@ class HistoryCaptain extends StatelessWidget { return CustomScrollView( physics: const BouncingScrollPhysics(), slivers: [ - // 1. Custom App Bar + // 1. رأس بتدرّج العلامة — نفس تدرّج رأس القائمة الجانبية SliverAppBar( - expandedHeight: 180, + expandedHeight: 170, pinned: true, stretch: true, - backgroundColor: FinanceDesignSystem.primaryDark, + foregroundColor: Colors.white, + backgroundColor: AppColor.primaryColor, flexibleSpace: FlexibleSpaceBar( centerTitle: true, + titlePadding: + const EdgeInsets.symmetric(horizontal: 48, vertical: 14), title: Text( 'Ride History'.tr, + overflow: TextOverflow.ellipsis, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, - fontSize: 18, + fontSize: 17, ), ), background: Stack( @@ -51,11 +52,11 @@ class HistoryCaptain extends StatelessWidget { children: [ Container( decoration: BoxDecoration( - gradient: FinanceDesignSystem.balanceGradient, + gradient: SiroUi.brandGradient(context), ), ), - Positioned( - right: -50, + PositionedDirectional( + end: -50, top: -20, child: Icon( Icons.history_rounded, @@ -66,12 +67,11 @@ class HistoryCaptain extends StatelessWidget { Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const SizedBox(height: 40), Text( trips.length.toString(), style: const TextStyle( color: Colors.white, - fontSize: 40, + fontSize: 38, fontWeight: FontWeight.w900, ), ), @@ -79,10 +79,11 @@ class HistoryCaptain extends StatelessWidget { 'Total Rides'.tr, style: TextStyle( color: Colors.white.withValues(alpha: 0.7), - fontSize: 14, + fontSize: 13, fontWeight: FontWeight.w500, ), ), + const SizedBox(height: 34), ], ), ], @@ -93,46 +94,16 @@ class HistoryCaptain extends StatelessWidget { // 2. Trips List if (trips.isEmpty) SliverFillRemaining( - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - padding: const EdgeInsets.all(32), - decoration: BoxDecoration( - color: Colors.grey.shade50, - shape: BoxShape.circle, - ), - child: Icon( - Icons.history_toggle_off_rounded, - size: 64, - color: Colors.grey.shade300, - ), - ), - const SizedBox(height: 24), - Text( - 'No Rides Yet'.tr, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.grey.shade600, - ), - ), - const SizedBox(height: 8), - Text( - 'Your completed trips will appear here'.tr, - style: TextStyle( - fontSize: 14, - color: Colors.grey.shade400, - ), - ), - ], - ), + hasScrollBody: false, + child: SiroEmptyState( + icon: Icons.history_toggle_off_rounded, + title: 'No Rides Yet'.tr, + message: 'Your completed trips will appear here'.tr, ), ) else SliverPadding( - padding: const EdgeInsets.fromLTRB(16, 24, 16, 40), + padding: const EdgeInsets.fromLTRB(16, 20, 16, 32), sliver: SliverList( delegate: SliverChildBuilderDelegate( (context, index) { @@ -197,141 +168,99 @@ class _TripHistoryCard extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.only(bottom: 20), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(FinanceDesignSystem.cardRadius), - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.04), - blurRadius: 15, - offset: const Offset(0, 8), - ), - ], - ), - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: onTap, - borderRadius: BorderRadius.circular(FinanceDesignSystem.cardRadius), - child: Padding( - padding: const EdgeInsets.all(20), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + final theme = Theme.of(context); + final accent = SiroUi.interactive(context); + + return SiroCard( + onTap: onTap, + padding: const EdgeInsets.all(16), + child: Column( + children: [ + Row( + children: [ + SiroIconBadge(Icons.receipt_long_rounded, color: accent), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: FinanceDesignSystem.primaryDark - .withValues(alpha: 0.05), - borderRadius: BorderRadius.circular(12), - ), - child: Icon( - Icons.receipt_long_rounded, - color: FinanceDesignSystem.primaryDark, - size: 20, - ), - ), - const SizedBox(width: 12), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '${'OrderId'.tr} #${trip['order_id']}', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 15, - color: FinanceDesignSystem.primaryDark, - ), - ), - Text( - _formatDateToSyria(trip['created_at']), - style: TextStyle( - fontSize: 11, - color: Colors.grey.shade500, - ), - ), - ], - ), - ], + Text( + '${'OrderId'.tr} #${trip['order_id']}', + overflow: TextOverflow.ellipsis, + style: theme.textTheme.bodyMedium + ?.copyWith(fontWeight: FontWeight.bold), + ), + Text( + _formatDateToSyria(trip['created_at']), + style: theme.textTheme.bodySmall?.copyWith(fontSize: 11), ), - _buildStatusChip(trip['status']), ], ), - const SizedBox(height: 20), - const Divider(height: 1), - const SizedBox(height: 20), - Row( + ), + const SizedBox(width: 8), + _buildStatusChip(trip['status']), + ], + ), + const SizedBox(height: 14), + const Divider(height: 1), + const SizedBox(height: 14), + Row( + children: [ + Column( + children: [ + Icon(Icons.circle, size: 11, color: accent), + Container( + width: 2, + height: 20, + color: SiroUi.border(context), + ), + const Icon(Icons.location_on_rounded, + size: 14, color: AppColor.redColor), + ], + ), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Column( - children: [ - Icon(Icons.circle, - size: 12, color: FinanceDesignSystem.accentBlue), - Container( - width: 2, - height: 20, - color: Colors.grey.shade200, - ), - Icon(Icons.location_on_rounded, - size: 14, color: FinanceDesignSystem.dangerRed), - ], + Text( + trip['start_name'] ?? 'Pickup Location'.tr, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: theme.textTheme.bodySmall + ?.copyWith(fontWeight: FontWeight.w500), ), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - trip['start_name'] ?? 'Pickup Location'.tr, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 13, - color: Colors.grey.shade700, - fontWeight: FontWeight.w500, - ), - ), - const SizedBox(height: 12), - Text( - trip['end_name'] ?? 'Destination Location'.tr, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 13, - color: Colors.grey.shade700, - fontWeight: FontWeight.w500, - ), - ), - ], + const SizedBox(height: 12), + Text( + trip['end_name'] ?? 'Destination Location'.tr, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: theme.textTheme.bodySmall + ?.copyWith(fontWeight: FontWeight.w500), + ), + ], + ), + ), + if (trip['price'] != null) ...[ + const SizedBox(width: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + '${trip['price']} ${CurrencyHelper.currency}', + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.w900, + color: AppColor.greenColor, ), ), - if (trip['price'] != null) - Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - '${trip['price']} ${CurrencyHelper.currency}', - style: TextStyle( - fontWeight: FontWeight.w900, - fontSize: 16, - color: FinanceDesignSystem.primaryDark, - ), - ), - const Icon(Icons.arrow_forward_ios_rounded, - size: 12, color: Colors.grey), - ], - ), + Icon(Icons.arrow_forward_ios_rounded, + size: 11, color: SiroUi.subtle(context)), ], ), ], - ), + ], ), - ), + ], ), ); } @@ -340,73 +269,33 @@ class _TripHistoryCard extends StatelessWidget { Color color; IconData icon; String label = status ?? 'Unknown'; - List gradientColors; switch (status) { case 'Apply': - color = FinanceDesignSystem.successGreen; + color = AppColor.greenColor; icon = Icons.check_circle_rounded; label = 'Completed'.tr; - gradientColors = [color.withOpacity(0.2), color.withOpacity(0.05)]; break; case 'Refused': - color = FinanceDesignSystem.dangerRed; + color = AppColor.redColor; icon = Icons.cancel_rounded; label = 'Refused'.tr; - gradientColors = [color.withOpacity(0.2), color.withOpacity(0.05)]; break; case 'Cancel': - color = Colors.orange; + color = AppColor.yellowColor; icon = Icons.info_rounded; label = 'Cancelled'.tr; - gradientColors = [color.withOpacity(0.2), color.withOpacity(0.05)]; break; case 'CancelByPassenger': - color = const Color(0xFF6B4EFF); + color = AppColor.secondaryColorStatic; icon = Icons.person_off_rounded; label = 'Cancelled by Passenger'.tr; - gradientColors = [color.withOpacity(0.2), color.withOpacity(0.05)]; break; default: color = Colors.grey; icon = Icons.help_rounded; - gradientColors = [color.withOpacity(0.2), color.withOpacity(0.05)]; } - return Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), - decoration: BoxDecoration( - gradient: LinearGradient( - colors: gradientColors, - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - borderRadius: BorderRadius.circular(20), - border: Border.all(color: color.withValues(alpha: 0.3), width: 1), - boxShadow: [ - BoxShadow( - color: color.withValues(alpha: 0.1), - blurRadius: 4, - offset: const Offset(0, 2), - ), - ], - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(icon, size: 14, color: color), - const SizedBox(width: 6), - Text( - label, - style: TextStyle( - color: color, - fontSize: 11, - fontWeight: FontWeight.w800, - letterSpacing: 0.3, - ), - ), - ], - ), - ); + return SiroTag(label, color: color, icon: icon); } } diff --git a/siro_driver/lib/views/home/Captin/home_captain/drawer_captain.dart b/siro_driver/lib/views/home/Captin/home_captain/drawer_captain.dart index eaa6874d..c650d521 100755 --- a/siro_driver/lib/views/home/Captin/home_captain/drawer_captain.dart +++ b/siro_driver/lib/views/home/Captin/home_captain/drawer_captain.dart @@ -26,6 +26,7 @@ import 'package:siro_driver/views/home/profile/profile_captain.dart'; import 'package:siro_driver/views/notification/notification_captain.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../../../constant/colors.dart'; +import '../../../widgets/ui/siro_ui.dart'; import '../../../transit/transit_driver_home_page.dart'; import '../About Us/video_page.dart'; import '../../../food_delivery/food_delivery_home_page.dart'; @@ -46,188 +47,224 @@ class DrawerItem { required this.onTap}); } +/// مجموعة عناصر تحت عنوان واحد. القائمة كانت ٢٢ عنصراً مسطّحاً بـ ٢٢ لوناً +/// مختلفاً، فيصعب العثور على أي بند. التجميع يقصّر زمن البحث بلا تغيير أي وجهة. +class DrawerSection { + final String title; + final List items; + + DrawerSection({required this.title, required this.items}); +} + // --- الويدجت الرئيسية للقائمة الجانبية --- class AppDrawer extends StatelessWidget { AppDrawer({super.key}); final ImageController imageController = Get.put(ImageController()); - // 2. تعريف بيانات القائمة بشكل مركزي ومنظم - final List drawerItems = [ - DrawerItem( - title: 'Mawasalati'.tr, - icon: Icons.directions_bus_filled_rounded, - color: Colors.teal, - onTap: () => Get.to(() => const TransitDriverHomePage())), - DrawerItem( - title: 'Delivery'.tr, - icon: Icons.delivery_dining_rounded, - color: Colors.deepOrange, - onTap: () => Get.to(() => const FoodDeliveryHomePage())), - DrawerItem( - title: 'Balance'.tr, - icon: Icons.account_balance_wallet, - color: Colors.green, - onTap: () => Get.to(() => const WalletCaptainRefactored())), - DrawerItem( - title: 'أرباحي', - icon: Icons.payments_rounded, - color: Colors.teal, - onTap: () => Get.to(() => EarningsPage())), - DrawerItem( - title: 'Challenges'.tr, - icon: Icons.bolt_rounded, - color: Colors.amber, - onTap: () => Get.to(() => ChallengesPage())), - DrawerItem( - title: 'My Schedule'.tr, - icon: Icons.calendar_today_rounded, - color: Colors.teal, - onTap: () => Get.to(() => SchedulePage())), - DrawerItem( - title: 'الرحلات المجدولة القادمة'.tr, // or 'Reserved Rides' - icon: Icons.edit_calendar_rounded, - color: Colors.orangeAccent, - onTap: () => Get.to(() => const DriverScheduledRidesPage())), - DrawerItem( - title: 'Insurance'.tr, - icon: Icons.health_and_safety_rounded, - color: Colors.indigo, - onTap: () => Get.to(() => const DriverInsurancePage())), - DrawerItem( - title: 'Leaderboard'.tr, - icon: Icons.leaderboard_rounded, - color: Colors.red, - onTap: () => Get.to(() => LeaderboardPage())), - DrawerItem( - title: 'Profile'.tr, - icon: Icons.person, - color: Colors.blue, - onTap: () => Get.to(() => ProfileCaptain())), - DrawerItem( - title: 'History of Trip'.tr, - icon: Icons.history, - color: Colors.orange, - onTap: () => Get.to(() => const HistoryCaptain())), - DrawerItem( - title: 'Available for rides'.tr, - icon: Icons.drive_eta, - color: Colors.teal, - onTap: () => Get.to(() => const AvailableRidesPage())), - DrawerItem( - title: 'Notifications'.tr, - icon: Icons.notifications, - color: Colors.purple, - onTap: () => Get.to(() => const NotificationCaptain())), - DrawerItem( - title: 'Helping Center'.tr, - icon: Icons.help_center, - color: Colors.cyan, - onTap: () => Get.to(() => HelpCaptain())), - DrawerItem( - title: 'Invite Driver'.tr, - icon: Icons.person_add_rounded, - color: Colors.indigo, - onTap: () => Get.to(() => InviteScreen())), - // DrawerItem( - // title: 'Maintenance Center'.tr, - // icon: Icons.build, - // color: Colors.brown, - // onTap: () => Get.to(() => MaintainCenterPage())), - // DrawerItem( - // title: 'Health Insurance'.tr, - // icon: Icons.favorite, - // color: Colors.pink, - // onTap: () => Get.to(() => AssuranceHealthPage())), - DrawerItem( - title: 'Contact Us'.tr, - icon: Icons.email, - color: Colors.blueGrey, - onTap: () => Get.to(() => ContactUsPage())), - DrawerItem( - title: 'Videos Tutorials'.tr, - icon: Icons.video_library, - color: Colors.redAccent, - onTap: () => Get.to(() => VideoListPage())), - DrawerItem( - title: 'Rate Our App'.tr, - icon: Icons.star, - color: Colors.amber, - onTap: () => Get.to(() => RatingScreen())), - DrawerItem( - title: 'Is device compatible'.tr, - icon: Icons.memory, - color: Colors.greenAccent, - onTap: () => Get.to(() => DeviceCompatibilityPage())), - DrawerItem( - title: 'Privacy Policy'.tr, - icon: Icons.memory, - color: Colors.greenAccent, - onTap: () => - launchUrl(Uri.parse('${AppLink.server}/privacy_policy.php'))), - DrawerItem( - title: 'Settings'.tr, - icon: Icons.settings, - color: Colors.grey.shade600, - onTap: () => Get.to(() => const SettingsCaptain())), + // 2. تعريف بيانات القائمة مجمَّعة حسب الغرض. + // الوجهات لم تتغيّر — التغيير في الترتيب واللون فقط. + // اللون صار دلالياً: أخضر = مال، كهرماني = مكافآت، أحمر = خروج، + // وما عداه لون العلامة. الطيف العشوائي السابق كان يشتّت لا يوجّه. + final List sections = [ + DrawerSection(title: 'Services'.tr, items: [ + DrawerItem( + title: 'Available for rides'.tr, + icon: Icons.drive_eta_rounded, + color: AppColor.primaryColor, + onTap: () => Get.to(() => const AvailableRidesPage())), + DrawerItem( + title: 'Mawasalati'.tr, + icon: Icons.directions_bus_filled_rounded, + color: AppColor.primaryColor, + onTap: () => Get.to(() => const TransitDriverHomePage())), + DrawerItem( + title: 'Delivery'.tr, + icon: Icons.delivery_dining_rounded, + color: AppColor.primaryColor, + onTap: () => Get.to(() => const FoodDeliveryHomePage())), + ]), + DrawerSection(title: 'Wallet & Earnings'.tr, items: [ + DrawerItem( + title: 'Balance'.tr, + icon: Icons.account_balance_wallet_rounded, + color: AppColor.greenColor, + onTap: () => Get.to(() => const WalletCaptainRefactored())), + DrawerItem( + title: 'My Earnings'.tr, + icon: Icons.payments_rounded, + color: AppColor.greenColor, + onTap: () => Get.to(() => EarningsPage())), + DrawerItem( + title: 'Insurance'.tr, + icon: Icons.health_and_safety_rounded, + color: AppColor.greenColor, + onTap: () => Get.to(() => const DriverInsurancePage())), + ]), + DrawerSection(title: 'My Trips'.tr, items: [ + DrawerItem( + title: 'My Schedule'.tr, + icon: Icons.calendar_today_rounded, + color: AppColor.primaryColor, + onTap: () => Get.to(() => SchedulePage())), + DrawerItem( + title: 'Upcoming Scheduled Rides'.tr, + icon: Icons.edit_calendar_rounded, + color: AppColor.primaryColor, + onTap: () => Get.to(() => const DriverScheduledRidesPage())), + DrawerItem( + title: 'History of Trip'.tr, + icon: Icons.history_rounded, + color: AppColor.primaryColor, + onTap: () => Get.to(() => const HistoryCaptain())), + ]), + DrawerSection(title: 'Rewards'.tr, items: [ + DrawerItem( + title: 'Challenges'.tr, + icon: Icons.bolt_rounded, + color: AppColor.yellowColor, + onTap: () => Get.to(() => ChallengesPage())), + DrawerItem( + title: 'Leaderboard'.tr, + icon: Icons.leaderboard_rounded, + color: AppColor.yellowColor, + onTap: () => Get.to(() => LeaderboardPage())), + DrawerItem( + title: 'Invite Driver'.tr, + icon: Icons.person_add_alt_1_rounded, + color: AppColor.yellowColor, + onTap: () => Get.to(() => InviteScreen())), + ]), + DrawerSection(title: 'My Account'.tr, items: [ + DrawerItem( + title: 'Profile'.tr, + icon: Icons.person_rounded, + color: AppColor.primaryColor, + onTap: () => Get.to(() => ProfileCaptain())), + DrawerItem( + title: 'Notifications'.tr, + icon: Icons.notifications_rounded, + color: AppColor.primaryColor, + onTap: () => Get.to(() => const NotificationCaptain())), + DrawerItem( + title: 'Settings'.tr, + icon: Icons.settings_rounded, + color: AppColor.primaryColor, + onTap: () => Get.to(() => const SettingsCaptain())), + ]), + DrawerSection(title: 'Support'.tr, items: [ + DrawerItem( + title: 'Helping Center'.tr, + icon: Icons.help_center_rounded, + color: AppColor.primaryColor, + onTap: () => Get.to(() => HelpCaptain())), + DrawerItem( + title: 'Contact Us'.tr, + icon: Icons.email_rounded, + color: AppColor.primaryColor, + onTap: () => Get.to(() => ContactUsPage())), + DrawerItem( + title: 'Videos Tutorials'.tr, + icon: Icons.play_circle_fill_rounded, + color: AppColor.primaryColor, + onTap: () => Get.to(() => VideoListPage())), + DrawerItem( + title: 'Rate Our App'.tr, + icon: Icons.star_rounded, + color: AppColor.primaryColor, + onTap: () => Get.to(() => RatingScreen())), + DrawerItem( + title: 'Is device compatible'.tr, + icon: Icons.memory_rounded, + color: AppColor.primaryColor, + onTap: () => Get.to(() => DeviceCompatibilityPage())), + DrawerItem( + title: 'Privacy Policy'.tr, + icon: Icons.privacy_tip_rounded, + color: AppColor.primaryColor, + onTap: () => + launchUrl(Uri.parse('${AppLink.server}/privacy_policy.php'))), + ]), ]; @override Widget build(BuildContext context) { + // نبني قائمة مسطّحة من العناوين والعناصر لتبقى الحركة المتدرّجة تعمل. + final rows = []; + for (final section in sections) { + rows.add(_DrawerSectionTitle(section.title)); + rows.addAll(section.items.map((e) => _DrawerItemTile(item: e))); + } + return Drawer( - child: Container( - color: Theme.of(context).scaffoldBackgroundColor, - child: Column( - children: [ - // --- الجزء العلوي من القائمة (بيانات المستخدم) --- - UserHeader(), // استخدمنا الويدجت المحسنة بالأسفل + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + child: Column( + children: [ + // --- الجزء العلوي من القائمة (بيانات المستخدم) --- + UserHeader(), - // --- قائمة العناصر المتحركة --- - Expanded( - child: AnimationLimiter( - child: ListView.builder( - padding: const EdgeInsets.all(8.0), - itemCount: drawerItems.length + 1, // +1 لزر تسجيل الخروج - itemBuilder: (BuildContext context, int index) { - // --- زر تسجيل الخروج في النهاية --- - if (index == drawerItems.length) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 375), - child: SlideAnimation( - verticalOffset: 50.0, - child: FadeInAnimation( - child: _DrawerItemTile( - item: DrawerItem( - title: 'Sign Out'.tr, - icon: Icons.logout, - color: Colors.red, - onTap: () => - Get.to(() => const LogoutCaptain())), - ), - ), - ), - ); - } - - // --- بقية العناصر --- - final item = drawerItems[index]; - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 375), - child: SlideAnimation( - verticalOffset: 50.0, - child: FadeInAnimation( - child: _DrawerItemTile(item: item), - ), - ), - ); - }, - ), + // --- قائمة العناصر المتحركة --- + Expanded( + child: AnimationLimiter( + child: ListView.builder( + padding: const EdgeInsets.fromLTRB(10, 8, 10, 8), + itemCount: rows.length, + itemBuilder: (BuildContext context, int index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 300), + child: SlideAnimation( + verticalOffset: 40.0, + child: FadeInAnimation(child: rows[index]), + ), + ); + }, ), ), - ], - ), + ), + + // --- تسجيل الخروج مثبَّت أسفل القائمة --- + // كان آخر عنصر في قائمة من ٢٢ بنداً، فيتطلّب تمريراً كاملاً للوصول إليه. + SafeArea( + top: false, + child: Container( + width: double.infinity, + decoration: BoxDecoration( + border: Border( + top: BorderSide(color: Theme.of(context).dividerColor)), + ), + padding: const EdgeInsets.fromLTRB(10, 6, 10, 6), + child: _DrawerItemTile( + item: DrawerItem( + title: 'Sign Out'.tr, + icon: Icons.logout_rounded, + color: AppColor.redColor, + onTap: () => Get.to(() => const LogoutCaptain())), + ), + ), + ), + ], + ), + ); + } +} + +// --- عنوان مجموعة داخل القائمة --- +class _DrawerSectionTitle extends StatelessWidget { + final String title; + const _DrawerSectionTitle(this.title); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsetsDirectional.fromSTEB(14, 16, 14, 6), + child: Text( + title.toUpperCase(), + style: Theme.of(context).textTheme.bodySmall?.copyWith( + fontSize: 11, + fontWeight: FontWeight.bold, + letterSpacing: 0.8, + ), ), ); } @@ -240,22 +277,26 @@ class _DrawerItemTile extends StatelessWidget { @override Widget build(BuildContext context) { + // النيفي الداكن لا يُقرأ على خلفية ليلية، فنستبدله بلون العلامة الفاتح. + final isDark = Theme.of(context).brightness == Brightness.dark; + final color = item.color == AppColor.primaryColor && isDark + ? AppColor.secondaryColorStatic + : item.color; + return Padding( - padding: const EdgeInsets.symmetric(vertical: 4.0), + padding: const EdgeInsets.symmetric(vertical: 2.0), child: ListTile( - leading: Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: item.color.withValues(alpha: 0.1), - shape: BoxShape.circle, - ), - child: Icon(item.icon, color: item.color, size: 24), - ), + dense: true, + visualDensity: const VisualDensity(vertical: -1), + horizontalTitleGap: 12, + contentPadding: const EdgeInsets.symmetric(horizontal: 12), + leading: SiroIconBadge(item.icon, color: color, size: 20), title: Text( item.title, + overflow: TextOverflow.ellipsis, style: Theme.of(context) .textTheme - .titleMedium + .bodyMedium ?.copyWith(fontWeight: FontWeight.w500), ), onTap: () { @@ -265,7 +306,7 @@ class _DrawerItemTile extends StatelessWidget { }); }, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - splashColor: item.color.withValues(alpha: 0.2), + splashColor: color.withValues(alpha: 0.15), ), ); } @@ -398,31 +439,31 @@ class UserHeader extends StatelessWidget { ), ), otherAccountsPictures: [ - SizedBox( - width: 60, + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(999), + ), child: Row( - mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.min, children: [ + const Icon(Icons.star_rounded, color: AppColor.gold, size: 15), + const SizedBox(width: 3), Text( homeCaptainController.rating.toString(), style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, - fontSize: 14), + fontSize: 13), ), - const SizedBox(width: 2), - const Icon(Icons.star, color: Colors.amber, size: 16), ], ), ), ], - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [Theme.of(context).primaryColor, Colors.blue.shade700], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - ), + // تدرّج العلامة نفسه المستعمل في رؤوس الصفحات الداخلية + // (كان `Colors.blue.shade700` وهو أزرق لا علاقة له بهوية سيرو). + decoration: BoxDecoration(gradient: SiroUi.brandGradient(context)), ); } } diff --git a/siro_driver/lib/views/home/Captin/home_captain/help_captain.dart b/siro_driver/lib/views/home/Captin/home_captain/help_captain.dart index c18827fd..c485555c 100755 --- a/siro_driver/lib/views/home/Captin/home_captain/help_captain.dart +++ b/siro_driver/lib/views/home/Captin/home_captain/help_captain.dart @@ -3,50 +3,41 @@ import 'package:get/get.dart'; import 'package:siro_driver/controller/home/captin/help/help_controller.dart'; import 'package:siro_driver/views/home/Captin/home_captain/help_details_replay_page.dart'; -import 'package:flutter/cupertino.dart'; - -import '../../../../constant/colors.dart'; import '../../../../controller/functions/encrypt_decrypt.dart'; -import '../../../widgets/my_scafold.dart'; +import '../../../widgets/ui/siro_ui.dart'; class HelpCaptain extends StatelessWidget { - HelpCaptain({super.key}); + const HelpCaptain({super.key}); @override Widget build(BuildContext context) { Get.put(HelpController()); final theme = Theme.of(context); - return MyScafolld( + return SiroPage( title: 'Helping Page'.tr, - isleading: true, - body: [ - SingleChildScrollView( - padding: const EdgeInsets.all(20.0), + padding: SiroUi.pageInsets, + body: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Container( - padding: const EdgeInsets.all(18.0), - decoration: BoxDecoration( - color: theme.colorScheme.surfaceVariant.withOpacity(0.5), - borderRadius: BorderRadius.circular(15.0), - border: Border.all(color: theme.dividerColor), - ), - child: Text( - 'If you need any help or have questions, this is the right place to do that. You are welcome!' - .tr, - style: theme.textTheme.bodyLarge, - textAlign: TextAlign.center, + SiroCard( + child: Row( + children: [ + SiroIconBadge(Icons.support_agent_rounded, + color: SiroUi.interactive(context)), + const SizedBox(width: 12), + Expanded( + child: Text( + 'If you need any help or have questions, this is the right place to do that. You are welcome!' + .tr, + style: theme.textTheme.bodyMedium, + ), + ), + ], ), ), - const SizedBox(height: 24), - Text( - 'Submit Your Question'.tr, - style: theme.textTheme.titleMedium - ?.copyWith(fontWeight: FontWeight.bold), - ), - const SizedBox(height: 12), + SiroSectionTitle('Submit Your Question'.tr), GetBuilder( builder: (helpController) => Form( key: helpController.formKey, @@ -58,8 +49,6 @@ class HelpCaptain extends StatelessWidget { hintText: 'Enter your Question here'.tr, prefixIcon: const Icon(Icons.question_answer_outlined), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12)), ), maxLines: 3, validator: (value) { @@ -71,10 +60,10 @@ class HelpCaptain extends StatelessWidget { ), const SizedBox(height: 16), helpController.isLoading - ? const Center(child: CircularProgressIndicator()) + ? const SiroLoading() : SizedBox( width: double.infinity, - child: ElevatedButton( + child: ElevatedButton.icon( onPressed: () { if (helpController.formKey.currentState! .validate()) { @@ -83,53 +72,36 @@ class HelpCaptain extends StatelessWidget { .clear(); } }, - style: ElevatedButton.styleFrom( - backgroundColor: AppColor.primaryColor, - foregroundColor: Colors.white, - padding: - const EdgeInsets.symmetric(vertical: 14), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12)), - ), - child: Text('Submit Question'.tr), + icon: const Icon(Icons.send_rounded, size: 18), + label: Text('Submit Question'.tr), ), ), ], ), ), ), - const SizedBox(height: 32), - Text( - 'Your Questions'.tr, - style: theme.textTheme.titleMedium - ?.copyWith(fontWeight: FontWeight.bold), - ), const SizedBox(height: 12), + SiroSectionTitle('Your Questions'.tr), GetBuilder( builder: (helpController) { final questions = helpController.helpQuestionDate['message']; if (questions == null || questions.isEmpty) { - return Center( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Text('No questions asked yet.'.tr), + return Padding( + padding: const EdgeInsets.symmetric(vertical: 24), + child: SiroEmptyState( + icon: Icons.forum_outlined, + title: 'No questions asked yet.'.tr, ), ); } - return ListView.separated( + return ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: questions.length, - separatorBuilder: (context, index) => - const SizedBox(height: 12), itemBuilder: (context, index) { var list = questions[index]; - return Card( - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - side: BorderSide(color: theme.dividerColor), - ), + return SiroCard( + padding: EdgeInsets.zero, child: ListTile( title: Text( EncryptionHelper.instance @@ -142,8 +114,8 @@ class HelpCaptain extends StatelessWidget { list['datecreated'], style: theme.textTheme.bodySmall, ), - trailing: - const Icon(Icons.arrow_forward_ios, size: 16), + trailing: Icon(Icons.arrow_forward_ios, + size: 14, color: SiroUi.subtle(context)), onTap: () { helpController.getIndex( int.parse(EncryptionHelper.instance @@ -159,173 +131,10 @@ class HelpCaptain extends StatelessWidget { ); }, ), + const SizedBox(height: 16), ], ), ), - ], ); } } - -// class HelpCaptain extends StatelessWidget { -// HelpCaptain({super.key}); - -// @override -// Widget build(BuildContext context) { -// Get.put(HelpController()); -// return CupertinoPageScaffold( -// navigationBar: CupertinoNavigationBar( -// middle: Text('Helping Page'.tr), -// leading: CupertinoButton( -// padding: EdgeInsets.zero, -// child: Icon(CupertinoIcons.back), -// onPressed: () => Navigator.pop(context), -// ), -// ), -// child: SafeArea( -// child: Padding( -// padding: const EdgeInsets.all(16.0), -// child: Column( -// children: [ -// Padding( -// padding: const EdgeInsets.symmetric(vertical: 12.0), -// child: Container( -// padding: const EdgeInsets.all(16.0), -// decoration: BoxDecoration( -// borderRadius: BorderRadius.circular(12.0), -// border: Border.all( -// color: CupertinoColors.systemGrey2, -// ), -// ), -// child: Text( -// 'If you need any help or have questions, this is the right place to do that. You are welcome!' -// .tr, -// style: CupertinoTheme.of(context).textTheme.textStyle, -// textAlign: TextAlign.center, -// ), -// ), -// ), -// Card( -// elevation: 3, -// color: CupertinoColors.systemGrey6, -// shape: RoundedRectangleBorder( -// borderRadius: BorderRadius.circular(12.0), -// ), -// child: Padding( -// padding: const EdgeInsets.all(16.0), -// child: GetBuilder( -// builder: (helpController) => Form( -// key: helpController.formKey, -// child: Column( -// children: [ -// CupertinoTextField( -// controller: helpController.helpQuestionController, -// placeholder: 'Enter your Question here'.tr, -// decoration: BoxDecoration( -// borderRadius: BorderRadius.circular(12), -// border: Border.all( -// color: CupertinoColors.systemGrey, -// ), -// ), -// padding: const EdgeInsets.all(16), -// clearButtonMode: OverlayVisibilityMode.editing, -// ), -// const SizedBox(height: 20), -// helpController.isLoading -// ? const CupertinoActivityIndicator() -// : CupertinoButton.filled( -// onPressed: () { -// if (helpController.formKey.currentState! -// .validate()) { -// helpController.addHelpQuestion(); - -// // Clear the feedback form -// helpController.formKey.currentState! -// .reset(); -// } -// }, -// child: Text('Submit Question'.tr), -// ), -// ], -// ), -// ), -// ), -// ), -// ), -// const SizedBox(height: 20), -// Expanded( -// child: GetBuilder( -// builder: (helpController) => Padding( -// padding: const EdgeInsets.all(10), -// child: Container( -// decoration: BoxDecoration( -// border: Border.all( -// color: CupertinoColors.systemGrey2, -// ), -// borderRadius: BorderRadius.circular(12.0), -// ), -// child: ListView.builder( -// itemCount: helpController.helpQuestionDate['message'] != -// null -// ? helpController.helpQuestionDate['message'].length -// : 0, -// itemBuilder: (BuildContext context, int index) { -// var list = -// helpController.helpQuestionDate['message'][index]; -// return Padding( -// padding: const EdgeInsets.all(3), -// child: Container( -// padding: const EdgeInsets.symmetric( -// vertical: 12, horizontal: 16), -// decoration: BoxDecoration( -// border: Border.all( -// color: CupertinoColors.activeGreen, -// width: 2, -// ), -// borderRadius: BorderRadius.circular(12), -// ), -// child: GestureDetector( -// onTap: () { -// helpController.getIndex( -// list['id'], list['helpQuestion']); -// helpController -// .getHelpRepley(list['id'].toString()); -// Get.to(() => const HelpDetailsReplayPage()); -// }, -// child: Row( -// mainAxisAlignment: -// MainAxisAlignment.spaceBetween, -// children: [ -// Expanded( -// child: Text( -// list['helpQuestion'], -// style: CupertinoTheme.of(context) -// .textTheme -// .textStyle, -// overflow: TextOverflow.ellipsis, -// ), -// ), -// Text( -// list['datecreated'], -// style: CupertinoTheme.of(context) -// .textTheme -// .tabLabelTextStyle, -// ), -// ], -// ), -// ), -// ), -// ); -// }, -// ), -// ), -// ), -// ), -// ), -// ], -// ), -// ), -// ), -// ); -// } -// } diff --git a/siro_driver/lib/views/home/Captin/orderCaptin/order_request_page.dart b/siro_driver/lib/views/home/Captin/orderCaptin/order_request_page.dart index 4f93ce54..b46d8f0c 100755 --- a/siro_driver/lib/views/home/Captin/orderCaptin/order_request_page.dart +++ b/siro_driver/lib/views/home/Captin/orderCaptin/order_request_page.dart @@ -79,7 +79,7 @@ class OrderRequestPage extends StatelessWidget { zoom: 13.0, ), markers: controller.markers, - mapType: Get.isDarkMode + mapType: Theme.of(context).brightness == Brightness.dark ? IntaleqMapType.normal : IntaleqMapType.light, polylines: controller.polylines, diff --git a/siro_driver/lib/views/home/journal/driver_insurance_page.dart b/siro_driver/lib/views/home/journal/driver_insurance_page.dart index b91245ef..9b99eb1a 100644 --- a/siro_driver/lib/views/home/journal/driver_insurance_page.dart +++ b/siro_driver/lib/views/home/journal/driver_insurance_page.dart @@ -3,6 +3,7 @@ import 'package:get/get.dart'; import '../../../constant/colors.dart'; import '../../../controller/insurance/driver_insurance_controller.dart'; +import '../../widgets/ui/siro_ui.dart'; /// شاشة التأمين: الوثيقة النشطة ودفترها، أو الخطط المتاحة. /// @@ -16,18 +17,20 @@ class DriverInsurancePage extends StatelessWidget { Widget build(BuildContext context) { final c = Get.put(DriverInsuranceController()); - return Scaffold( - appBar: AppBar(title: Text('Insurance'.tr), centerTitle: true), + return SiroPage( + title: 'Insurance'.tr, body: Obx(() { if (c.isLoading.value) { - return const Center(child: CircularProgressIndicator()); + return const SiroLoading(); } return RefreshIndicator( onRefresh: c.refreshAll, + color: SiroUi.interactive(context), child: ListView( - padding: const EdgeInsets.all(16), - children: c.hasPolicy ? _policyView(context, c) : _plansView(context, c), + padding: SiroUi.pageInsets, + children: + c.hasPolicy ? _policyView(context, c) : _plansView(context, c), ), ); }), @@ -39,29 +42,34 @@ class DriverInsurancePage extends StatelessWidget { final p = c.policy.value!; return [ - Card( - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( + SiroCard( + accent: AppColor.greenColor, + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - const Icon(Icons.verified_user, color: Colors.green), - const SizedBox(width: 8), + const SiroIconBadge(Icons.verified_user_rounded, + color: AppColor.greenColor), + const SizedBox(width: 10), Expanded( - child: Text( - '${p['plan'] ?? ''}', - style: const TextStyle( - fontSize: 18, fontWeight: FontWeight.bold), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${p['plan'] ?? ''}', + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith(fontWeight: FontWeight.bold), + ), + Text('${p['provider'] ?? ''}', + style: Theme.of(context).textTheme.bodySmall), + ], ), ), ], ), - const SizedBox(height: 4), - Text('${p['provider'] ?? ''}', - style: TextStyle(color: Colors.grey.shade600)), const Divider(height: 24), _row('Premium'.tr, '${p['premium']} ${p['currency']} / ' @@ -72,28 +80,25 @@ class DriverInsurancePage extends StatelessWidget { if ((p['coverage'] ?? '').toString().isNotEmpty) ...[ const SizedBox(height: 12), Text('${p['coverage']}', - style: TextStyle(color: Colors.grey.shade700)), + style: Theme.of(context).textTheme.bodySmall), ], ], ), - ), ), - const SizedBox(height: 16), // ── دفتر الأقساط ── // يظهر في نفس الشاشة لا في أخرى: الخصم الذي لا يرى السائق مقابله // مفاجأة تُفقد الثقة، مهما كان مشروحاً في مكان آخر. - Card( - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( + SiroCard( + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Premium ledger'.tr, - style: const TextStyle( - fontSize: 16, fontWeight: FontWeight.bold)), - const SizedBox(height: 12), + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith(fontWeight: FontWeight.bold)), + const SizedBox(height: 8), _row('Outstanding'.tr, '${c.pendingTotal.toStringAsFixed(2)} ${c.currency}'), _row('Settled'.tr, @@ -106,13 +111,13 @@ class DriverInsurancePage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('${e['charge_date']}', - style: TextStyle(color: Colors.grey.shade700)), + style: Theme.of(context).textTheme.bodySmall), Text('${e['amount']} ${e['currency']}', style: TextStyle( color: e['status'] == 'settled' - ? Colors.green - : Colors.orange.shade800, - fontWeight: FontWeight.w600, + ? AppColor.greenColor + : AppColor.yellowColor, + fontWeight: FontWeight.w700, )), ], ), @@ -120,20 +125,21 @@ class DriverInsurancePage extends StatelessWidget { ], ], ), - ), ), - const SizedBox(height: 24), + const SizedBox(height: 12), OutlinedButton.icon( - onPressed: c.isSubmitting.value ? null : () => _confirmCancel(context, c), - icon: const Icon(Icons.cancel_outlined, color: Colors.red), - label: Text('Cancel policy'.tr, - style: const TextStyle(color: Colors.red)), + onPressed: + c.isSubmitting.value ? null : () => _confirmCancel(context, c), + icon: const Icon(Icons.cancel_outlined, size: 18), + label: Text('Cancel policy'.tr), style: OutlinedButton.styleFrom( - side: const BorderSide(color: Colors.red), + foregroundColor: AppColor.redColor, + side: const BorderSide(color: AppColor.redColor), minimumSize: const Size.fromHeight(48), ), ), + const SizedBox(height: 16), ]; } @@ -144,34 +150,15 @@ class DriverInsurancePage extends StatelessWidget { // ‏الحالة الحقيقية: لا خطط مُفعّلة بعد لدى الشركاء — لا خطأ ولا // ‏نقص أهلية — ونقولها صراحة حتى لا يظن الكابتن أن التطبيق خربان. return [ - const SizedBox(height: 72), - Icon(Icons.health_and_safety_outlined, - size: 80, color: Colors.grey.shade400), - const SizedBox(height: 16), - Text('No insurance plans available yet'.tr, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.w700, - color: Colors.grey)), - const SizedBox(height: 10), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: Text( - 'Insurance plans have not been launched in your area yet. Once a partner plan is available it will appear here automatically.' - .tr, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, height: 1.5, color: Colors.grey.shade600), - ), - ), - const SizedBox(height: 20), - Center( - child: TextButton.icon( - onPressed: c.isLoading.value ? null : () => c.refreshAll(), - icon: const Icon(Icons.refresh_rounded, size: 18), - label: Text('Refresh'.tr), - ), + const SizedBox(height: 48), + SiroEmptyState( + icon: Icons.health_and_safety_outlined, + title: 'No insurance plans available yet'.tr, + message: + 'Insurance plans have not been launched in your area yet. Once a partner plan is available it will appear here automatically.' + .tr, + actionLabel: 'Refresh'.tr, + onAction: c.isLoading.value ? null : () => c.refreshAll(), ), ]; } @@ -182,46 +169,58 @@ class DriverInsurancePage extends StatelessWidget { final progress = (plan['progress_rides'] ?? 0) as num; final required = (plan['required_rides'] ?? 0) as num; - return Card( - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), - margin: const EdgeInsets.only(bottom: 16), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( + return SiroCard( + accent: eligible ? AppColor.greenColor : null, + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('${plan['name'] ?? ''}', - style: const TextStyle( - fontSize: 18, fontWeight: FontWeight.bold)), + Row( + children: [ + Expanded( + child: Text('${plan['name'] ?? ''}', + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith(fontWeight: FontWeight.bold)), + ), + if (eligible) + SiroTag('Eligible'.tr, + color: AppColor.greenColor, + icon: Icons.check_circle_rounded), + ], + ), Text('${plan['provider'] ?? ''}', - style: TextStyle(color: Colors.grey.shade600)), + style: Theme.of(context).textTheme.bodySmall), const SizedBox(height: 8), Text( '${plan['premium']} ${plan['currency']} / ' '${plan['billing_cycle'] == 'monthly' ? 'Monthly'.tr : 'Daily'.tr}', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: AppColor.primaryColor), + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + color: SiroUi.interactive(context), + ), ), if ((plan['coverage'] ?? '').toString().isNotEmpty) ...[ const SizedBox(height: 8), Text('${plan['coverage']}', - style: TextStyle(color: Colors.grey.shade700)), + style: Theme.of(context).textTheme.bodySmall), ], // شريط التقدّم نحو الشرط — الرقم وحده لا يحرّك أحداً، // ورؤية القرب من الهدف تفعل. if (!eligible && required > 0) ...[ const SizedBox(height: 12), - LinearProgressIndicator( - value: required == 0 ? 0 : (progress / required).clamp(0, 1), - backgroundColor: Colors.grey.shade300, + ClipRRect( + borderRadius: BorderRadius.circular(999), + child: LinearProgressIndicator( + minHeight: 7, + value: required == 0 ? 0 : (progress / required).clamp(0, 1), + backgroundColor: SiroUi.border(context), + ), ), const SizedBox(height: 6), Text('$progress / $required ${'rides'.tr}', - style: TextStyle( - fontSize: 12, color: Colors.grey.shade600)), + style: Theme.of(context).textTheme.bodySmall), ], if (!eligible && missing.isNotEmpty) ...[ @@ -230,33 +229,31 @@ class DriverInsurancePage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ const Icon(Icons.lock_outline, - size: 14, color: Colors.orange), + size: 14, color: AppColor.yellowColor), const SizedBox(width: 6), Expanded( child: Text('$m', - style: TextStyle( + style: const TextStyle( fontSize: 12, - color: Colors.orange.shade900)), + color: AppColor.yellowColor)), ), ], )), ], - const SizedBox(height: 12), - ElevatedButton( - onPressed: (!eligible || c.isSubmitting.value) - ? null - : () => c.subscribe(plan['id'] as int), - style: ElevatedButton.styleFrom( - backgroundColor: AppColor.primaryColor, - foregroundColor: Colors.white, - minimumSize: const Size.fromHeight(44), + const SizedBox(height: 14), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: (!eligible || c.isSubmitting.value) + ? null + : () => c.subscribe(plan['id'] as int), + child: + Text(eligible ? 'Subscribe'.tr : 'Not eligible yet'.tr), ), - child: Text(eligible ? 'Subscribe'.tr : 'Not eligible yet'.tr), ), ], ), - ), ); }).toList(); } @@ -283,7 +280,7 @@ class DriverInsurancePage extends StatelessWidget { c.cancel(); }, child: Text('Confirm'.tr, - style: const TextStyle(color: Colors.red)), + style: const TextStyle(color: AppColor.redColor)), ), ], ), @@ -293,18 +290,6 @@ class DriverInsurancePage extends StatelessWidget { static String ledgerNum(dynamic v) => (double.tryParse('${v ?? 0}') ?? 0).toStringAsFixed(2); - Widget _row(String label, String value) => Padding( - padding: const EdgeInsets.symmetric(vertical: 4), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(label, style: TextStyle(color: Colors.grey.shade700)), - Flexible( - child: Text(value, - textAlign: TextAlign.end, - style: const TextStyle(fontWeight: FontWeight.w600)), - ), - ], - ), - ); + Widget _row(String label, String value) => + SiroDetailRow(label: label, value: value); } diff --git a/siro_driver/lib/views/home/journal/driver_scheduled_rides_page.dart b/siro_driver/lib/views/home/journal/driver_scheduled_rides_page.dart index 337aab49..1fd78859 100644 --- a/siro_driver/lib/views/home/journal/driver_scheduled_rides_page.dart +++ b/siro_driver/lib/views/home/journal/driver_scheduled_rides_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../../controller/scheduled/driver_scheduled_rides_controller.dart'; import '../../../constant/colors.dart'; +import '../../widgets/ui/siro_ui.dart'; class DriverScheduledRidesPage extends StatelessWidget { const DriverScheduledRidesPage({super.key}); @@ -10,75 +11,35 @@ class DriverScheduledRidesPage extends StatelessWidget { Widget build(BuildContext context) { final controller = Get.put(DriverScheduledRidesController()); - return Scaffold( - appBar: AppBar( - title: Text('Upcoming Scheduled Rides'.tr), - centerTitle: true, - ), + return SiroPage( + title: 'Upcoming Scheduled Rides'.tr, body: Obx(() { if (controller.isLoading.value) { - return const Center(child: CircularProgressIndicator()); + return const SiroLoading(); } // ‏تعذّر الجلب: نقولها بدل ادّعاء "لا حجوزات" — الفرق بينهما // ‏هو الفرق بين كابتن يطمئن وكابتن يفوته عمل مؤكد. if (controller.hasError.value) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.cloud_off_rounded, - size: 80, color: Colors.grey.shade400), - const SizedBox(height: 16), - Text( - 'Could not load scheduled rides'.tr, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.w700, - color: Colors.grey), - ), - const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 32), - child: Text( - 'Please check your connection and try again.'.tr, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, color: Colors.grey.shade600, height: 1.5), - ), - ), - const SizedBox(height: 16), - ElevatedButton.icon( - onPressed: controller.fetch, - icon: const Icon(Icons.refresh_rounded, size: 18), - label: Text('Retry'.tr), - ), - ], - ), + return SiroErrorState( + message: '${'Could not load scheduled rides'.tr}\n' + '${'Please check your connection and try again.'.tr}', + onRetry: controller.fetch, ); } if (controller.bookings.isEmpty) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.calendar_month, - size: 80, color: Colors.grey.shade400), - const SizedBox(height: 16), - Text( - 'You have no upcoming scheduled rides'.tr, - style: const TextStyle(fontSize: 18, color: Colors.grey), - ), - ], - ), + return SiroEmptyState( + icon: Icons.calendar_month_rounded, + title: 'You have no upcoming scheduled rides'.tr, ); } return RefreshIndicator( onRefresh: controller.fetch, + color: SiroUi.interactive(context), child: ListView.builder( - padding: const EdgeInsets.all(12), + padding: SiroUi.pageInsets, itemCount: controller.bookings.length, itemBuilder: (context, index) { final booking = controller.bookings[index]; @@ -91,75 +52,65 @@ class DriverScheduledRidesPage extends StatelessWidget { } Widget _buildBookingCard(Map booking, BuildContext context) { - return Card( - margin: const EdgeInsets.only(bottom: 12), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - elevation: 2, - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), - decoration: BoxDecoration( - color: AppColor.primaryColor.withOpacity(0.1), - borderRadius: BorderRadius.circular(20), - ), - child: Row( - children: [ - const Icon(Icons.access_time, - size: 16, color: AppColor.primaryColor), - const SizedBox(width: 6), - Text( - booking['scheduled_at'] ?? '', - style: const TextStyle( - color: AppColor.primaryColor, - fontWeight: FontWeight.bold), - ), - ], + return SiroCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Align( + alignment: AlignmentDirectional.centerStart, + child: SiroTag( + booking['scheduled_at'] ?? '', + color: SiroUi.interactive(context), + icon: Icons.access_time_rounded, ), ), - Text( - '${booking['estimated_price']} ${'Currency'.tr}', - style: const TextStyle( - fontWeight: FontWeight.bold, fontSize: 16), + ), + const SizedBox(width: 8), + Text( + '${booking['estimated_price']} ${'Currency'.tr}', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + color: AppColor.greenColor, + ), + ), + ], + ), + const SizedBox(height: 14), + Row( + children: [ + const Icon(Icons.my_location_rounded, + color: AppColor.greenColor, size: 18), + const SizedBox(width: 10), + Expanded( + child: Text( + booking['start_name'] ?? 'Starting Point'.tr, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyMedium, ), - ], - ), - const SizedBox(height: 16), - Row( - children: [ - const Icon(Icons.my_location, color: Colors.green, size: 20), - const SizedBox(width: 8), - Expanded( - child: Text( - booking['start_name'] ?? 'Starting Point'.tr, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + const Icon(Icons.location_on_rounded, + color: AppColor.redColor, size: 18), + const SizedBox(width: 10), + Expanded( + child: Text( + booking['end_name'] ?? 'Destination'.tr, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyMedium, ), - ], - ), - const SizedBox(height: 8), - Row( - children: [ - const Icon(Icons.location_on, color: Colors.red, size: 20), - const SizedBox(width: 8), - Expanded( - child: Text( - booking['end_name'] ?? 'Destination'.tr, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - ], - ), + ), + ], + ), + ], ), ); } diff --git a/siro_driver/lib/views/home/journal/schedule_page.dart b/siro_driver/lib/views/home/journal/schedule_page.dart index 8ab68707..fb21cb8f 100644 --- a/siro_driver/lib/views/home/journal/schedule_page.dart +++ b/siro_driver/lib/views/home/journal/schedule_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import '../../../constant/finance_design_system.dart'; import '../../../controller/home/journal/schedule_controller.dart'; +import '../../widgets/ui/siro_ui.dart'; class SchedulePage extends StatelessWidget { SchedulePage({super.key}); @@ -9,71 +9,34 @@ class SchedulePage extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: FinanceDesignSystem.backgroundColor, - appBar: AppBar( - title: Text('My Schedule'.tr, - style: TextStyle( - fontWeight: FontWeight.bold, - color: FinanceDesignSystem.primaryDark)), - backgroundColor: Colors.transparent, - elevation: 0, - centerTitle: true, - leading: IconButton( - icon: Icon(Icons.arrow_back_ios_new_rounded, - color: FinanceDesignSystem.primaryDark, size: 20), - onPressed: () => Get.back()), - ), + return SiroPage( + title: 'My Schedule'.tr, + padding: SiroUi.pageInsets, body: GetBuilder(builder: (sc) { return SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: - Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Summary Card - Container( - padding: const EdgeInsets.all(20), - decoration: BoxDecoration( - gradient: FinanceDesignSystem.balanceGradient, - borderRadius: - BorderRadius.circular(FinanceDesignSystem.cardRadius), - ), - child: Row(children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Weekly Plan'.tr, - style: TextStyle( - color: Colors.white.withValues(alpha: 0.7), - fontSize: 14)), - const SizedBox(height: 8), - Text('${sc.totalWeeklyHours.toStringAsFixed(1)}h', - style: const TextStyle( - fontSize: 32, - fontWeight: FontWeight.w900, - color: Colors.white)), - Text('${sc.activeDays} ${'Days'.tr}', - style: TextStyle( - color: Colors.white.withValues(alpha: 0.6), - fontSize: 13)), - ])), - Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SiroHeroHeader( + label: 'Weekly Plan'.tr, + value: '${sc.totalWeeklyHours.toStringAsFixed(1)}h', + caption: '${sc.activeDays} ${'Days'.tr}', + icon: Icons.calendar_today_rounded, + trailing: Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.15), - borderRadius: BorderRadius.circular(14)), - child: const Icon(Icons.calendar_today_rounded, - color: Colors.white, size: 28), + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(14), + ), + child: const Icon(Icons.calendar_month_rounded, + color: Colors.white, size: 26), ), - ]), - ), - const SizedBox(height: 24), - - Text('Work Days'.tr, style: FinanceDesignSystem.headingStyle), - const SizedBox(height: 12), - - ...sc.schedule.map((slot) => _buildDayCard(context, slot, sc)), - ]), + ), + SiroSectionTitle('Work Days'.tr), + ...sc.schedule.map((slot) => _buildDayCard(context, slot, sc)), + const SizedBox(height: 16), + ], + ), ); }), ); @@ -82,76 +45,72 @@ class SchedulePage extends StatelessWidget { Widget _buildDayCard( BuildContext context, WorkSlot slot, ScheduleController sc) { final isAr = Get.locale?.languageCode == 'ar'; - return Container( + final accent = SiroUi.interactive(context); + final off = SiroUi.subtle(context); + + return SiroCard( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), margin: const EdgeInsets.only(bottom: 10), - padding: const EdgeInsets.all(14), - decoration: BoxDecoration( - color: slot.isActive ? Colors.white : Colors.grey.shade50, - borderRadius: BorderRadius.circular(14), - boxShadow: slot.isActive - ? [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.03), - blurRadius: 8, - offset: const Offset(0, 3)) - ] - : null, - ), + accent: slot.isActive ? accent : null, child: Row(children: [ // Toggle GestureDetector( onTap: () => sc.toggleDay(slot.dayOfWeek), child: AnimatedContainer( duration: const Duration(milliseconds: 200), - width: 44, - height: 44, + width: 42, + height: 42, decoration: BoxDecoration( color: slot.isActive - ? FinanceDesignSystem.accentBlue.withValues(alpha: 0.1) - : Colors.grey.shade200, + ? accent.withValues(alpha: 0.12) + : off.withValues(alpha: 0.10), borderRadius: BorderRadius.circular(12), ), child: Center( - child: Text( - isAr ? slot.dayNameAr.substring(0, 2) : slot.dayName, - style: TextStyle( + child: Text( + isAr ? slot.dayNameAr.substring(0, 2) : slot.dayName, + style: TextStyle( fontSize: 12, fontWeight: FontWeight.bold, - color: slot.isActive - ? FinanceDesignSystem.accentBlue - : Colors.grey.shade400), - )), + color: slot.isActive ? accent : off, + ), + ), + ), ), ), - const SizedBox(width: 14), + const SizedBox(width: 12), // Day name Expanded( - child: - Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(isAr ? slot.dayNameAr : slot.dayName.tr, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - color: slot.isActive - ? FinanceDesignSystem.primaryDark - : Colors.grey.shade400)), - if (slot.isActive) - Text(slot.timeRange, - style: TextStyle(fontSize: 12, color: Colors.grey.shade500)), - if (!slot.isActive) - Text('Day Off'.tr, - style: TextStyle( - fontSize: 12, - color: Colors.grey.shade400, - fontStyle: FontStyle.italic)), - ])), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + isAr ? slot.dayNameAr : slot.dayName.tr, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w600, + color: slot.isActive ? null : off, + ), + ), + Text( + slot.isActive ? slot.timeRange : 'Day Off'.tr, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + fontSize: 12, + fontStyle: + slot.isActive ? FontStyle.normal : FontStyle.italic, + ), + ), + ], + ), + ), // Time pickers if (slot.isActive) ...[ _timePicker(context, slot.startTime, (t) => sc.updateStartTime(slot.dayOfWeek, t)), Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Text('-', style: TextStyle(color: Colors.grey.shade400))), + padding: const EdgeInsets.symmetric(horizontal: 3), + child: Text('-', style: TextStyle(color: off)), + ), _timePicker(context, slot.endTime, (t) => sc.updateEndTime(slot.dayOfWeek, t)), ], @@ -159,7 +118,7 @@ class SchedulePage extends StatelessWidget { Switch( value: slot.isActive, onChanged: (_) => sc.toggleDay(slot.dayOfWeek), - activeThumbColor: FinanceDesignSystem.accentBlue, + activeThumbColor: accent, ), ]), ); @@ -174,16 +133,19 @@ class SchedulePage extends StatelessWidget { if (picked != null) onChanged(picked); }, child: Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 5), decoration: BoxDecoration( - color: Colors.grey.shade100, - borderRadius: BorderRadius.circular(8)), + color: SiroUi.interactive(context).withValues(alpha: 0.10), + borderRadius: BorderRadius.circular(8), + ), child: Text( - '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: FinanceDesignSystem.primaryDark)), + '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w700, + color: SiroUi.interactive(context), + ), + ), ), ); } diff --git a/siro_driver/lib/views/home/my_wallet/pay_out_screen.dart b/siro_driver/lib/views/home/my_wallet/pay_out_screen.dart index 131257c7..6ee3a1e9 100644 --- a/siro_driver/lib/views/home/my_wallet/pay_out_screen.dart +++ b/siro_driver/lib/views/home/my_wallet/pay_out_screen.dart @@ -4,6 +4,7 @@ import 'package:siro_driver/constant/box_name.dart'; import 'package:siro_driver/main.dart'; import '../../../controller/payment/smsPaymnet/pay_out_syria_controller.dart'; +import '../../widgets/ui/siro_ui.dart'; class PayoutScreen extends StatefulWidget { // استقبال كل البيانات المطلوبة جاهزة @@ -128,10 +129,10 @@ class _PayoutScreenState extends State { // حساب المبلغ الإجمالي المخصوم final totalDeducted = widget.amountToWithdraw + PayoutService.payoutFee; - return Scaffold( - appBar: AppBar(title: const Text("تأكيد سحب الأموال")), + return SiroPage( + title: "تأكيد سحب الأموال", body: SingleChildScrollView( - padding: const EdgeInsets.all(16.0), + padding: SiroUi.pageInsets, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -164,13 +165,10 @@ class _PayoutScreenState extends State { } Widget _buildSummaryCard(double totalDeducted) { - return Card( - elevation: 2, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ + return SiroCard( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ _summaryRow("المبلغ المسحوب:", "${widget.amountToWithdraw.toStringAsFixed(2)} ل.س"), const Divider(), @@ -187,7 +185,6 @@ class _PayoutScreenState extends State { _summaryRow("عبر محفظة:", widget.walletType), ], ), - ), ); } diff --git a/siro_driver/lib/views/home/my_wallet/payment_history_driver_page.dart b/siro_driver/lib/views/home/my_wallet/payment_history_driver_page.dart index 1ec94560..a35268cb 100755 --- a/siro_driver/lib/views/home/my_wallet/payment_history_driver_page.dart +++ b/siro_driver/lib/views/home/my_wallet/payment_history_driver_page.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:siro_driver/constant/finance_design_system.dart'; -import 'package:siro_driver/views/widgets/mycircular.dart'; import '../../../controller/payment/driver_payment_controller.dart'; import 'widgets/transaction_preview_item.dart'; +import '../../widgets/ui/siro_ui.dart'; class PaymentHistoryDriverPage extends StatelessWidget { const PaymentHistoryDriverPage({super.key}); @@ -13,26 +13,12 @@ class PaymentHistoryDriverPage extends StatelessWidget { Widget build(BuildContext context) { Get.put(DriverWalletHistoryController()); - return Scaffold( - backgroundColor: FinanceDesignSystem.backgroundColor, - appBar: AppBar( - title: Text('Payment History'.tr, - style: TextStyle( - fontWeight: FontWeight.bold, - color: FinanceDesignSystem.primaryDark)), - backgroundColor: Colors.transparent, - elevation: 0, - centerTitle: true, - leading: IconButton( - icon: Icon(Icons.arrow_back_ios_new_rounded, - color: FinanceDesignSystem.primaryDark, size: 20), - onPressed: () => Get.back(), - ), - ), + return SiroPage( + title: 'Payment History'.tr, body: GetBuilder( builder: (controller) { if (controller.isLoading) { - return const Center(child: MyCircularProgressIndicator()); + return const SiroLoading(); } if (controller.archive.isEmpty) { diff --git a/siro_driver/lib/views/home/my_wallet/walet_captain.dart b/siro_driver/lib/views/home/my_wallet/walet_captain.dart index fb1b38cb..aff0f37a 100755 --- a/siro_driver/lib/views/home/my_wallet/walet_captain.dart +++ b/siro_driver/lib/views/home/my_wallet/walet_captain.dart @@ -5,7 +5,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:siro_driver/constant/finance_design_system.dart'; import 'package:siro_driver/controller/home/payment/captain_wallet_controller.dart'; -import 'package:siro_driver/views/widgets/mycircular.dart'; import 'package:siro_driver/views/widgets/elevated_btn.dart'; import 'package:siro_driver/views/widgets/my_textField.dart'; import 'package:siro_driver/views/widgets/mydialoug.dart'; @@ -25,6 +24,7 @@ import 'widgets/quick_actions.dart'; import 'widgets/financial_summary_card.dart'; import 'widgets/promo_gamification_card.dart'; import 'widgets/transaction_preview_item.dart'; +import '../../widgets/ui/siro_ui.dart'; class WalletCaptainRefactored extends StatefulWidget { const WalletCaptainRefactored({super.key}); @@ -49,34 +49,20 @@ class _WalletCaptainRefactoredState extends State { @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: FinanceDesignSystem.backgroundColor, - appBar: AppBar( - title: Text('Driver Balance'.tr, - style: TextStyle( - fontWeight: FontWeight.bold, - color: FinanceDesignSystem.primaryDark)), - backgroundColor: Colors.transparent, - elevation: 0, - centerTitle: true, - leading: IconButton( - icon: Icon(Icons.arrow_back_ios_new_rounded, - color: FinanceDesignSystem.primaryDark, size: 20), - onPressed: () => Get.back(), + // شريط العنوان من الثيم — لا ألوان مبنيّة يدوياً هنا. + return SiroPage( + title: 'Driver Balance'.tr, + actions: [ + IconButton( + icon: const Icon(Icons.refresh_rounded), + onPressed: () => controller.refreshCaptainWallet(), + tooltip: 'Refresh'.tr, ), - actions: [ - IconButton( - icon: Icon(Icons.refresh_rounded, - color: FinanceDesignSystem.primaryDark), - onPressed: () => controller.refreshCaptainWallet(), - tooltip: 'Refresh'.tr, - ), - ], - ), + ], body: GetBuilder( builder: (controller) { if (controller.isLoading) { - return const Center(child: MyCircularProgressIndicator()); + return const SiroLoading(); } return SingleChildScrollView( padding: const EdgeInsets.symmetric( @@ -145,7 +131,7 @@ class _WalletCaptainRefactoredState extends State { Text('Recharge Balance'.tr, style: FinanceDesignSystem.headingStyle), Icon(Icons.info_outline_rounded, - size: 18, color: Colors.grey.shade400), + size: 18, color: FinanceDesignSystem.textMuted), ], ), const SizedBox(height: 12), @@ -233,7 +219,7 @@ class _WalletCaptainRefactoredState extends State { padding: const EdgeInsets.symmetric(vertical: 20), child: Center( child: Text("No transactions yet".tr, - style: TextStyle(color: Colors.grey.shade400))), + style: TextStyle(color: FinanceDesignSystem.textMuted))), ); } // Show only last 3 @@ -270,8 +256,9 @@ class _WalletCaptainRefactoredState extends State { Get.bottomSheet( Container( padding: const EdgeInsets.all(24), + // كان `Colors.white` ثابتاً ⇒ ورقة بيضاء بنص فاتح في الوضع الليلي. decoration: BoxDecoration( - color: Colors.white, + color: FinanceDesignSystem.cardColor, borderRadius: const BorderRadius.vertical(top: Radius.circular(24)), ), child: Column( diff --git a/siro_driver/lib/views/home/my_wallet/weekly_payment_page.dart b/siro_driver/lib/views/home/my_wallet/weekly_payment_page.dart index 9a7ec28c..e1282c48 100755 --- a/siro_driver/lib/views/home/my_wallet/weekly_payment_page.dart +++ b/siro_driver/lib/views/home/my_wallet/weekly_payment_page.dart @@ -3,9 +3,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:get/get.dart'; import 'package:siro_driver/constant/finance_design_system.dart'; -import 'package:siro_driver/views/widgets/mycircular.dart'; import '../../../controller/payment/driver_payment_controller.dart'; import 'widgets/transaction_preview_item.dart'; +import '../../widgets/ui/siro_ui.dart'; class WeeklyPaymentPage extends StatelessWidget { const WeeklyPaymentPage({super.key}); @@ -14,26 +14,12 @@ class WeeklyPaymentPage extends StatelessWidget { Widget build(BuildContext context) { Get.put(DriverWalletHistoryController()); - return Scaffold( - backgroundColor: FinanceDesignSystem.backgroundColor, - appBar: AppBar( - title: Text('Weekly Summary'.tr, - style: TextStyle( - fontWeight: FontWeight.bold, - color: FinanceDesignSystem.primaryDark)), - backgroundColor: Colors.transparent, - elevation: 0, - centerTitle: true, - leading: IconButton( - icon: Icon(Icons.arrow_back_ios_new_rounded, - color: FinanceDesignSystem.primaryDark, size: 20), - onPressed: () => Get.back(), - ), - ), + return SiroPage( + title: 'Weekly Summary'.tr, body: GetBuilder( builder: (controller) { if (controller.isLoading) { - return const Center(child: MyCircularProgressIndicator()); + return const SiroLoading(); } return Column( diff --git a/siro_driver/lib/views/home/statistics/earnings_page.dart b/siro_driver/lib/views/home/statistics/earnings_page.dart index ed57237b..905437b5 100644 --- a/siro_driver/lib/views/home/statistics/earnings_page.dart +++ b/siro_driver/lib/views/home/statistics/earnings_page.dart @@ -13,6 +13,7 @@ import 'package:siro_driver/controller/gamification/gamification_controller.dart import 'package:siro_driver/controller/home/statistics/earnings_controller.dart'; import 'package:siro_driver/controller/home/statistics/earnings_models.dart'; +import '../../widgets/ui/siro_ui.dart'; import 'widgets/behavior_and_achievements.dart'; import 'widgets/daily_goal_widget.dart'; import 'widgets/level_progress_widget.dart'; @@ -28,26 +29,14 @@ class EarningsPage extends StatelessWidget { @override Widget build(BuildContext context) { + // شريط العنوان يأتي من الثيم — كان يبني ألوانه يدوياً في كل صفحة، + // فيختلف ارتفاعه ووزن خطه من شاشة لأخرى. return Scaffold( - backgroundColor: FinanceDesignSystem.backgroundColor, - appBar: AppBar( - backgroundColor: Colors.transparent, - elevation: 0, - centerTitle: true, - title: Text('أرباحي', - style: TextStyle( - fontWeight: FontWeight.bold, - color: FinanceDesignSystem.primaryDark)), - leading: IconButton( - icon: Icon(Icons.arrow_back_ios_new_rounded, - color: FinanceDesignSystem.primaryDark, size: 20), - onPressed: () => Get.back(), - ), - ), + appBar: AppBar(title: Text('My Earnings'.tr)), body: GetBuilder( builder: (ctl) { if (ctl.isLoading && ctl.summary == null) { - return const Center(child: CircularProgressIndicator()); + return const SiroLoading(); } // ‏لا بيانات ولا كاش: نعرض الخطأ صريحاً مع زر إعادة. الشاشة diff --git a/siro_driver/lib/views/notification/notification_captain.dart b/siro_driver/lib/views/notification/notification_captain.dart index 3a25916a..f24c4556 100755 --- a/siro_driver/lib/views/notification/notification_captain.dart +++ b/siro_driver/lib/views/notification/notification_captain.dart @@ -1,84 +1,108 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:siro_driver/controller/notification/notification_captain_controller.dart'; -import 'package:flutter/cupertino.dart'; + +import '../widgets/ui/siro_ui.dart'; class NotificationCaptain extends StatelessWidget { - const NotificationCaptain({Key? key}) : super(key: key); + const NotificationCaptain({super.key}); @override Widget build(BuildContext context) { Get.put(NotificationCaptainController()); - return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - middle: Text('Notifications'.tr), - leading: CupertinoNavigationBarBackButton( - onPressed: () => Get.back(), - ), - ), - child: SafeArea( - child: GetBuilder( - builder: (notificationCaptainController) => - notificationCaptainController.isLoading - ? const Center(child: CupertinoActivityIndicator()) - : ListView.builder( - itemCount: notificationCaptainController - .notificationData['message'].length, - itemBuilder: (BuildContext context, int index) { - if (notificationCaptainController - .notificationData['message'] == - "No notification data found") { - _showCupertinoDialog(context, 'No Notifications', - 'There are no notifications at this time.'); - return const SizedBox.shrink(); - } - var res = notificationCaptainController - .notificationData['message'][index]; - return CupertinoListTile( - leading: const Icon(CupertinoIcons.bell_fill), - title: Text( - res['title'], - style: - CupertinoTheme.of(context).textTheme.textStyle, - ), - subtitle: Text( - res['body'], - style: CupertinoTheme.of(context) + return SiroPage( + title: 'Notifications'.tr, + body: GetBuilder( + builder: (controller) { + if (controller.isLoading) return const SiroLoading(); + + final data = controller.notificationData['message']; + + // الردّ قد يكون نصاً ("No notification data found") لا قائمة. + // كان الكود يقرأ `.length` عليه ثم يفتح حواراً من داخل `itemBuilder` + // — أي أثناء البناء، وهو ما يرمي استثناءً بدل عرض حالة فراغ. + final List items = data is List ? data : const []; + + if (items.isEmpty) { + return SiroEmptyState( + icon: Icons.notifications_off_rounded, + title: 'No Notifications'.tr, + message: 'There are no notifications at this time.'.tr, + ); + } + + return ListView.builder( + padding: SiroUi.pageInsets, + itemCount: items.length, + itemBuilder: (BuildContext context, int index) { + final res = items[index]; + return SiroCard( + onTap: () => _showDetails( + context, + res['title']?.toString() ?? '', + res['body']?.toString() ?? '', + () { + controller.updateNotification(res['id'].toString()); + Navigator.of(context).pop(); + }, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SiroIconBadge(Icons.notifications_rounded, + color: SiroUi.interactive(context)), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + res['title']?.toString() ?? '', + style: Theme.of(context) .textTheme - .tabLabelTextStyle, + .bodyMedium + ?.copyWith(fontWeight: FontWeight.bold), ), - onTap: () { - _showCupertinoDialog( - context, - res['title'], - res['body'], - onConfirm: () { - notificationCaptainController - .updateNotification(res['id'].toString()); - Navigator.of(context).pop(); - }, - ); - }, - ); - }, + const SizedBox(height: 4), + Text( + res['body']?.toString() ?? '', + maxLines: 3, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ), ), - ), + ], + ), + ); + }, + ); + }, ), ); } - void _showCupertinoDialog(BuildContext context, String title, String content, - {VoidCallback? onConfirm}) { - showCupertinoDialog( + void _showDetails(BuildContext context, String title, String content, + VoidCallback onConfirm) { + showDialog( context: context, - builder: (BuildContext context) => CupertinoAlertDialog( - title: Text(title), - content: Text(content), - actions: [ - CupertinoDialogAction( - child: const Text('OK'), - onPressed: onConfirm ?? () => Navigator.of(context).pop(), + builder: (BuildContext ctx) => AlertDialog( + icon: SiroIconBadge(Icons.notifications_active_rounded, + color: SiroUi.interactive(ctx), size: 22), + title: Text(title, textAlign: TextAlign.center), + content: Text(content, textAlign: TextAlign.center), + actionsAlignment: MainAxisAlignment.center, + actions: [ + TextButton( + onPressed: () => Navigator.of(ctx).pop(), + child: Text('Back'.tr, + style: TextStyle(color: SiroUi.subtle(ctx))), + ), + ElevatedButton( + onPressed: onConfirm, + child: Text('OK'.tr), ), ], ), diff --git a/siro_driver/lib/views/widgets/ui/siro_ui.dart b/siro_driver/lib/views/widgets/ui/siro_ui.dart new file mode 100644 index 00000000..4fea7c95 --- /dev/null +++ b/siro_driver/lib/views/widgets/ui/siro_ui.dart @@ -0,0 +1,585 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../constant/colors.dart'; + +/// عدّة الواجهة الموحّدة لتطبيق السائق. +/// +/// كل صفحات القائمة الجانبية تُبنى من هذه القطع، فالشكل واحد والثيم الليلي +/// والنهاري يتبعان `Theme.of(context)` بلا ألوان مثبّتة داخل الصفحات. +/// +/// **لا تضع منطقاً هنا** — القطع كلها عرض صرف بلا حالة ولا كنترولرات. +class SiroUi { + const SiroUi._(); + + // --- المسافات والانحناءات (مقياس واحد لكل التطبيق) --- + static const double gap = 12; + static const double pagePadding = 16; + static const double cardRadius = 18; + static const double controlRadius = 14; + + static const EdgeInsets pageInsets = + EdgeInsets.symmetric(horizontal: pagePadding, vertical: 12); + + /// لون العنصر التفاعلي حسب الوضع: النيفي يختفي على الخلفية الداكنة. + static Color interactive(BuildContext context) => + Theme.of(context).colorScheme.primary; + + static Color subtle(BuildContext context) => + Theme.of(context).textTheme.bodySmall?.color ?? AppColor.grayColor; + + static Color border(BuildContext context) => Theme.of(context).dividerColor; + + static Color surface(BuildContext context) => Theme.of(context).cardColor; + + /// تدرّج العلامة — يُستعمل في الرؤوس والبطاقات البارزة فقط، لا كخلفية صفحة. + static LinearGradient brandGradient(BuildContext context) { + final dark = Theme.of(context).brightness == Brightness.dark; + return LinearGradient( + colors: dark + ? const [Color(0xFF1E2745), Color(0xFF2E3A63)] + : const [AppColor.primaryColor, Color(0xFF3A4674)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ); + } +} + +/// الهيكل الموحّد لكل صفحة داخلية. +/// +/// يستبدل الخليط الحالي: `Scaffold` عادي و`CupertinoPageScaffold` و`SliverAppBar` +/// وخلفيات `Colors.white` المثبّتة (التي كانت تُنتج صفحة بيضاء بنص أبيض في +/// الوضع الليلي — كما في سجل الرحلات). +class SiroPage extends StatelessWidget { + final String title; + final Widget body; + final List? actions; + final Widget? floatingActionButton; + final Widget? bottomBar; + final PreferredSizeWidget? appBarBottom; + final bool showBack; + final Future Function()? onRefresh; + final EdgeInsetsGeometry? padding; + + const SiroPage({ + super.key, + required this.title, + required this.body, + this.actions, + this.floatingActionButton, + this.bottomBar, + this.appBarBottom, + this.showBack = true, + this.onRefresh, + this.padding, + }); + + @override + Widget build(BuildContext context) { + Widget content = padding == null + ? body + : Padding(padding: padding!, child: body); + + if (onRefresh != null) { + content = RefreshIndicator( + onRefresh: onRefresh!, + color: SiroUi.interactive(context), + child: content, + ); + } + + return Scaffold( + appBar: AppBar( + title: Text(title, overflow: TextOverflow.ellipsis), + automaticallyImplyLeading: showBack, + actions: actions, + bottom: appBarBottom, + ), + body: SafeArea(child: content), + floatingActionButton: floatingActionButton, + bottomNavigationBar: bottomBar == null + ? null + : SafeArea( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 12), + child: bottomBar, + ), + ), + ); + } +} + +/// البطاقة الموحّدة: سطح + حدّ رفيع + انحناء واحد. بلا ظلال ثقيلة. +class SiroCard extends StatelessWidget { + final Widget child; + final EdgeInsetsGeometry padding; + final EdgeInsetsGeometry? margin; + final VoidCallback? onTap; + final Color? accent; + + const SiroCard({ + super.key, + required this.child, + this.padding = const EdgeInsets.all(16), + this.margin, + this.onTap, + this.accent, + }); + + @override + Widget build(BuildContext context) { + final radius = BorderRadius.circular(SiroUi.cardRadius); + return Padding( + padding: margin ?? const EdgeInsets.only(bottom: SiroUi.gap), + child: Material( + color: SiroUi.surface(context), + borderRadius: radius, + clipBehavior: Clip.antiAlias, + child: InkWell( + onTap: onTap, + child: Container( + padding: padding, + decoration: BoxDecoration( + borderRadius: radius, + border: Border.all( + color: accent?.withValues(alpha: 0.35) ?? SiroUi.border(context), + ), + ), + child: child, + ), + ), + ), + ); + } +} + +/// عنوان قسم داخل الصفحة، بخط جانبي بلون العلامة. +class SiroSectionTitle extends StatelessWidget { + final String title; + final Widget? trailing; + + const SiroSectionTitle(this.title, {super.key, this.trailing}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 8, bottom: 10), + child: Row( + children: [ + Container( + width: 3, + height: 16, + decoration: BoxDecoration( + color: SiroUi.interactive(context), + borderRadius: BorderRadius.circular(2), + ), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + title, + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith(fontWeight: FontWeight.bold), + ), + ), + if (trailing != null) trailing!, + ], + ), + ); + } +} + +/// أيقونة داخل دائرة ملوّنة خفيفة — نفس نمط أيقونات القائمة الجانبية. +class SiroIconBadge extends StatelessWidget { + final IconData icon; + final Color color; + final double size; + + const SiroIconBadge(this.icon, + {super.key, required this.color, this.size = 20}); + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.all(size * 0.45), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.12), + shape: BoxShape.circle, + ), + child: Icon(icon, color: color, size: size), + ); + } +} + +/// شارة نصية صغيرة (حالة رحلة، نوع عرض، ...). +class SiroTag extends StatelessWidget { + final String label; + final Color color; + final IconData? icon; + + const SiroTag(this.label, {super.key, required this.color, this.icon}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.12), + borderRadius: BorderRadius.circular(999), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (icon != null) ...[ + Icon(icon, size: 13, color: color), + const SizedBox(width: 4), + ], + Text( + label, + style: TextStyle( + color: color, fontSize: 12, fontWeight: FontWeight.bold), + ), + ], + ), + ); + } +} + +/// خانة إحصاء (قيمة كبيرة + تسمية) — تُستعمل في الأرباح والمحفظة والملف. +class SiroStat extends StatelessWidget { + final String value; + final String label; + final IconData? icon; + final Color? color; + + const SiroStat({ + super.key, + required this.value, + required this.label, + this.icon, + this.color, + }); + + @override + Widget build(BuildContext context) { + final c = color ?? SiroUi.interactive(context); + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (icon != null) ...[ + SiroIconBadge(icon!, color: c, size: 18), + const SizedBox(height: 8), + ], + FittedBox( + fit: BoxFit.scaleDown, + child: Text( + value, + style: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + color: c, + ), + ), + ), + const SizedBox(height: 2), + Text( + label, + textAlign: TextAlign.center, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ); + } +} + +/// صفّ إحصاءات متساوية داخل بطاقة واحدة، بفواصل رأسية. +class SiroStatRow extends StatelessWidget { + final List stats; + + const SiroStatRow(this.stats, {super.key}); + + @override + Widget build(BuildContext context) { + final children = []; + for (var i = 0; i < stats.length; i++) { + if (i > 0) { + children.add(Container( + width: 1, + height: 40, + color: SiroUi.border(context), + )); + } + children.add(Expanded(child: stats[i])); + } + return SiroCard( + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: children, + ), + ); + } +} + +/// حالة الفراغ. **منفصلة عمداً عن [SiroErrorState]**: عرض «لا يوجد» عند فشل +/// الشبكة يُضلّل الكابتن ويجعله يظن أن لا عمل لديه. +class SiroEmptyState extends StatelessWidget { + final IconData icon; + final String title; + final String? message; + final String? actionLabel; + final VoidCallback? onAction; + + const SiroEmptyState({ + super.key, + required this.icon, + required this.title, + this.message, + this.actionLabel, + this.onAction, + }); + + @override + Widget build(BuildContext context) { + return Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(32), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.all(22), + decoration: BoxDecoration( + color: SiroUi.interactive(context).withValues(alpha: 0.08), + shape: BoxShape.circle, + ), + child: Icon(icon, size: 40, color: SiroUi.interactive(context)), + ), + const SizedBox(height: 18), + Text( + title, + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith(fontWeight: FontWeight.bold), + ), + if (message != null) ...[ + const SizedBox(height: 8), + Text( + message!, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + if (onAction != null && actionLabel != null) ...[ + const SizedBox(height: 20), + OutlinedButton(onPressed: onAction, child: Text(actionLabel!)), + ], + ], + ), + ), + ); + } +} + +/// حالة الخطأ — بلون تحذيري وبزر إعادة محاولة صريح. +class SiroErrorState extends StatelessWidget { + final String message; + final VoidCallback? onRetry; + + const SiroErrorState({super.key, required this.message, this.onRetry}); + + @override + Widget build(BuildContext context) { + return Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(32), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.all(22), + decoration: BoxDecoration( + color: AppColor.redColor.withValues(alpha: 0.10), + shape: BoxShape.circle, + ), + child: const Icon(Icons.cloud_off_rounded, + size: 40, color: AppColor.redColor), + ), + const SizedBox(height: 18), + Text( + message, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyMedium, + ), + if (onRetry != null) ...[ + const SizedBox(height: 20), + OutlinedButton.icon( + onPressed: onRetry, + icon: const Icon(Icons.refresh_rounded, size: 18), + label: Text('Retry'.tr), + ), + ], + ], + ), + ), + ); + } +} + +/// مؤشّر التحميل الموحّد. +class SiroLoading extends StatelessWidget { + final String? label; + const SiroLoading({super.key, this.label}); + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CircularProgressIndicator( + strokeWidth: 2.6, + color: SiroUi.interactive(context), + ), + if (label != null) ...[ + const SizedBox(height: 14), + Text(label!, style: Theme.of(context).textTheme.bodySmall), + ], + ], + ), + ); + } +} + +/// صفّ «تسمية ← قيمة» داخل بطاقة تفاصيل. +class SiroDetailRow extends StatelessWidget { + final String label; + final String value; + final IconData? icon; + final Color? valueColor; + + const SiroDetailRow({ + super.key, + required this.label, + required this.value, + this.icon, + this.valueColor, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 7), + child: Row( + children: [ + if (icon != null) ...[ + Icon(icon, size: 17, color: SiroUi.subtle(context)), + const SizedBox(width: 8), + ], + Expanded( + child: Text(label, style: Theme.of(context).textTheme.bodySmall), + ), + const SizedBox(width: 12), + Flexible( + child: Text( + value, + textAlign: TextAlign.end, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w600, + color: valueColor, + ), + ), + ), + ], + ), + ); + } +} + +/// رأس بارز بتدرّج العلامة — بديل الـ `SliverAppBar` الملوّن يدوياً في كل صفحة. +class SiroHeroHeader extends StatelessWidget { + final String label; + final String value; + final String? caption; + final IconData? icon; + final Widget? trailing; + + const SiroHeroHeader({ + super.key, + required this.label, + required this.value, + this.caption, + this.icon, + this.trailing, + }); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + margin: const EdgeInsets.only(bottom: SiroUi.gap), + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + gradient: SiroUi.brandGradient(context), + borderRadius: BorderRadius.circular(SiroUi.cardRadius), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + if (icon != null) ...[ + Icon(icon, color: Colors.white70, size: 16), + const SizedBox(width: 6), + ], + Flexible( + child: Text( + label, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: Colors.white70, + fontSize: 13, + fontWeight: FontWeight.w500), + ), + ), + ], + ), + const SizedBox(height: 8), + FittedBox( + fit: BoxFit.scaleDown, + alignment: AlignmentDirectional.centerStart, + child: Text( + value, + style: const TextStyle( + color: Colors.white, + fontSize: 28, + fontWeight: FontWeight.bold), + ), + ), + if (caption != null) ...[ + const SizedBox(height: 4), + Text( + caption!, + style: const TextStyle(color: Colors.white60, fontSize: 12), + ), + ], + ], + ), + ), + if (trailing != null) trailing!, + ], + ), + ); + } +}