Documentation Index
Fetch the complete documentation index at: https://mintlify.com/ihfaz297/MND/llms.txt
Use this file to discover all available pages before exploring further.
The MND mobile app integrates Google Maps to provide interactive route visualization with color-coded polylines, markers, and real-time directions.
Google Maps Integration
The app uses the google_maps_flutter package for map rendering:
dependencies:
google_maps_flutter: ^2.5.0
Source: pubspec.yaml:54
RouteMapScreen
The RouteMapScreen displays a route on an interactive map.
Basic Setup
class RouteMapScreen extends StatefulWidget {
final RouteOption routeOption;
const RouteMapScreen({
super.key,
required this.routeOption,
});
@override
State<RouteMapScreen> createState() => _RouteMapScreenState();
}
class _RouteMapScreenState extends State<RouteMapScreen> {
final Completer<GoogleMapController> _mapController = Completer();
Set<Polyline> _polylines = {};
Set<Marker> _markers = {};
bool _isLoading = true;
@override
void initState() {
super.initState();
_loadRoutePolylines();
}
}
Source: lib/screens/route_map/route_map_screen.dart:39-70
Rendering the Map
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.routeOption.label),
),
body: GoogleMap(
initialCameraPosition: const CameraPosition(
target: LatLng(24.9178, 91.8320), // Sylhet center
zoom: 12,
),
onMapCreated: (controller) {
_mapController.complete(controller);
controller.setMapStyle(_darkMapStyle);
},
polylines: _polylines,
markers: _markers,
myLocationEnabled: false,
zoomControlsEnabled: true,
mapToolbarEnabled: false,
),
);
}
Source: lib/screens/route_map/route_map_screen.dart:254-283
DirectionsService
The DirectionsService fetches route polylines from Google Directions API.
Getting Directions
class DirectionsService {
static const String _baseUrl =
'https://maps.googleapis.com/maps/api/directions/json';
static Future<DirectionsResult?> getDirections({
required String origin,
required String destination,
}) async {
final url = Uri.parse(
'$_baseUrl?origin=${Uri.encodeComponent(origin)}'
'&destination=${Uri.encodeComponent(destination)}'
'&mode=driving'
'&key=${ApiConfig.googleMapsApiKey}',
);
try {
final response = await http.get(url);
if (response.statusCode != 200) {
return null;
}
final data = json.decode(response.body);
if (data['status'] != 'OK') {
return null;
}
final route = data['routes'][0];
final leg = route['legs'][0];
// Decode polyline
final polylinePoints =
_decodePolyline(route['overview_polyline']['points']);
// Extract bounds
final bounds = route['bounds'];
final northeast = LatLng(
bounds['northeast']['lat'].toDouble(),
bounds['northeast']['lng'].toDouble(),
);
final southwest = LatLng(
bounds['southwest']['lat'].toDouble(),
bounds['southwest']['lng'].toDouble(),
);
return DirectionsResult(
polylinePoints: polylinePoints,
distanceMeters: leg['distance']['value'],
distanceText: leg['distance']['text'],
durationSeconds: leg['duration']['value'],
durationText: leg['duration']['text'],
startLocation: LatLng(
leg['start_location']['lat'].toDouble(),
leg['start_location']['lng'].toDouble(),
),
endLocation: LatLng(
leg['end_location']['lat'].toDouble(),
leg['end_location']['lng'].toDouble(),
),
bounds: LatLngBounds(southwest: southwest, northeast: northeast),
);
} catch (e) {
return null;
}
}
}
Source: lib/services/directions_service.dart:7-85
DirectionsResult Model
class DirectionsResult {
final List<LatLng> polylinePoints;
final int distanceMeters;
final String distanceText;
final int durationSeconds;
final String durationText;
final LatLng startLocation;
final LatLng endLocation;
final LatLngBounds bounds;
DirectionsResult({
required this.polylinePoints,
required this.distanceMeters,
required this.distanceText,
required this.durationSeconds,
required this.durationText,
required this.startLocation,
required this.endLocation,
required this.bounds,
});
}
Source: lib/services/directions_service.dart:127-148
Drawing Polylines
Polylines are drawn for each leg of the route.
Color Coding by Mode
class RouteColors {
static const Color bus = Color(0xFF4285F4); // Google Blue
static const Color local = Color(0xFFFF9800); // Orange
static const Color walk = Color(0xFF4CAF50); // Green
}
Color _getModeColor(String mode) {
switch (mode.toLowerCase()) {
case 'bus':
return RouteColors.bus;
case 'local':
case 'cng':
return RouteColors.local;
case 'walk':
case 'walking':
return RouteColors.walk;
default:
return RouteColors.bus;
}
}
Source: lib/screens/route_map/route_map_screen.dart:31-91
Creating Polylines
Future<void> _loadRoutePolylines() async {
final Set<Polyline> polylines = {};
int polylineIdCounter = 0;
// Process each leg of the route
for (final leg in widget.routeOption.legs) {
final color = _getModeColor(leg.mode);
// Get the origin and destination addresses for this leg
final originAddress = _getNodeAddress(leg.from);
final destinationAddress = _getNodeAddress(leg.to);
// Fetch directions from Google API
final directions = await DirectionsService.getDirections(
origin: originAddress,
destination: destinationAddress,
);
if (directions != null) {
// Add polyline for this leg
polylines.add(
Polyline(
polylineId: PolylineId('leg_$polylineIdCounter'),
points: directions.polylinePoints,
color: color,
width: 6,
patterns: leg.mode.toLowerCase() == 'walk'
? [PatternItem.dot, PatternItem.gap(10)]
: [],
),
);
polylineIdCounter++;
}
// Small delay to avoid rate limiting
await Future.delayed(const Duration(milliseconds: 150));
}
setState(() {
_polylines = polylines;
_isLoading = false;
});
}
Source: lib/screens/route_map/route_map_screen.dart:94-187
Walking Segments
Walking segments use dotted lines:
Polyline(
polylineId: PolylineId('leg_$polylineIdCounter'),
points: directions.polylinePoints,
color: color,
width: 6,
patterns: leg.mode.toLowerCase() == 'walk'
? [PatternItem.dot, PatternItem.gap(10)] // Dotted for walking
: [], // Solid for bus/vehicle
)
Source: lib/screens/route_map/route_map_screen.dart:123-132
Markers
Markers indicate start, end, and transfer points.
Creating Markers
final Set<Marker> markers = {};
for (int i = 0; i < allStopPositions.length; i++) {
final isStart = i == 0;
final isEnd = i == allStopPositions.length - 1;
final stop = allStopPositions[i];
markers.add(
Marker(
markerId: MarkerId('stop_$i'),
position: stop.position,
icon: isStart
? BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen)
: (isEnd
? BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed)
: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueAzure)),
infoWindow: InfoWindow(
title: isStart ? 'Start' : (isEnd ? 'End' : 'Stop ${i + 1}'),
snippet: stop.nodeId,
),
),
);
}
Source: lib/screens/route_map/route_map_screen.dart:190-211
Marker Colors
- Green: Start point
- Red: End point
- Blue: Transfer/intermediate stops
Camera Bounds
The map automatically fits all route points in view:
if (totalBounds != null) {
final controller = await _mapController.future;
controller.animateCamera(
CameraUpdate.newLatLngBounds(totalBounds, 60),
);
}
Source: lib/screens/route_map/route_map_screen.dart:220-225
Node Address Mapping
Node IDs are mapped to real addresses for Google Directions API:
const Map<String, String> nodeAddresses = {
'TILAGOR': 'Tilagor, Sylhet, Bangladesh',
'SHIBGONJ': 'Shibgonj, Sylhet, Bangladesh',
'NAIORPUL': 'Naiorpul, Sylhet, Bangladesh',
'KUMARPARA': 'Kumarpara, Sylhet, Bangladesh',
'CAMPUS': 'Shahjalal University of Science and Technology, Sylhet, Bangladesh',
'AMBARKHANA': 'Ambarkhana, Sylhet, Bangladesh',
// ... more nodes
};
String _getNodeAddress(String nodeId) {
return nodeAddresses[nodeId] ?? '$nodeId, Sylhet, Bangladesh';
}
Source: lib/screens/route_map/route_map_screen.dart:8-75
Statistics Card
Display route statistics at the bottom:
Widget _buildStatsCard() {
return Card(
elevation: 8,
color: Colors.grey.shade900,
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStatItem(
icon: Icons.straighten,
value: _formatDistance(_totalDistanceMeters),
label: 'Distance',
),
_buildStatItem(
icon: Icons.access_time,
value: _formatDuration(_totalDurationSeconds),
label: 'Duration',
),
_buildStatItem(
icon: Icons.swap_horiz,
value: '${widget.routeOption.transfers}',
label: 'Transfers',
),
],
),
),
);
}
Source: lib/screens/route_map/route_map_screen.dart:352-390
Legend
Show color legend for transport modes:
Widget _buildLegend() {
return Card(
color: Colors.grey.shade900.withOpacity(0.9),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Legend',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
_buildLegendItem(RouteColors.bus, 'Bus'),
_buildLegendItem(RouteColors.local, 'Local/CNG'),
_buildLegendItem(RouteColors.walk, 'Walking'),
],
),
),
);
}
Source: lib/screens/route_map/route_map_screen.dart:421-447
Dark Map Style
Custom dark theme for the map:
static const String _darkMapStyle = '''
[
{"elementType": "geometry", "stylers": [{"color": "#242f3e"}]},
{"elementType": "labels.text.stroke", "stylers": [{"color": "#242f3e"}]},
{"elementType": "labels.text.fill", "stylers": [{"color": "#746855"}]},
{"featureType": "road", "elementType": "geometry", "stylers": [{"color": "#38414e"}]},
{"featureType": "water", "elementType": "geometry", "stylers": [{"color": "#17263c"}]}
]
''';
Source: lib/screens/route_map/route_map_screen.dart:474-486
Example Usage
// Navigate to map screen from route card
void _openRouteMap(BuildContext context, RouteOption route) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => RouteMapScreen(routeOption: route),
),
);
}
Error Handling
try {
final directions = await DirectionsService.getDirections(
origin: originAddress,
destination: destinationAddress,
);
if (directions == null) {
print('Failed to get directions');
// Show error to user
}
} catch (e) {
setState(() {
_errorMessage = 'Failed to load route: $e';
});
}
Source: lib/screens/route_map/route_map_screen.dart:226-231
Next Steps