feat: add Jordan 3D topographic platform, Siro Map cinematic orbit studio, sun-shadow simulation, and sovereign intelligence security gating

This commit is contained in:
Hamza-Ayed
2026-09-19 12:34:26 +03:00
parent 91e9e71ce4
commit 0bf13824c9
180 changed files with 245224 additions and 704 deletions
@@ -56,12 +56,12 @@ class AboutAwardsSheet extends StatelessWidget {
shape: BoxShape.circle,
color: AppColors.pureWhite,
border: Border.all(
color: AppColors.urukGold.withValues(alpha: 0.45),
color: AppColors.appleBlue.withValues(alpha: 0.35),
width: 2,
),
boxShadow: [
BoxShadow(
color: AppColors.urukGold.withValues(alpha: 0.2),
color: AppColors.appleBlue.withValues(alpha: 0.15),
blurRadius: 26,
offset: const Offset(0, 8),
),
@@ -70,14 +70,14 @@ class AboutAwardsSheet extends StatelessWidget {
padding: const EdgeInsets.all(6),
child: ClipOval(
child: Image.asset(
'assets/images/uruk_prize_logo.png',
'assets/images/siro_maps_logo.png',
fit: BoxFit.contain,
),
),
),
const SizedBox(height: 14),
Text(
'جائزة أوروك الدولية',
'خرائط سيرو',
style: GoogleFonts.alexandria(
fontSize: 22,
fontWeight: FontWeight.w800,
@@ -86,11 +86,11 @@ class AboutAwardsSheet extends StatelessWidget {
),
const SizedBox(height: 2),
Text(
'URUK INTERNATIONAL PRIZE',
'SIRO MAP',
style: GoogleFonts.alexandria(
fontSize: 12,
fontWeight: FontWeight.w700,
color: AppColors.urukGoldDark,
color: AppColors.appleBlue,
letterSpacing: 1.5,
),
),
@@ -98,25 +98,25 @@ class AboutAwardsSheet extends StatelessWidget {
Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6),
decoration: BoxDecoration(
color: AppColors.urukGoldLight,
color: AppColors.surfaceMuted,
borderRadius: BorderRadius.circular(20),
border: Border.all(color: AppColors.urukGoldBorder),
border: Border.all(color: AppColors.borderSubtle),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.workspace_premium_rounded,
Icons.verified_rounded,
size: 16,
color: AppColors.urukGoldDark,
color: AppColors.appleBlue,
),
const SizedBox(width: 6),
Text(
'رعاية ودعم الابتكار والخدمات التقنية',
'منظومة الملاحة والخرائط الذكية المستقلة',
style: GoogleFonts.alexandria(
fontSize: 11.5,
fontWeight: FontWeight.w700,
color: AppColors.urukGoldDark,
color: AppColors.textPrimary,
),
),
],
@@ -128,16 +128,16 @@ class AboutAwardsSheet extends StatelessWidget {
const SizedBox(height: 22),
// ── 2. URUK PRIZE MISSION & ROLE ──
// ── 2. SIRO MAP MISSION & VISION ──
_buildSectionCard(
icon: Icons.lightbulb_rounded,
iconColor: AppColors.urukGoldDark,
title: 'دور جائزة أوروك ورسالتها التقنية',
icon: Icons.explore_rounded,
iconColor: AppColors.appleBlue,
title: 'عن سيرو ماب والرؤية التقنية',
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'تستلهم جائزة أوروك الدولية رمزيتها من حضارة أوروك العريقة — مهد التنظيم المدني والقياسات الأولى في تاريخ الإنسانية — لتبني وتدعم المشاريع والخدمات التقنية المبتكرة في العالم العربي والشرق الأوسط.',
'تعتمد خرائط سيرو (Siro Map) على منظومة خرائط وملاحة فيكتورية متقدمة ومستضافة ذاتياً، لتقديم حلول مكانية متطورة تخدم التنقل الذكي وسلاسل الإمداد في العالم العربي ومنطقة الشرق الأوسط وشمال أفريقيا.',
style: GoogleFonts.alexandria(
fontSize: 12.5,
height: 1.65,
@@ -147,7 +147,7 @@ class AboutAwardsSheet extends StatelessWidget {
),
const SizedBox(height: 10),
Text(
'تعمل الجائزة وبرامجها على تشجيع التوسع في البنى التحتية الرقمية، وتمكين الحلول الذكية المستقلة التي تسد الفجوات التقنية وتخدم المجتمعات وقطاعات النقل والحركة بكفاءة عالية.',
'تهدف المنظومة إلى تمكين الاستقلالية الرقمية الجغرافية الكاملة، وتقديم خرائط عالية السرعة والدقة مع خفض جذري في تكاليف واستهلاك البيانات مقارنة بالخدمات العالمية التقليدية.',
style: GoogleFonts.alexandria(
fontSize: 12.5,
height: 1.65,
@@ -191,7 +191,7 @@ class AboutAwardsSheet extends StatelessWidget {
const Divider(height: 18, color: AppColors.borderSubtle),
_buildTechRow(
icon: Icons.public_rounded,
iconColor: AppColors.urukGoldDark,
iconColor: AppColors.appleBlue,
title: 'تغطية الأسواق الإقليمية الحيوية',
desc: 'تركيز استراتيجي على توفير بيانات طرق تفصيلية ومحدثة للأسواق التي تعاني من نقص أو قيود في الخرائط العالمية (العراق، السودان، اليمن، سوريا، والمنطقة).',
),
@@ -55,7 +55,7 @@ class _AddPlaceSheetState extends State<AddPlaceSheet> {
),
const SizedBox(height: 18),
const Text(
'إضافة مكان جديد إلى خرائط أوروك',
'إضافة مكان جديد إلى خرائط سيرو',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700),
),
@@ -0,0 +1,756 @@
import 'dart:async';
import 'package:flutter/material.dart';
import '../../../../core/constants/app_colors.dart';
import '../../../../logic/cubits/navigation/navigation_cubit.dart';
import '../../../../logic/cubits/navigation/navigation_state.dart';
class Cinematic3DStudioSheet extends StatefulWidget {
final NavigationCubit cubit;
final NavigationState state;
const Cinematic3DStudioSheet({
super.key,
required this.cubit,
required this.state,
});
@override
State<Cinematic3DStudioSheet> createState() => _Cinematic3DStudioSheetState();
}
class _Cinematic3DStudioSheetState extends State<Cinematic3DStudioSheet> {
// Camera Tilt State
double _tilt = 55.0;
// Orbit State
bool _isOrbiting = false;
double _orbitSpeed = 1.0;
Timer? _orbitTimer;
// Sun & Shadow Simulation State
// Range: 330 minutes (05:30 AM) to 1260 minutes (09:00 PM)
int _sunTimeMinutes = 1110; // Default: 18:30 (Golden Hour Sunset)
bool _isSunPlaying = false;
Timer? _sunTimer;
// Layer Toggles
bool _showBuildings = true;
bool _showContours = true;
bool _showHillshading = true;
@override
void initState() {
super.initState();
// Get initial camera tilt if available
final initialPos = widget.cubit.mapController?.cameraPosition;
if (initialPos != null && initialPos.tilt > 0) {
_tilt = initialPos.tilt;
} else {
// Set to 55 degrees for instant 3D perspective
widget.cubit.mapController?.setTilt(55.0);
}
// Apply initial golden hour lighting
WidgetsBinding.instance.addPostFrameCallback((_) {
_applySunLighting(_sunTimeMinutes);
});
}
@override
void dispose() {
_orbitTimer?.cancel();
_sunTimer?.cancel();
super.dispose();
}
// ── Orbit Logic ──
void _toggleOrbit() {
setState(() {
_isOrbiting = !_isOrbiting;
});
_orbitTimer?.cancel();
if (_isOrbiting) {
_orbitTimer = Timer.periodic(const Duration(milliseconds: 60), (_) {
if (!mounted || !_isOrbiting) return;
widget.cubit.mapController?.orbitStep(
_orbitSpeed,
duration: const Duration(milliseconds: 60),
);
});
}
}
// ── Sun & Shadows Simulation Logic ──
void _toggleSunAutoPlay() {
setState(() {
_isSunPlaying = !_isSunPlaying;
});
_sunTimer?.cancel();
if (_isSunPlaying) {
_sunTimer = Timer.periodic(const Duration(milliseconds: 130), (_) {
if (!mounted || !_isSunPlaying) return;
setState(() {
_sunTimeMinutes += 12;
if (_sunTimeMinutes > 1260) {
_sunTimeMinutes = 330; // Loop back to 05:30 AM
}
});
_applySunLighting(_sunTimeMinutes);
});
}
}
void _onSunSliderChanged(double value) {
setState(() {
_sunTimeMinutes = value.toInt();
});
_applySunLighting(_sunTimeMinutes);
}
void _setPresetTime(int minutes) {
setState(() {
_sunTimeMinutes = minutes;
});
_applySunLighting(minutes);
}
void _applySunLighting(int minutes) {
// Fraction of daylight cycle: 0.0 (05:30) to 1.0 (21:00)
final double fraction = ((minutes - 330) / (1260 - 330)).clamp(0.0, 1.0);
// Azimuth sweeps from 75° (East - Sunrise) to 180° (South - Noon) to 290° (West - Sunset)
final double sunAzimuth = 75.0 + (fraction * 215.0);
String shadowColor;
String highlightColor;
String buildingColor;
double opacity = 0.88;
if (minutes < 420) {
// Dawn (05:30 - 07:00)
shadowColor = '#4c1d95'; // Deep violet dawn shadow
highlightColor = '#fed7aa'; // Peach highlight
buildingColor = '#fbcfe8'; // Morning rose tint
opacity = 0.85;
} else if (minutes < 660) {
// Morning (07:00 - 11:00)
shadowColor = '#334155';
highlightColor = '#ffffff';
buildingColor = '#fef08a'; // Golden morning sun
opacity = 0.88;
} else if (minutes < 840) {
// Noon (11:00 - 14:00)
shadowColor = '#1e293b'; // Crisp direct shadow
highlightColor = '#ffffff';
buildingColor = '#f8fafc'; // Crisp bright daylight
opacity = 0.90;
} else if (minutes < 1050) {
// Afternoon (14:00 - 17:30)
shadowColor = '#374151';
highlightColor = '#ffedd5';
buildingColor = '#fed7aa'; // Warm afternoon sun
opacity = 0.88;
} else if (minutes < 1170) {
// Sunset / Golden Hour (17:30 - 19:30)
shadowColor = '#3b0764'; // Dramatic deep purple-amber shadow
highlightColor = '#ffedd5';
buildingColor = '#ea580c'; // Glowing warm sunset orange
opacity = 0.92;
} else {
// Night / Twilight (19:30 - 21:00)
shadowColor = '#020617';
highlightColor = '#60a5fa'; // Cool blue moonlight
buildingColor = '#1e293b'; // Dark sapphire buildings
opacity = 0.80;
}
widget.cubit.mapController?.updateSunSimulation(
sunAzimuth: sunAzimuth,
shadowColorHex: shadowColor,
highlightColorHex: highlightColor,
buildingColorHex: buildingColor,
buildingOpacity: opacity,
);
}
String _formatSunTime(int minutes) {
final int hours = minutes ~/ 60;
final int mins = minutes % 60;
final String minsStr = mins.toString().padLeft(2, '0');
final String period = hours >= 12 ? 'م' : 'ص';
final int displayHour = hours > 12 ? hours - 12 : (hours == 0 ? 12 : hours);
String label = '';
if (minutes < 420) {
label = 'الفجر والشروق 🌅';
} else if (minutes < 660) {
label = 'الصباح الباكر ☀️';
} else if (minutes < 840) {
label = 'الظهيرة الساطعة 🌤️';
} else if (minutes < 1050) {
label = 'العصر ⛅';
} else if (minutes < 1170) {
label = 'الغروب الذهبي 🌇';
} else {
label = 'الليل التكتيكي 🌙';
}
return '$displayHour:$minsStr $period • $label';
}
// ── Tilt Logic ──
void _setTilt(double newTilt) {
setState(() => _tilt = newTilt);
widget.cubit.mapController?.setTilt(newTilt);
}
// ── Layer Toggles ──
void _toggleBuildings() {
setState(() => _showBuildings = !_showBuildings);
widget.cubit.mapController?.setLayerVisibility('building-3d', _showBuildings);
widget.cubit.mapController?.setLayerVisibility('building-3d-osm', _showBuildings);
}
void _toggleContours() {
setState(() => _showContours = !_showContours);
widget.cubit.mapController?.setLayerVisibility('tactical-contour-minor', _showContours);
widget.cubit.mapController?.setLayerVisibility('tactical-contour-major', _showContours);
widget.cubit.mapController?.setLayerVisibility('opentopo-contour-lines', _showContours);
}
void _toggleHillshading() {
setState(() => _showHillshading = !_showHillshading);
widget.cubit.mapController?.setLayerVisibility('hillshading', _showHillshading);
}
@override
Widget build(BuildContext context) {
final isSatellite = widget.state.mapTheme == MapThemeType.satellite;
final double fraction = ((_sunTimeMinutes - 330) / (1260 - 330)).clamp(0.0, 1.0);
final double sunAzimuth = 75.0 + (fraction * 215.0);
return Container(
padding: const EdgeInsets.fromLTRB(20, 16, 20, 36),
decoration: const BoxDecoration(
color: AppColors.pureWhite,
borderRadius: BorderRadius.vertical(top: Radius.circular(28)),
boxShadow: [
BoxShadow(
color: Color(0x28000000),
blurRadius: 28,
offset: Offset(0, -8),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Drag Handle
Center(
child: Container(
width: 42,
height: 4.5,
decoration: BoxDecoration(
color: AppColors.borderGlass,
borderRadius: BorderRadius.circular(3),
),
),
),
const SizedBox(height: 14),
// Header
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 38,
height: 38,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFFEA580C), Color(0xFFF59E0B)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(11),
boxShadow: [
BoxShadow(
color: const Color(0xFFEA580C).withValues(alpha: 0.35),
blurRadius: 10,
),
],
),
child: const Icon(Icons.view_in_ar_rounded, color: Colors.white, size: 22),
),
const SizedBox(width: 12),
const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'استوديو التضاريس والسينما 3D',
style: TextStyle(
fontSize: 15.5,
fontWeight: FontWeight.w800,
color: AppColors.textPrimary,
),
),
Text(
'دوران سينمائي • محاكاة حركة الشمس والظلال',
style: TextStyle(fontSize: 11, color: AppColors.textSecondary),
),
],
),
],
),
IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: const Icon(Icons.close_rounded, color: AppColors.textMuted),
),
],
),
const SizedBox(height: 18),
const Divider(height: 1, color: AppColors.borderSubtle),
const SizedBox(height: 16),
// ── 1. DAY-TO-NIGHT SUN & SHADOWS SIMULATION (Stationary Location) ──
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(18),
border: Border.all(color: const Color(0xFF334155)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: const Color(0xFFEA580C).withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(8),
),
child: const Icon(Icons.wb_sunny_rounded, color: Color(0xFFFB923C), size: 16),
),
const SizedBox(width: 8),
const Text(
'محاكاة حركة الشمس وسقوط الظلال',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
],
),
// Auto-Play Toggle Button
InkWell(
onTap: _toggleSunAutoPlay,
borderRadius: BorderRadius.circular(8),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: _isSunPlaying
? const Color(0xFFEA580C)
: Colors.white.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: _isSunPlaying
? const Color(0xFFEA580C)
: Colors.white.withValues(alpha: 0.2),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
_isSunPlaying ? Icons.pause_rounded : Icons.play_arrow_rounded,
size: 14,
color: Colors.white,
),
const SizedBox(width: 4),
Text(
_isSunPlaying ? 'إيقاف الدورة' : 'دورة تلقائية',
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
],
),
),
),
],
),
const SizedBox(height: 6),
const Text(
'المكان ثابت: وحركة الضوء تحاكي زاوية الشمس لرؤية تشكل وظلال المباني والجبال',
style: TextStyle(fontSize: 10.5, color: Color(0xFF94A3B8)),
),
const SizedBox(height: 12),
// Current Time Display & Azimuth
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
_formatSunTime(_sunTimeMinutes),
style: const TextStyle(
fontSize: 12.5,
fontWeight: FontWeight.w800,
color: Color(0xFFFDBA74),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(6),
),
child: Text(
'زاوية الإسقاط: ${sunAzimuth.toStringAsFixed(0)}°',
style: const TextStyle(fontSize: 10, color: Color(0xFFCBD5E1)),
),
),
],
),
const SizedBox(height: 6),
// Slider
SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: const Color(0xFFEA580C),
inactiveTrackColor: Colors.white.withValues(alpha: 0.15),
thumbColor: const Color(0xFFFB923C),
overlayColor: const Color(0xFFEA580C).withValues(alpha: 0.2),
trackHeight: 4,
),
child: Slider(
min: 330.0,
max: 1260.0,
value: _sunTimeMinutes.toDouble(),
onChanged: _onSunSliderChanged,
),
),
// Quick Sun Period Buttons
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildSunPeriodChip('06:00 ص', 'الفجر', 360),
_buildSunPeriodChip('09:00 ص', 'الصباح', 540),
_buildSunPeriodChip('12:00 م', 'الظهيرة', 720),
_buildSunPeriodChip('06:30 م', 'الغروب', 1110),
_buildSunPeriodChip('08:30 م', 'الليل', 1230),
],
),
],
),
),
const SizedBox(height: 16),
// ── 2. CINEMATIC 360° ORBIT & CAMERA TILT ──
Row(
children: [
// Orbit Button
Expanded(
child: InkWell(
onTap: _toggleOrbit,
borderRadius: BorderRadius.circular(16),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 13, horizontal: 12),
decoration: BoxDecoration(
color: _isOrbiting
? AppColors.appleBlue.withValues(alpha: 0.1)
: AppColors.surfaceMuted,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: _isOrbiting ? AppColors.appleBlue : AppColors.borderSubtle,
width: _isOrbiting ? 2 : 1,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.rotate_right_rounded,
color: _isOrbiting ? AppColors.appleBlue : AppColors.textPrimary,
size: 20,
),
const SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_isOrbiting ? 'الدوران نشط 360°' : 'دوران سينمائي',
style: TextStyle(
fontSize: 12.5,
fontWeight: FontWeight.w700,
color: _isOrbiting ? AppColors.appleBlue : AppColors.textPrimary,
),
),
Text(
_isOrbiting ? 'انقر للإيقاف' : 'التفاف حول الموقع',
style: const TextStyle(fontSize: 10, color: AppColors.textMuted),
),
],
),
],
),
),
),
),
const SizedBox(width: 10),
// Orbit Speed Selector (When active)
if (_isOrbiting)
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(
color: AppColors.surfaceMuted,
borderRadius: BorderRadius.circular(14),
border: Border.all(color: AppColors.borderSubtle),
),
child: Row(
children: [
_buildSpeedChip(0.6, 'بطيء'),
_buildSpeedChip(1.2, 'سلس'),
_buildSpeedChip(2.4, 'سريع'),
],
),
),
],
),
const SizedBox(height: 14),
// ── 3. CAMERA TILT CONTROLS ──
Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
decoration: BoxDecoration(
color: AppColors.surfaceMuted,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColors.borderSubtle),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'إمالة الكاميرا للأفق (Camera Tilt):',
style: TextStyle(
fontSize: 12.5,
fontWeight: FontWeight.w700,
color: AppColors.textPrimary,
),
),
Text(
'${_tilt.toStringAsFixed(0)}°',
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w800,
color: AppColors.appleBlue,
),
),
],
),
const SizedBox(height: 8),
Row(
children: [
_buildTiltPresetChip(0.0, '2D مسطح (0°)'),
const SizedBox(width: 8),
_buildTiltPresetChip(45.0, '3D كتل (45°)'),
const SizedBox(width: 8),
_buildTiltPresetChip(60.0, 'أفق سينمائي (60°)'),
],
),
],
),
),
const SizedBox(height: 14),
// ── 4. 3D LAYERS TOGGLES ──
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildFeatureToggle(
title: 'المباني 3D',
icon: Icons.apartment_rounded,
active: _showBuildings,
onTap: _toggleBuildings,
),
_buildFeatureToggle(
title: 'خطوط الكنتور',
icon: Icons.layers_outlined,
active: _showContours,
onTap: _toggleContours,
),
_buildFeatureToggle(
title: 'ظلال التضاريس',
icon: Icons.landscape_rounded,
active: _showHillshading,
onTap: _toggleHillshading,
),
_buildFeatureToggle(
title: 'أقمار صناعية',
icon: Icons.satellite_alt_rounded,
active: isSatellite,
onTap: () {
widget.cubit.setMapTheme(
isSatellite ? MapThemeType.vectorLight : MapThemeType.satellite,
);
},
),
],
),
],
),
);
}
Widget _buildSunPeriodChip(String time, String label, int minutes) {
final bool isSelected = (_sunTimeMinutes - minutes).abs() < 40;
return InkWell(
onTap: () => _setPresetTime(minutes),
borderRadius: BorderRadius.circular(6),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 3),
decoration: BoxDecoration(
color: isSelected
? const Color(0xFFEA580C)
: Colors.white.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(6),
),
child: Column(
children: [
Text(
label,
style: TextStyle(
fontSize: 9.5,
fontWeight: isSelected ? FontWeight.w800 : FontWeight.w500,
color: isSelected ? Colors.white : const Color(0xFFCBD5E1),
),
),
Text(
time,
style: TextStyle(
fontSize: 8.5,
color: isSelected ? Colors.white.withValues(alpha: 0.9) : const Color(0xFF64748B),
),
),
],
),
),
);
}
Widget _buildSpeedChip(double speed, String label) {
final bool isSelected = (_orbitSpeed - speed).abs() < 0.1;
return InkWell(
onTap: () => setState(() => _orbitSpeed = speed),
borderRadius: BorderRadius.circular(8),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 5),
decoration: BoxDecoration(
color: isSelected ? AppColors.appleBlue : Colors.transparent,
borderRadius: BorderRadius.circular(8),
),
child: Text(
label,
style: TextStyle(
fontSize: 10.5,
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w500,
color: isSelected ? Colors.white : AppColors.textSecondary,
),
),
),
);
}
Widget _buildTiltPresetChip(double tiltValue, String label) {
final bool isSelected = (_tilt - tiltValue).abs() < 4.0;
return Expanded(
child: InkWell(
onTap: () => _setTilt(tiltValue),
borderRadius: BorderRadius.circular(10),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
color: isSelected ? AppColors.appleBlue : Colors.white,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isSelected ? AppColors.appleBlue : AppColors.borderSubtle,
),
),
child: Text(
label,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 11,
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w600,
color: isSelected ? Colors.white : AppColors.textPrimary,
),
),
),
),
);
}
Widget _buildFeatureToggle({
required String title,
required IconData icon,
required bool active,
required VoidCallback onTap,
}) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Container(
width: 76,
padding: const EdgeInsets.symmetric(vertical: 9),
decoration: BoxDecoration(
color: active
? AppColors.appleBlue.withValues(alpha: 0.08)
: AppColors.surfaceMuted,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: active ? AppColors.appleBlue : AppColors.borderSubtle,
width: active ? 1.5 : 1,
),
),
child: Column(
children: [
Icon(
icon,
size: 20,
color: active ? AppColors.appleBlue : AppColors.textMuted,
),
const SizedBox(height: 5),
Text(
title,
style: TextStyle(
fontSize: 10,
fontWeight: active ? FontWeight.w700 : FontWeight.w500,
color: active ? AppColors.appleBlue : AppColors.textSecondary,
),
),
],
),
),
);
}
}
@@ -7,6 +7,7 @@ class LayerSelectorSheet extends StatelessWidget {
final ValueChanged<MapThemeType> onThemeChanged;
final VoidCallback? onOpenVehicleCustomizer;
final VoidCallback? onOpenAboutAwards;
final VoidCallback? onOpenCinematic3DStudio;
const LayerSelectorSheet({
super.key,
@@ -14,6 +15,7 @@ class LayerSelectorSheet extends StatelessWidget {
required this.onThemeChanged,
this.onOpenVehicleCustomizer,
this.onOpenAboutAwards,
this.onOpenCinematic3DStudio,
});
@override
@@ -68,6 +70,43 @@ class LayerSelectorSheet extends StatelessWidget {
),
],
),
if (onOpenCinematic3DStudio != null) ...[
const SizedBox(height: 16),
ListTile(
onTap: onOpenCinematic3DStudio,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: const BorderSide(color: Color(0x33EA580C)),
),
tileColor: const Color(0xFFEA580C).withValues(alpha: 0.08),
leading: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFFEA580C), Color(0xFFF59E0B)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(Icons.view_in_ar_rounded, color: Colors.white, size: 22),
),
title: const Text(
'استوديو التضاريس والسينما 3D',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
color: AppColors.textPrimary,
),
),
subtitle: const Text(
'دوران سينمائي 360° • محاكاة حركة الشمس وسقوط الظلال',
style: TextStyle(fontSize: 11, color: Color(0xFFEA580C), fontWeight: FontWeight.w600),
),
trailing: const Icon(Icons.arrow_forward_ios_rounded, size: 14, color: Color(0xFFEA580C)),
),
],
if (onOpenVehicleCustomizer != null) ...[
const SizedBox(height: 18),
const Divider(height: 1, color: AppColors.borderSubtle),
@@ -106,37 +145,37 @@ class LayerSelectorSheet extends StatelessWidget {
onTap: onOpenAboutAwards,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: const BorderSide(color: AppColors.urukGoldBorder),
side: const BorderSide(color: AppColors.borderSubtle),
),
tileColor: AppColors.urukGoldLight,
tileColor: AppColors.surfaceMuted,
leading: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: AppColors.pureWhite,
shape: BoxShape.circle,
border: Border.all(color: AppColors.urukGold.withValues(alpha: 0.4)),
border: Border.all(color: AppColors.borderSubtle),
),
child: ClipOval(
child: Image.asset(
'assets/images/uruk_prize_logo.png',
'assets/images/siro_maps_logo.png',
fit: BoxFit.contain,
),
),
),
title: const Text(
'عن منظومة أوروك والتقنيات المفعلة',
'عن منظومة سيرو ماب والتقنيات المفعلة',
style: TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w700,
color: AppColors.urukGoldDark,
color: AppColors.textPrimary,
),
),
subtitle: const Text(
'الكاش الذكي، مفاتيح الأجهزة، والملاحة دون اتصال',
style: TextStyle(fontSize: 11, color: AppColors.textSecondary),
),
trailing: const Icon(Icons.workspace_premium_rounded, size: 20, color: AppColors.urukGoldDark),
trailing: const Icon(Icons.info_outline_rounded, size: 20, color: AppColors.appleBlue),
),
],
],