dynamic_maps_engine

A robust, plug-and-play Flutter map engine for real-time courier/delivery tracking, with smooth animated markers, automatic rerouting, turn-by-turn voice navigation, and zero-cost routing via OSRM.
Read the introduction article on my journal: Introducing Dynamic Maps Engine
Preview
| Navigation Flow |
Simulation Screen Record |
 |
 |
Table of Contents
- Features
- Installation
- Quick Start
- DynamicMapWidget Parameters
- DynamicMapCubit - Full API Reference
- NavigationVoiceService - Full API Reference
- MarkerMotionService - Full API Reference
- LocationTrackingService - Full API Reference
- MapCameraService - Full API Reference
- RouteCalculationService - Full API Reference
- IconService - Full API Reference
- Cost Optimization
- Security Note
Features
| Feature |
Description |
| Plug-and-Play Widget |
Drop DynamicMapWidget into any screen. No boilerplate. |
| Gradient Polyline |
Route line rendered with smooth color gradient toward destination. |
| Smooth Marker Animation |
Driver icon glides along the route — no jumping or stuttering. |
| Smart Road Snapping |
Driver marker stays clamped to the road polyline despite GPS jitter. |
| Auto Rerouting |
Detects off-route movement and recalculates in background. |
| Voice Navigation |
Turn-by-turn TTS: "In 300 meters, turn right onto Jl. Sudirman" |
| Roundabout Support |
Accurately announces the correct exit number: "Take the 2nd exit" |
| Zero-Cost Routing |
Uses OSRM (OpenStreetMap) by default; falls back to Google Maps. |
| Secure API Key |
API keys are never stored inside the package. You pass them in. |
| iOS & Android |
Optimized for both platforms, including background location. |
Installation
For Development (Local Path)
dependencies:
dynamic_maps_engine:
path: ../dynamic_maps_engine
For Production (pub.dev)
dependencies:
dynamic_maps_engine: ^0.0.17
Quick Start
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:dynamic_maps_engine/dynamic_maps_engine.dart';
class CourierTrackingMap extends StatelessWidget {
const CourierTrackingMap({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: DynamicMapWidget(
apiKey: 'YOUR_GOOGLE_MAPS_API_KEY',
origin: const LatLng(-6.200000, 106.816666),
destination: const LatLng(-6.2100, 106.8200),
onLocationUpdated: (position) {
// Send to your backend every GPS tick
print('Driver: ${position.latitude}, ${position.longitude}');
},
),
);
}
}
| Parameter |
Type |
Required |
Default |
Description |
apiKey |
String |
Yes |
— |
Google Maps API key for map tiles & fallback routing |
origin |
LatLng |
Yes |
— |
Starting coordinate (driver's current location) |
destination |
LatLng |
Yes |
— |
Destination coordinate (customer address) |
onLocationUpdated |
Function(Position)? |
No |
null |
Callback fired on every GPS update |
driverIcon |
BitmapDescriptor? |
No |
Default pin |
Custom bitmap for driver marker |
destinationIcon |
BitmapDescriptor? |
No |
Default pin |
Custom bitmap for destination marker |
enableSimulation |
bool |
No |
false |
Simulate driving along the route (for testing) |
simulationSpeedKmh |
double |
No |
40.0 |
Speed of simulation in km/h |
DynamicMapCubit - Full API Reference
The central state controller. Manages markers, polylines, camera, voice, and rerouting.
Class Constants
| Constant |
Type |
Value |
Description |
offRouteDistance |
double |
40.0 |
Meters off-route before rerouting is triggered |
offRouteThreshold |
int |
3 |
Consecutive off-route ticks before recalculating |
minSpeedForReroute |
double |
0.5 |
Min speed (m/s) for off-route detection to activate |
Constructor Parameters
DynamicMapCubit({
required String apiKey,
BitmapDescriptor? driverIcon,
BitmapDescriptor? destinationIcon,
void Function(Position)? onLocationUpdated,
})
| Parameter |
Type |
Description |
apiKey |
String |
Google Maps API key |
driverIcon |
BitmapDescriptor? |
Custom driver marker icon |
destinationIcon |
BitmapDescriptor? |
Custom destination marker icon |
onLocationUpdated |
Function(Position)? |
Fired on every real GPS update (use for socket/backend sync) |
Public Methods
| Method |
Returns |
Description |
initialize({LatLng origin, LatLng destination, GoogleMapController? controller}) |
Future<void> |
One-time setup: loads route, sets up markers, starts GPS tracking |
selectRoute(int index) |
void |
Switch between alternative routes (index from state.availableRoutes) |
recalculateRoute() |
Future<void> |
Force reroute from current GPS position to existing destination |
toggleSimulation(bool enable, {double? speedKmh}) |
void |
Start/stop simulation mode. speedKmh defaults to last set value |
close() |
Future<void> |
Disposes all timers, streams, and voice service. Always call on dispose |
Internal Methods (For Contributors)
| Method |
Returns |
Description |
_setupMarkers(LatLng currentLocation, LatLng destination) |
Future<void> |
Creates driver and destination Marker objects with icons |
_calculateAndSetRoute(LatLng start, LatLng end) |
Future<void> |
Calls RouteCalculationService, builds polylines, loads voice steps |
_generateRoutePolylines(List<RouteData> routes, int selectedIndex) |
Set<Polyline> |
Builds all route polylines — selected route bold, alternatives faded |
_buildGradientPolylines(List<LatLng> points) |
Set<Polyline> |
Segments route into color-graded sections (green to red gradient) |
_startLocationTracking() |
void |
Starts LocationTrackingService stream and binds _handleLocationUpdate |
_handleLocationUpdate(Position position, {bool forceUpdate}) |
void |
Entry point for GPS updates: saves raw GPS, triggers marker animation and onLocationUpdated callback |
_updateDriverMarker(LatLng position, double bearing, int routeIndex) |
void |
Updates marker position & bearing; trims polyline to remaining route; recalculates live ETA |
_startRerouteCheck() |
void |
Starts a 2-second periodic timer that calls _checkOffRoute |
_checkOffRoute() |
void |
Measures driver distance from route polyline; increments _offRouteCount and calls recalculateRoute when threshold reached |
State Fields (DynamicMapState)
| Field |
Type |
Description |
markers |
Set<Marker> |
All visible map markers |
polylines |
Set<Polyline> |
Currently drawn polylines (remaining route) |
currentLocation |
LatLng? |
Latest known driver position |
destinationLocation |
LatLng? |
Target destination |
currentBearing |
double |
Current heading in degrees (0–360) |
currentSpeed |
double |
Smoothed speed in km/h (8-sample average) |
routeDistance |
String |
Remaining distance ("2.3 km") |
routeDuration |
String |
Remaining ETA ("7 min") |
routingEngine |
String |
"OSRM (Free)" or "Google Maps (Paid)" |
isRerouting |
bool |
True while recalculating route |
isSimulating |
bool |
True when simulation mode is active |
availableRoutes |
List<RouteData> |
All calculated route alternatives |
selectedRouteIndex |
int |
Index of active route |
NavigationVoiceService - Full API Reference
Handles all turn-by-turn navigation logic: step detection, voice announcements, distance calculation.
Public Getters
| Getter |
Type |
Description |
isNavigating |
bool |
Whether navigation is currently active |
currentInstruction |
String? |
Instruction for the current active step (e.g. "Turn right") |
nextInstruction |
String? |
Instruction for the next upcoming step |
currentManeuverIcon |
String? |
Icon name for current maneuver |
nextManeuverIcon |
String? |
Icon name for upcoming maneuver |
currentSpeed |
double |
Last known speed in km/h |
nextStepDistance |
double |
Distance to end of current step in meters |
remainingDistance |
double |
Total remaining distance (meters) to destination |
remainingDuration |
double |
Estimated remaining duration in seconds |
formattedRemainingDistance |
String |
Human readable: "1.2 km" or "850 m" |
formattedRemainingDuration |
String |
Human readable: "5 min" or "1 hr 12 min" |
currentSteps |
List<NavigationStep> |
All loaded navigation steps |
currentStep |
NavigationStep? |
Currently active step object |
roundaboutExit |
int? |
Exit number if current step is a roundabout |
Public Methods
| Method |
Returns |
Description |
startNavigation(List<Map<String, dynamic>> stepsData) |
Future<void> |
Loads step data, initializes TTS engine, sets first instruction |
updatePosition(Position position) |
void |
Main update loop. Feed GPS positions here; handles step advancement and voice triggers |
getManeuverIcon(String instruction, {String? maneuver}) |
String |
Returns icon name string for a given maneuver type or instruction text |
dispose() |
void |
Stops TTS, cancels timers, clears all state |
Internal Methods (For Contributors)
| Method |
Returns |
Description |
_initializeTts() |
Future<void> |
Sets up flutter_tts engine: language, pitch, rate, volume. Must be awaited before any speech. |
_updateDisplayInstructions() |
void |
Refreshes instructions from current step index. Called after every step change. |
_moveToNextStep() |
void |
Advances step index by 1, resets warning flags, calls _updateDisplayInstructions() |
_snapToClosestStep(Position position) |
void |
Jumps step index forward if driver is closest to a later step. |
_checkNavigationProgress(Position position) |
void |
Checks distance to step end, fires voice warnings, advances to next step when arrived |
_calculateDistanceAlongPolyline(LatLng position, List<LatLng> polyline) |
double |
Calculates remaining meters along a step's polyline |
_roundDistanceForSpeech(double meters) |
int |
Rounds distance to nearest 50m or 100m for clean voice output |
_cleanHtmlInstruction(String html) |
String |
Strips HTML tags from instructions |
NavigationStep Object
| Field |
Type |
Description |
instruction |
String |
Cleaned human-readable instruction |
distance |
double |
Step distance in meters |
duration |
double |
Step duration in seconds |
startLat / startLng |
double |
Coordinate of step start |
endLat / endLng |
double |
Coordinate of step end |
maneuver |
String? |
Raw maneuver type |
roundaboutExit |
int? |
Exit number for roundabouts |
polylinePoints |
List<LatLng> |
All coordinates along this step's path |
isRoundabout |
bool |
Shortcut for roundabout detection |
formattedDistance |
String |
Human readable distance |
formattedDuration |
String |
Human readable duration |
MarkerMotionService - Full API Reference
Smoothly animates a map marker from one position to another using frame-by-frame interpolation.
Public Methods
| Method |
Returns |
Description |
setRoutePoints(List<LatLng> points) |
void |
Sets the full route polyline for snapping and simulation |
reset() |
void |
Resets all position, bearing, and index state |
injectPosition(LatLng position, double bearing) |
void |
Force-sets position without animation |
moveTo({required LatLng target, required Function(LatLng, double, int) onUpdate, double? initialBearing, double? heading}) |
void |
Animate marker to target. |
startSimulation(Function(LatLng, double, int) onUpdate, {double speedKmh, bool fromBeginning}) |
void |
Begins automatic route simulation at given speed. |
stopSimulation() |
void |
Stops simulation timer |
dispose() |
void |
Cancels all timers and cleans up resources |
LocationTrackingService - Full API Reference
Wraps Geolocator to provide filtered, platform-optimized GPS streams.
Public Methods
| Method |
Returns |
Description |
getCurrentPosition({LocationAccuracy accuracy}) |
Future<Position?> |
One-shot: gets current GPS fix. |
getLastKnownPosition() |
Future<Position?> |
Returns last cached GPS position |
startTracking({required Function(Position) onPositionUpdate, Duration? interval, bool useStream}) |
void |
Start continuous GPS tracking. |
pauseTracking() |
void |
Temporarily stops the position stream |
resumeTracking() |
void |
Resumes a paused stream |
stopTracking() |
void |
Fully stops tracking and clears all listeners |
updateLocationSettings({int? interval, double? distanceFilter}) |
Future<void> |
Hot-update GPS interval and distance filter |
dispose() |
void |
Stops tracking and releases all resources |
MapCameraService - Full API Reference
Controls Google Map camera behavior.
Public Methods
| Method |
Returns |
Description |
setController(GoogleMapController controller) |
void |
Binds the GoogleMapController after map is created |
setMode(CameraMode mode) |
void |
Switch camera mode |
activateManualMode({Duration? autoResetDuration}) |
void |
Lock camera to manual. Auto-resets to following after timeout. |
followUser(Position position, {bool isSimulating}) |
Future<void> |
Animate camera to follow driver with tilt and bearing |
centerToLocation(LatLng location, {double zoom}) |
Future<void> |
Move camera to a fixed location |
fitToRoute(LatLng start, LatLng destination, {bool isSimulating}) |
Future<void> |
Fit camera to show entire route |
fitBoundsWithPadding(LatLng start, LatLng end, {bool isRerouting}) |
Future<void> |
Fit camera to bounds with adaptive padding |
droneArrival(Position position) |
Future<void> |
Cinematic arrival animation |
RouteCalculationService - Full API Reference
Calculates routes using OSRM (free) with automatic Google Maps Directions API fallback.
Public Methods
| Method |
Returns |
Description |
calculateRoutes(LatLng origin, LatLng destination) |
Future<List<RouteData>> |
Calculates all available routes (with alternatives). |
calculateRoute(LatLng origin, LatLng destination) |
Future<RouteData> |
Convenience: returns first (best) route |
calculateDistance(LatLng point1, LatLng point2) |
double |
Haversine distance in meters between two points |
IconService - Full API Reference
Pre-loads and caches custom bitmap icons for use as Google Maps markers.
Public Methods
| Method |
Returns |
Description |
preloadDriverIcon({Color color}) |
Future<void> |
Pre-renders driver chevron icon in given color. |
getDriverIcon() |
BitmapDescriptor |
Returns pre-loaded driver icon |
getUserLocationIcon() |
BitmapDescriptor |
Returns user location dot icon |
getDestinationIcon() |
BitmapDescriptor |
Returns destination icon |
getRealGpsIcon() |
BitmapDescriptor |
Returns raw GPS position indicator |
getConnectionDotIcon() |
BitmapDescriptor |
Returns animated connection status dot |
getTurnArrowIcon() |
BitmapDescriptor |
Returns turn arrow icon for navigation overlay |
Cost Optimization
By default, routing uses OSRM (100% free, OpenStreetMap). Only falls back to Google Maps Directions API (paid) when OSRM is unavailable.
| Service |
Google Maps API Cost |
OSRM (This Package) Cost |
Savings |
| Routing / Directions |
~$5.00 / 1000 requests |
$0.00 |
100% |
| Turn-by-turn Steps |
Included in Directions |
$0.00 |
100% |
| Map Tiles Rendering |
~$7.00 / 1000 loads |
Normal GMap Cost |
— |
| Geocoding |
~$5.00 / 1000 requests |
Use OSM Nominatim |
~95%+ |
Security Note
This package never reads .env files internally. Always pass your apiKey from your application's own secure environment setup.
// Correct
DynamicMapWidget(apiKey: dotenv.env['GOOGLE_MAPS_KEY']!, ...)
// Wrong
DynamicMapWidget(apiKey: 'AIzaSy...', ...)
License
This project is licensed under the MIT License.