Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
589 changes: 104 additions & 485 deletions lib/features/disaster_alerts/widgets/evacuation_map.dart

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../constants/colors.dart';

class MapControls extends StatelessWidget {
final bool isDarkMode;
final bool trafficEnabled;
final Function() onMapTypePressed;
final Function() onTrafficToggled;
final Function() onDarkModeToggled;
final Function() onMyLocationPressed;

const MapControls({
Key? key,
required this.isDarkMode,
required this.trafficEnabled,
required this.onMapTypePressed,
required this.onTrafficToggled,
required this.onDarkModeToggled,
required this.onMyLocationPressed,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: const Duration(milliseconds: 200),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Map type button
IconButton(
icon: const Icon(Icons.layers_outlined),
color: EvacuationColors.primaryColor,
onPressed: () {
HapticFeedback.selectionClick();
onMapTypePressed();
},
),

// Traffic toggle
IconButton(
icon: Icon(
Icons.traffic_outlined,
color: trafficEnabled
? EvacuationColors.primaryColor
: Colors.grey[600],
),
onPressed: () {
HapticFeedback.selectionClick();
onTrafficToggled();
},
),

// Dark mode toggle
IconButton(
icon: Icon(
isDarkMode ? Icons.wb_sunny_outlined : Icons.nights_stay_outlined,
color: isDarkMode
? EvacuationColors.primaryColor
: Colors.grey[600],
),
onPressed: () {
HapticFeedback.selectionClick();
onDarkModeToggled();
},
),

// My location button
IconButton(
icon: const Icon(Icons.my_location),
color: EvacuationColors.primaryColor,
onPressed: () {
HapticFeedback.selectionClick();
onMyLocationPressed();
},
),
],
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import 'package:google_maps_flutter/google_maps_flutter.dart';

class MapStyleUtils {
static Future<void> setMapStyle(GoogleMapController controller, bool isDarkMode) async {
if (isDarkMode) {
await controller.setMapStyle('''
[
{
"elementType": "geometry",
"stylers": [{"color": "#242f3e"}]
},
{
"elementType": "labels.text.fill",
"stylers": [{"color": "#746855"}]
},
{
"elementType": "labels.text.stroke",
"stylers": [{"color": "#242f3e"}]
},
{
"featureType": "administrative.locality",
"elementType": "labels.text.fill",
"stylers": [{"color": "#d59563"}]
},
{
"featureType": "poi",
"elementType": "labels.text.fill",
"stylers": [{"color": "#d59563"}]
},
{
"featureType": "poi.park",
"elementType": "geometry",
"stylers": [{"color": "#263c3f"}]
},
{
"featureType": "poi.park",
"elementType": "labels.text.fill",
"stylers": [{"color": "#6b9a76"}]
},
{
"featureType": "road",
"elementType": "geometry",
"stylers": [{"color": "#38414e"}]
},
{
"featureType": "road",
"elementType": "geometry.stroke",
"stylers": [{"color": "#212a37"}]
},
{
"featureType": "road",
"elementType": "labels.text.fill",
"stylers": [{"color": "#9ca5b3"}]
},
{
"featureType": "road.highway",
"elementType": "geometry",
"stylers": [{"color": "#746855"}]
},
{
"featureType": "road.highway",
"elementType": "geometry.stroke",
"stylers": [{"color": "#1f2835"}]
},
{
"featureType": "road.highway",
"elementType": "labels.text.fill",
"stylers": [{"color": "#f3d19c"}]
},
{
"featureType": "transit",
"elementType": "geometry",
"stylers": [{"color": "#2f3948"}]
},
{
"featureType": "transit.station",
"elementType": "labels.text.fill",
"stylers": [{"color": "#d59563"}]
},
{
"featureType": "water",
"elementType": "geometry",
"stylers": [{"color": "#17263c"}]
},
{
"featureType": "water",
"elementType": "labels.text.fill",
"stylers": [{"color": "#515c6d"}]
},
{
"featureType": "water",
"elementType": "labels.text.stroke",
"stylers": [{"color": "#17263c"}]
}
]
''');
} else {
await controller.setMapStyle(null); // Reset to default style
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../constants/colors.dart';

class MapTypeSelector {
static void show(BuildContext context, MapType currentMapType, Function(MapType) onMapTypeSelected) {
showModalBottomSheet(
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
builder: (context) => Container(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Padding(
padding: EdgeInsets.only(bottom: 16),
child: Text(
'Select Map Type',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
_buildMapTypeOption(
context,
'Standard',
Icons.map_outlined,
MapType.normal,
currentMapType,
onMapTypeSelected,
),
_buildMapTypeOption(
context,
'Satellite',
Icons.satellite_outlined,
MapType.satellite,
currentMapType,
onMapTypeSelected,
),
_buildMapTypeOption(
context,
'Terrain',
Icons.terrain_outlined,
MapType.terrain,
currentMapType,
onMapTypeSelected,
),
_buildMapTypeOption(
context,
'Hybrid',
Icons.layers_outlined,
MapType.hybrid,
currentMapType,
onMapTypeSelected,
),
],
),
),
);
}

static Widget _buildMapTypeOption(
BuildContext context,
String title,
IconData icon,
MapType mapType,
MapType currentMapType,
Function(MapType) onMapTypeSelected,
) {
final isSelected = currentMapType == mapType;

return ListTile(
leading: Icon(
icon,
color: isSelected ? EvacuationColors.primaryColor : Colors.grey[600],
),
title: Text(
title,
style: TextStyle(
color: isSelected ? EvacuationColors.primaryColor : Colors.black,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
trailing: isSelected
? const Icon(Icons.check, color: EvacuationColors.primaryColor)
: null,
onTap: () {
onMapTypeSelected(mapType);
Navigator.pop(context);
},
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:async';
import '../../constants/colors.dart';

class RouteAnimator {
List<LatLng> _animatedPoints = [];
Set<Polyline> _animatedPolylines = {};
Timer? _routeAnimationTimer;
bool _isAnimatingRoute = false;

// Getters
Set<Polyline> get animatedPolylines => _animatedPolylines;
bool get isAnimating => _isAnimatingRoute;

void dispose() {
_routeAnimationTimer?.cancel();
}

void clearRoute() {
_animatedPolylines.clear();
_animatedPoints.clear();
_isAnimatingRoute = false;
_routeAnimationTimer?.cancel();
}

void startAnimation(Set<Polyline> polylines, Function(Set<Polyline>) onUpdate, Function() onComplete) {
// Cancel any existing animation
_routeAnimationTimer?.cancel();

if (polylines.isEmpty) return;

_isAnimatingRoute = true;
_animatedPoints = [];
_animatedPolylines = {};
onUpdate(_animatedPolylines);

final allPoints = polylines.first.points;
final totalPoints = allPoints.length;
int currentPointIndex = 0;

// Create a timer that adds points to the animated polyline
_routeAnimationTimer = Timer.periodic(const Duration(milliseconds: 50), (timer) {
if (currentPointIndex >= totalPoints) {
timer.cancel();
return;
}

// Add the next point to our animated points list
_animatedPoints.add(allPoints[currentPointIndex]);

// Create a new polyline with the current points
_animatedPolylines = {
Polyline(
polylineId: const PolylineId('animated_route'),
points: _animatedPoints,
color: EvacuationColors.primaryColor,
width: 5,
patterns: [
PatternItem.dash(20),
PatternItem.gap(5),
],
startCap: Cap.roundCap,
endCap: Cap.roundCap,
),
};

onUpdate(_animatedPolylines);

currentPointIndex++;

// If we've added all points, stop the animation
if (currentPointIndex >= totalPoints) {
_isAnimatingRoute = false;
onComplete();
timer.cancel();
}
});
}
}
Loading