Update: 2026-08-08 16:55:39
This commit is contained in:
@@ -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": "إعادة المحاولة",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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!,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user