Flutter Native - Navigation
This guide explains how to start, control, and integrate turn-by-turn navigation along an itinerary. Navigation follows a route computed and displayed via Itinerary. You must add an itinerary first, or use startNavigationFromUserLocation to compute and navigate in one step.
Overview
Navigation provides turn-by-turn guidance along an itinerary. The flow is:
- Add an itinerary — See Itinerary for
addItineraryFromUserLocation,addItineraryFromMapCenter, oraddItinerary - Start navigation —
startNavigationWithCurrentItineraryorstartNavigationFromUserLocation - Follow instructions — Navigation UI shows current step, remaining distance, and next instruction
- Arrive or stop —
onArrivedAtDestinationfires, or user stops viastopNavigation
Starting navigation
With existing itinerary
After an itinerary is added (Itinerary), start turn-by-turn navigation:
await mapManager.startNavigationWithCurrentItinerary();
Optional: customize the route color during navigation:
await mapManager.startNavigationWithCurrentItinerary(color: Colors.purple);
Direct from user location (no prior itinerary)
Compute an itinerary and start navigation in one call. Requires location (GPS) or camera (VPS) permission granted before calling—see GPS and VPS.
await mapManager.startNavigationFromUserLocation(
destination: poi.coordinate,
searchRules: ItinerarySearchRules.WHEELCHAIR,
);
Stopping navigation
await mapManager.stopNavigation();
Callbacks
Listen to navigation events via WemapMap:
| Callback | When triggered |
|---|---|
onNavigationStarted | Navigation begins; provides Navigation (contains itinerary) |
onNavigationStopped | User stopped navigation |
onNavigationFailed | Navigation failed (e.g. positioning lost) |
onArrivedAtDestination | User reached the destination |
onNavigationInfoChanged | Progress update (current step, remaining distance/time) |
WemapMap(
options: MapOptions(...),
onNavigationStarted: (Navigation navigation) {
print('Started: ${navigation.itinerary.distance}m');
},
onNavigationStopped: () {
print('Stopped');
},
onNavigationFailed: (String error) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(error)));
},
onArrivedAtDestination: () {
print('Arrived!');
// Show success UI, play sound, etc.
},
onNavigationInfoChanged: (NavigationInfo info) {
// Update your UI: info.remainingDistance, info.remainingTime, info.nextStep
},
)
Navigation models
Navigation
| Field | Type | Description |
|---|---|---|
itinerary | Itinerary | The route being followed |
NavigationInfo
Called frequently during navigation via onNavigationInfoChanged:
| Field | Type | Description |
|---|---|---|
previousStep | Step? | Last completed step |
nextStep | Step? | Current/upcoming step |
leg | Leg | Current leg |
traveledDistance | double | Distance traveled so far (meters) |
remainingDistance | double | Distance to destination (meters) |
remainingTime | double | Time to destination (seconds) |
remainingStepDistance | double? | Distance to next instruction point |
Built-in widgets
Enable these in WemapMap to show the default navigation UI:
| Option | Description |
|---|---|
navigationWidgetEnabled: true | Shows NavigationWidget during turn-by-turn — current instruction, remaining distance/time, stop button |
arrivedToDestinationDialog: true | Shows a dialog when the user reaches the destination |
navigationSuggestionDialog: true | Shows a dialog ~10 seconds after an itinerary is added, suggesting to start navigation |
See Widgets for screenshots.
Integration flow: POI → Itinerary → Navigation
When detailedViewEnabled, itineraryWidgetEnabled, and navigationWidgetEnabled are enabled:
- User taps a POI on the map →
DetailedViewopens - User taps "Go there" →
addItineraryFromUserLocationis called (Itinerary) - Itinerary is computed and drawn →
onItineraryAddedfires ItineraryWidgetappears with duration and "Start" button- User taps "Start" →
startNavigationWithCurrentItineraryis called NavigationWidgetshows turn-by-turn instructions- On arrival →
onArrivedAtDestinationfires (and optionallyarrivedToDestinationDialog)
Custom integration (no built-in NavigationWidget)
You can drive navigation entirely from your own UI:
// Start navigation
await mapManager.startNavigationWithCurrentItinerary();
// Listen for progress
onNavigationInfoChanged: (NavigationInfo info) {
setState(() {
_remainingMeters = info.remainingDistance.round();
_remainingSeconds = info.remainingTime.round();
_currentInstruction = info.nextStep?.navInstructions.instructions;
});
},
// Stop
ElevatedButton(
onPressed: () => mapManager.stopNavigation(),
child: Text('Stop'),
)
Troubleshooting
| Issue | Solution |
|---|---|
startNavigationWithCurrentItinerary does nothing | Add an itinerary first (Itinerary); ensure onItineraryAdded fired |
startNavigationFromUserLocation fails | Grant location (GPS) or camera (VPS) permission before calling—see GPS and VPS |
| Navigation stops unexpectedly | Check onNavigationFailed for error; may be due to positioning loss or connectivity |
| Instructions not announced | Ensure navigationWidgetEnabled or your custom UI handles onNavigationInfoChanged |