import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../../constant/colors.dart'; /// عدّة الواجهة الموحّدة لتطبيق السائق. /// /// كل صفحات القائمة الجانبية تُبنى من هذه القطع، فالشكل واحد والثيم الليلي /// والنهاري يتبعان `Theme.of(context)` بلا ألوان مثبّتة داخل الصفحات. /// /// **لا تضع منطقاً هنا** — القطع كلها عرض صرف بلا حالة ولا كنترولرات. class SiroUi { const SiroUi._(); // --- المسافات والانحناءات (مقياس واحد لكل التطبيق) --- static const double gap = 12; static const double pagePadding = 16; static const double cardRadius = 18; static const double controlRadius = 14; static const EdgeInsets pageInsets = EdgeInsets.symmetric(horizontal: pagePadding, vertical: 12); /// لون العنصر التفاعلي حسب الوضع: النيفي يختفي على الخلفية الداكنة. static Color interactive(BuildContext context) => Theme.of(context).colorScheme.primary; static Color subtle(BuildContext context) => Theme.of(context).textTheme.bodySmall?.color ?? AppColor.grayColor; static Color border(BuildContext context) => Theme.of(context).dividerColor; static Color surface(BuildContext context) => Theme.of(context).cardColor; /// تدرّج العلامة — يُستعمل في الرؤوس والبطاقات البارزة فقط، لا كخلفية صفحة. static LinearGradient brandGradient(BuildContext context) { final dark = Theme.of(context).brightness == Brightness.dark; return LinearGradient( colors: dark ? const [Color(0xFF1E2745), Color(0xFF2E3A63)] : const [AppColor.primaryColor, Color(0xFF3A4674)], begin: Alignment.topLeft, end: Alignment.bottomRight, ); } } /// الهيكل الموحّد لكل صفحة داخلية. /// /// يستبدل الخليط الحالي: `Scaffold` عادي و`CupertinoPageScaffold` و`SliverAppBar` /// وخلفيات `Colors.white` المثبّتة (التي كانت تُنتج صفحة بيضاء بنص أبيض في /// الوضع الليلي — كما في سجل الرحلات). class SiroPage extends StatelessWidget { final String title; final Widget body; final List? actions; final Widget? floatingActionButton; final Widget? bottomBar; final PreferredSizeWidget? appBarBottom; final bool showBack; final Future Function()? onRefresh; final EdgeInsetsGeometry? padding; const SiroPage({ super.key, required this.title, required this.body, this.actions, this.floatingActionButton, this.bottomBar, this.appBarBottom, this.showBack = true, this.onRefresh, this.padding, }); @override Widget build(BuildContext context) { Widget content = padding == null ? body : Padding(padding: padding!, child: body); if (onRefresh != null) { content = RefreshIndicator( onRefresh: onRefresh!, color: SiroUi.interactive(context), child: content, ); } return Scaffold( appBar: AppBar( title: Text(title, overflow: TextOverflow.ellipsis), automaticallyImplyLeading: showBack, actions: actions, bottom: appBarBottom, ), body: SafeArea(child: content), floatingActionButton: floatingActionButton, bottomNavigationBar: bottomBar == null ? null : SafeArea( child: Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 12), child: bottomBar, ), ), ); } } /// البطاقة الموحّدة: سطح + حدّ رفيع + انحناء واحد. بلا ظلال ثقيلة. class SiroCard extends StatelessWidget { final Widget child; final EdgeInsetsGeometry padding; final EdgeInsetsGeometry? margin; final VoidCallback? onTap; final Color? accent; const SiroCard({ super.key, required this.child, this.padding = const EdgeInsets.all(16), this.margin, this.onTap, this.accent, }); @override Widget build(BuildContext context) { final radius = BorderRadius.circular(SiroUi.cardRadius); return Padding( padding: margin ?? const EdgeInsets.only(bottom: SiroUi.gap), child: Material( color: SiroUi.surface(context), borderRadius: radius, clipBehavior: Clip.antiAlias, child: InkWell( onTap: onTap, child: Container( padding: padding, decoration: BoxDecoration( borderRadius: radius, border: Border.all( color: accent?.withValues(alpha: 0.35) ?? SiroUi.border(context), ), ), child: child, ), ), ), ); } } /// عنوان قسم داخل الصفحة، بخط جانبي بلون العلامة. class SiroSectionTitle extends StatelessWidget { final String title; final Widget? trailing; const SiroSectionTitle(this.title, {super.key, this.trailing}); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(top: 8, bottom: 10), child: Row( children: [ Container( width: 3, height: 16, decoration: BoxDecoration( color: SiroUi.interactive(context), borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 8), Expanded( child: Text( title, style: Theme.of(context) .textTheme .titleMedium ?.copyWith(fontWeight: FontWeight.bold), ), ), if (trailing != null) trailing!, ], ), ); } } /// أيقونة داخل دائرة ملوّنة خفيفة — نفس نمط أيقونات القائمة الجانبية. class SiroIconBadge extends StatelessWidget { final IconData icon; final Color color; final double size; const SiroIconBadge(this.icon, {super.key, required this.color, this.size = 20}); @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.all(size * 0.45), decoration: BoxDecoration( color: color.withValues(alpha: 0.12), shape: BoxShape.circle, ), child: Icon(icon, color: color, size: size), ); } } /// شارة نصية صغيرة (حالة رحلة، نوع عرض، ...). class SiroTag extends StatelessWidget { final String label; final Color color; final IconData? icon; const SiroTag(this.label, {super.key, required this.color, this.icon}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), decoration: BoxDecoration( color: color.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(999), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ if (icon != null) ...[ Icon(icon, size: 13, color: color), const SizedBox(width: 4), ], Text( label, style: TextStyle( color: color, fontSize: 12, fontWeight: FontWeight.bold), ), ], ), ); } } /// خانة إحصاء (قيمة كبيرة + تسمية) — تُستعمل في الأرباح والمحفظة والملف. class SiroStat extends StatelessWidget { final String value; final String label; final IconData? icon; final Color? color; const SiroStat({ super.key, required this.value, required this.label, this.icon, this.color, }); @override Widget build(BuildContext context) { final c = color ?? SiroUi.interactive(context); return Column( mainAxisSize: MainAxisSize.min, children: [ if (icon != null) ...[ SiroIconBadge(icon!, color: c, size: 18), const SizedBox(height: 8), ], FittedBox( fit: BoxFit.scaleDown, child: Text( value, style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, color: c, ), ), ), const SizedBox(height: 2), Text( label, textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.bodySmall, ), ], ); } } /// صفّ إحصاءات متساوية داخل بطاقة واحدة، بفواصل رأسية. class SiroStatRow extends StatelessWidget { final List stats; const SiroStatRow(this.stats, {super.key}); @override Widget build(BuildContext context) { final children = []; for (var i = 0; i < stats.length; i++) { if (i > 0) { children.add(Container( width: 1, height: 40, color: SiroUi.border(context), )); } children.add(Expanded(child: stats[i])); } return SiroCard( padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: children, ), ); } } /// حالة الفراغ. **منفصلة عمداً عن [SiroErrorState]**: عرض «لا يوجد» عند فشل /// الشبكة يُضلّل الكابتن ويجعله يظن أن لا عمل لديه. class SiroEmptyState extends StatelessWidget { final IconData icon; final String title; final String? message; final String? actionLabel; final VoidCallback? onAction; const SiroEmptyState({ super.key, required this.icon, required this.title, this.message, this.actionLabel, this.onAction, }); @override Widget build(BuildContext context) { return Center( child: SingleChildScrollView( padding: const EdgeInsets.all(32), child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.all(22), decoration: BoxDecoration( color: SiroUi.interactive(context).withValues(alpha: 0.08), shape: BoxShape.circle, ), child: Icon(icon, size: 40, color: SiroUi.interactive(context)), ), const SizedBox(height: 18), Text( title, textAlign: TextAlign.center, style: Theme.of(context) .textTheme .titleMedium ?.copyWith(fontWeight: FontWeight.bold), ), if (message != null) ...[ const SizedBox(height: 8), Text( message!, textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodySmall, ), ], if (onAction != null && actionLabel != null) ...[ const SizedBox(height: 20), OutlinedButton(onPressed: onAction, child: Text(actionLabel!)), ], ], ), ), ); } } /// حالة الخطأ — بلون تحذيري وبزر إعادة محاولة صريح. class SiroErrorState extends StatelessWidget { final String message; final VoidCallback? onRetry; const SiroErrorState({super.key, required this.message, this.onRetry}); @override Widget build(BuildContext context) { return Center( child: SingleChildScrollView( padding: const EdgeInsets.all(32), child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.all(22), decoration: BoxDecoration( color: AppColor.redColor.withValues(alpha: 0.10), shape: BoxShape.circle, ), child: const Icon(Icons.cloud_off_rounded, size: 40, color: AppColor.redColor), ), const SizedBox(height: 18), Text( message, textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium, ), if (onRetry != null) ...[ const SizedBox(height: 20), OutlinedButton.icon( onPressed: onRetry, icon: const Icon(Icons.refresh_rounded, size: 18), label: Text('Retry'.tr), ), ], ], ), ), ); } } /// مؤشّر التحميل الموحّد. class SiroLoading extends StatelessWidget { final String? label; const SiroLoading({super.key, this.label}); @override Widget build(BuildContext context) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ CircularProgressIndicator( strokeWidth: 2.6, color: SiroUi.interactive(context), ), if (label != null) ...[ const SizedBox(height: 14), Text(label!, style: Theme.of(context).textTheme.bodySmall), ], ], ), ); } } /// صفّ «تسمية ← قيمة» داخل بطاقة تفاصيل. class SiroDetailRow extends StatelessWidget { final String label; final String value; final IconData? icon; final Color? valueColor; const SiroDetailRow({ super.key, required this.label, required this.value, this.icon, this.valueColor, }); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(vertical: 7), child: Row( children: [ if (icon != null) ...[ Icon(icon, size: 17, color: SiroUi.subtle(context)), const SizedBox(width: 8), ], Expanded( child: Text(label, style: Theme.of(context).textTheme.bodySmall), ), const SizedBox(width: 12), Flexible( child: Text( value, textAlign: TextAlign.end, style: Theme.of(context).textTheme.bodyMedium?.copyWith( fontWeight: FontWeight.w600, color: valueColor, ), ), ), ], ), ); } } /// رأس بارز بتدرّج العلامة — بديل الـ `SliverAppBar` الملوّن يدوياً في كل صفحة. class SiroHeroHeader extends StatelessWidget { final String label; final String value; final String? caption; final IconData? icon; final Widget? trailing; const SiroHeroHeader({ super.key, required this.label, required this.value, this.caption, this.icon, this.trailing, }); @override Widget build(BuildContext context) { return Container( width: double.infinity, margin: const EdgeInsets.only(bottom: SiroUi.gap), padding: const EdgeInsets.all(20), decoration: BoxDecoration( gradient: SiroUi.brandGradient(context), borderRadius: BorderRadius.circular(SiroUi.cardRadius), ), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Row( children: [ if (icon != null) ...[ Icon(icon, color: Colors.white70, size: 16), const SizedBox(width: 6), ], Flexible( child: Text( label, overflow: TextOverflow.ellipsis, style: const TextStyle( color: Colors.white70, fontSize: 13, fontWeight: FontWeight.w500), ), ), ], ), const SizedBox(height: 8), FittedBox( fit: BoxFit.scaleDown, alignment: AlignmentDirectional.centerStart, child: Text( value, style: const TextStyle( color: Colors.white, fontSize: 28, fontWeight: FontWeight.bold), ), ), if (caption != null) ...[ const SizedBox(height: 4), Text( caption!, style: const TextStyle(color: Colors.white60, fontSize: 12), ), ], ], ), ), if (trailing != null) trailing!, ], ), ); } }