Update: 2026-08-08 16:55:39

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