dynamic_maps_engine

Pub Version Platform License: MIT

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
Navigation Flow Simulation Screen Record

Table of Contents

  1. Features
  2. Installation
  3. Quick Start
  4. DynamicMapWidget Parameters
  5. DynamicMapCubit - Full API Reference
  6. NavigationVoiceService - Full API Reference
  7. MarkerMotionService - Full API Reference
  8. LocationTrackingService - Full API Reference
  9. MapCameraService - Full API Reference
  10. RouteCalculationService - Full API Reference
  11. IconService - Full API Reference
  12. Cost Optimization
  13. 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}');
        },
      ),
    );
  }
}

DynamicMapWidget Parameters

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

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
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.