MENA region data enrichment scripts` -> Wait, let's keep it concise and clean.
* *Option 3:* `feat: add tourist landmarks support with UI sheets, data models
This commit is contained in:
@@ -22,6 +22,11 @@ import 'widgets/add_place_sheet.dart';
|
||||
import 'widgets/vehicle_customizer_sheet.dart';
|
||||
import 'widgets/about_awards_sheet.dart';
|
||||
import 'widgets/cinematic_3d_studio_sheet.dart';
|
||||
import 'widgets/siro_drawer_widget.dart';
|
||||
import 'widgets/tourist_landmarks_sheet.dart';
|
||||
import 'widgets/settings_sheet.dart';
|
||||
import 'widgets/system_updates_sheet.dart';
|
||||
import '../common/siro_toast.dart';
|
||||
|
||||
class MapView extends StatefulWidget {
|
||||
const MapView({super.key});
|
||||
@@ -31,6 +36,7 @@ class MapView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _MapViewState extends State<MapView> {
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
final FocusNode _searchFocusNode = FocusNode();
|
||||
bool _isSearchFocused = false;
|
||||
@@ -117,6 +123,36 @@ class _MapViewState extends State<MapView> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showTouristLandmarks(BuildContext context, NavigationCubit cubit, NavigationState state) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (_) => TouristLandmarksSheet(
|
||||
cubit: cubit,
|
||||
state: state,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showSettingsSheet(BuildContext context, NavigationCubit cubit) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (_) => SettingsSheet(cubit: cubit),
|
||||
);
|
||||
}
|
||||
|
||||
void _showSystemUpdatesSheet(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (_) => const SystemUpdatesSheet(),
|
||||
);
|
||||
}
|
||||
|
||||
void _showCinematic3DStudio(BuildContext context, NavigationCubit cubit, NavigationState state) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
@@ -170,14 +206,10 @@ class _MapViewState extends State<MapView> {
|
||||
position: initialLocation,
|
||||
);
|
||||
if (mounted && ok) {
|
||||
messenger.showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'تم إرسال البلاغ بنجاح وتحديث الشبكة التشاركية.',
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
backgroundColor: AppColors.tacticalEmerald,
|
||||
),
|
||||
_showLuxuryNotification(
|
||||
context,
|
||||
message: 'تم إرسال البلاغ بنجاح وتحديث الشبكة التشاركية.',
|
||||
isError: false,
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -196,21 +228,16 @@ class _MapViewState extends State<MapView> {
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (_) => AddPlaceSheet(
|
||||
onSubmit: (name, cat) async {
|
||||
final messenger = ScaffoldMessenger.of(context);
|
||||
final ok = await cubit.submitPlace(
|
||||
name,
|
||||
cat,
|
||||
position: initialLocation,
|
||||
);
|
||||
if (mounted) {
|
||||
messenger.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
ok ? 'تمت إضافة المكان بنجاح! شكراً لمساهمتك.' : 'تعذر حفظ المكان، يرجى المحاولة لاحقاً.',
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
backgroundColor: ok ? AppColors.tacticalEmerald : AppColors.coralDanger,
|
||||
),
|
||||
_showLuxuryNotification(
|
||||
context,
|
||||
message: ok ? 'تمت إضافة المكان بنجاح! شكراً لمساهمتك.' : 'تعذر حفظ المكان، يرجى المحاولة لاحقاً.',
|
||||
isError: !ok,
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -222,6 +249,18 @@ class _MapViewState extends State<MapView> {
|
||||
});
|
||||
}
|
||||
|
||||
void _showLuxuryNotification(
|
||||
BuildContext context, {
|
||||
required String message,
|
||||
bool isError = false,
|
||||
}) {
|
||||
if (isError) {
|
||||
SiroToast.showError(context, message);
|
||||
} else {
|
||||
SiroToast.showSuccess(context, message);
|
||||
}
|
||||
}
|
||||
|
||||
void _showPlaceGatesSheet(BuildContext context, NavigationCubit cubit, PlaceModel place) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
@@ -255,14 +294,10 @@ class _MapViewState extends State<MapView> {
|
||||
return BlocConsumer<NavigationCubit, NavigationState>(
|
||||
listener: (context, state) {
|
||||
if (state.errorMessage != null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
state.errorMessage!,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
backgroundColor: AppColors.coralDanger,
|
||||
),
|
||||
_showLuxuryNotification(
|
||||
context,
|
||||
message: state.errorMessage!,
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -313,8 +348,21 @@ class _MapViewState extends State<MapView> {
|
||||
valueListenable: PipService.instance.isInPipMode,
|
||||
builder: (context, isInPip, _) {
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
resizeToAvoidBottomInset: false,
|
||||
backgroundColor: AppColors.canvasLight,
|
||||
drawer: SiroDrawerWidget(
|
||||
cubit: cubit,
|
||||
state: state,
|
||||
onOpenTouristLandmarks: () => _showTouristLandmarks(context, cubit, state),
|
||||
onOpen3DStudio: () => _showCinematic3DStudio(context, cubit, state),
|
||||
onOpenLayers: () => _showLayerSelector(context, cubit, state.mapTheme),
|
||||
onOpenSettings: () => _showSettingsSheet(context, cubit),
|
||||
onOpenUpdates: () => _showSystemUpdatesSheet(context),
|
||||
onOpenAbout: () => _showAboutAwardsSheet(context),
|
||||
onStartReportHazard: () => cubit.startLocationPicking('hazard'),
|
||||
onStartAddPlace: () => cubit.startLocationPicking('place'),
|
||||
),
|
||||
body: SizedBox.expand(
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
@@ -375,7 +423,7 @@ class _MapViewState extends State<MapView> {
|
||||
_searchController.clear();
|
||||
cubit.clearSearch();
|
||||
},
|
||||
onMenuTap: () => _showLayerSelector(context, cubit, state.mapTheme),
|
||||
onMenuTap: () => _scaffoldKey.currentState?.openDrawer(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
ExplorePanelWidget(
|
||||
@@ -383,6 +431,7 @@ class _MapViewState extends State<MapView> {
|
||||
_searchController.text = q;
|
||||
cubit.searchImmediately(q);
|
||||
},
|
||||
onTouristSelected: () => _showTouristLandmarks(context, cubit, state),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -1276,38 +1325,6 @@ class _MapViewState extends State<MapView> {
|
||||
onTap: () => _showCinematic3DStudio(context, cubit, state),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// Layer Selector Button
|
||||
_buildFloatingCircle(
|
||||
icon: Icons.layers_rounded,
|
||||
color: AppColors.textPrimary,
|
||||
tooltip: 'طبقات الخريطة',
|
||||
onTap: () => _showLayerSelector(context, cubit, state.mapTheme),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// Siro Map Platform Architecture & Info Button
|
||||
_buildFloatingCircle(
|
||||
icon: Icons.info_outline_rounded,
|
||||
color: AppColors.appleBlue,
|
||||
tooltip: 'عن سيرو ماب والمنظومة التقنية',
|
||||
onTap: () => _showAboutAwardsSheet(context),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// Add Place Button
|
||||
_buildFloatingCircle(
|
||||
icon: Icons.add_location_alt_rounded,
|
||||
color: AppColors.appleBlue,
|
||||
tooltip: 'إضافة مكان',
|
||||
onTap: () => cubit.startLocationPicking('place'),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// Report Hazard Button
|
||||
_buildFloatingCircle(
|
||||
icon: Icons.warning_amber_rounded,
|
||||
color: AppColors.sovereignGold,
|
||||
tooltip: 'إبلاغ عن حالة طريق',
|
||||
onTap: () => cubit.startLocationPicking('hazard'),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// Recenter GPS Button
|
||||
_buildFloatingCircle(
|
||||
icon: Icons.my_location_rounded,
|
||||
@@ -1374,6 +1391,88 @@ class _MapViewState extends State<MapView> {
|
||||
),
|
||||
),
|
||||
|
||||
// ── 6c. FLOATING 3D ORBIT STATUS & STOP BUTTON ──
|
||||
if (!isInPip && state.isOrbiting)
|
||||
Positioned(
|
||||
left: 16,
|
||||
bottom: (state.speed > 3.0) ? 100 : 32,
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF1D1D1F).withValues(alpha: 0.94),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
border: Border.all(
|
||||
color: const Color(0xFFEA580C).withValues(alpha: 0.8),
|
||||
width: 1.5,
|
||||
),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x3D000000),
|
||||
blurRadius: 16,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => cubit.stopOrbit(),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.stop_circle_rounded,
|
||||
color: AppColors.coralDanger,
|
||||
size: 18,
|
||||
),
|
||||
SizedBox(width: 6),
|
||||
Text(
|
||||
'إيقاف الدوران 360°',
|
||||
style: TextStyle(
|
||||
fontFamily: '.SF Pro Text',
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Container(
|
||||
width: 1,
|
||||
height: 16,
|
||||
color: Colors.white.withValues(alpha: 0.2),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () => _showCinematic3DStudio(context, cubit, state),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.15),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.tune_rounded,
|
||||
color: Colors.white,
|
||||
size: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// ── 7. LOADING OVERLAY ──
|
||||
if (state.status == NavigationStatus.loading)
|
||||
Container(
|
||||
|
||||
Reference in New Issue
Block a user