A Flutter package for decoding polyline points from Google Maps Directions API and the new Google Routes API. This package provides a unified interface supporting both legacy Directions API and the enhanced Google Routes API.
Version 3.0 introduces a simplified and unified approach to Google's routing services:
- Single
PolylinePointsclass for both APIs - Simplified Routes API integration with essential features
- Enhanced request/response models with better type safety
- Custom body parameters for advanced use cases
- Comprehensive test coverage for reliability
- Backward compatibility maintained for existing code
Add this to your package's pubspec.yaml file:
dependencies:
flutter_polyline_points: ^3.0.1Then run:
flutter pub get- Go to the Google Cloud Console
- Enable the Directions API and/or Routes API
- Create an API key
- Configure API key restrictions as needed
Note: The Routes API may have different pricing than the Directions API. Check the Google Routes API documentation for details.
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
// Initialize PolylinePoints
PolylinePoints polylinePoints = PolylinePoints(apiKey: "YOUR_API_KEY");
// Get route using legacy Directions API
PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
request: PolylineRequest(
origin: PointLatLng(37.7749, -122.4194), // San Francisco
destination: PointLatLng(37.3382, -121.8863), // San Jose
mode: TravelMode.driving,
),
);
if (result.points.isNotEmpty) {
// Convert to LatLng for Google Maps
List<LatLng> polylineCoordinates = result.points
.map((point) => LatLng(point.latitude, point.longitude))
.toList();
}import 'package:flutter_polyline_points/flutter_polyline_points.dart';
// Initialize PolylinePoints
PolylinePoints polylinePoints = PolylinePoints(apiKey: "YOUR_API_KEY");
// Create Routes API request
RoutesApiRequest request = RoutesApiRequest(
origin: PointLatLng(37.7749, -122.4194),
destination: PointLatLng(37.3382, -121.8863),
travelMode: TravelMode.driving,
routingPreference: RoutingPreference.trafficAware,
);
// Get route using Routes API
RoutesApiResponse response = await polylinePoints.getRouteBetweenCoordinatesV2(
request: request,
);
if (response.routes.isNotEmpty) {
Route route = response.routes.first;
// Access route information
print('Duration: ${route.durationMinutes} minutes');
print('Distance: ${route.distanceKm} km');
// Get polyline points
List<PointLatLng> points = route.polylinePoints ?? [];
}// Get optimized route for motorcycles/scooters
RoutesApiRequest request = RoutesApiRequest(
origin: PointLatLng(37.7749, -122.4194),
destination: PointLatLng(37.3382, -121.8863),
travelMode: TravelMode.twoWheeler,
routeModifiers: RouteModifiers(
avoidHighways: true,
avoidTolls: false,
),
);
RoutesApiResponse response = await polylinePoints.getRouteBetweenCoordinatesV2(
request: request,
);// Get multiple route options
RoutesApiRequest request = RoutesApiRequest(
origin: PointLatLng(37.7749, -122.4194),
destination: PointLatLng(37.3382, -121.8863),
computeAlternativeRoutes: true,
intermediates: [
PolylineWayPoint(location: "37.4419,-122.1430"), // Palo Alto coordinates
],
);
RoutesApiResponse response = await polylinePoints.getRouteBetweenCoordinatesV2(
request: request,
);
// Access all alternative routes
for (int i = 0; i < response.routes.length; i++) {
Route route = response.routes[i];
print('Route ${i + 1}: ${route.durationMinutes} min, ${route.distanceKm} km');
}RoutesApiRequest request = RoutesApiRequest(
origin: PointLatLng(37.7749, -122.4194),
destination: PointLatLng(37.3382, -121.8863),
travelMode: TravelMode.driving,
routeModifiers: RouteModifiers(
avoidTolls: true,
avoidHighways: false,
avoidFerries: true,
avoidIndoor: false,
),
routingPreference: RoutingPreference.trafficAware,
units: Units.metric,
polylineQuality: PolylineQuality.highQuality,
);
RoutesApiResponse response = await polylinePoints.getRouteBetweenCoordinatesV2(
request: request,
);// Add custom parameters not covered by the standard API
RoutesApiRequest request = RoutesApiRequest(
origin: PointLatLng(37.7749, -122.4194),
destination: PointLatLng(37.3382, -121.8863),
customBodyParameters: {
'extraComputations': ['TRAFFIC_ON_POLYLINE'],
'requestedReferenceTime': DateTime.now().toIso8601String(),
},
);RoutesApiRequest request = RoutesApiRequest(
origin: PointLatLng(37.7749, -122.4194),
destination: PointLatLng(37.3382, -121.8863),
travelMode: TravelMode.driving,
routingPreference: RoutingPreference.trafficAware,
departureTime: DateTime.now().add(Duration(hours: 1)),
// OR
// arrivalTime: DateTime.now().add(Duration(hours: 2)),
);You can pass custom HTTP headers with your Routes API requests. This is particularly useful for Android-restricted API keys:
RoutesApiRequest request = RoutesApiRequest(
origin: PointLatLng(37.7749, -122.4194),
destination: PointLatLng(37.3382, -121.8863),
travelMode: TravelMode.driving,
headers: {
'X-Android-Package': 'com.example.myapp',
'X-Android-Cert': 'YOUR_SHA1_FINGERPRINT',
// Add any other custom headers
},
);For easier Android header management, you can use the google_api_headers package:
dependencies:
flutter_polyline_points: ^3.0.0
google_api_headers: ^4.0.0import 'package:google_api_headers/google_api_headers.dart';
// Get Android-specific headers
Map<String, String> headers = await const GoogleApiHeaders().getHeaders();
RoutesApiRequest request = RoutesApiRequest(
origin: PointLatLng(37.7749, -122.4194),
destination: PointLatLng(37.3382, -121.8863),
travelMode: TravelMode.driving,
headers: headers, // Use the generated headers
);
RoutesApiResponse response = await polylinePoints.getRouteBetweenCoordinatesV2(
request: request,
);Note: Custom headers are only supported with the Routes API (getRouteBetweenCoordinatesV2). The legacy Directions API uses GET requests and doesn't support custom headers.
Version 3.0 simplifies the API while maintaining backward compatibility:
// OLD (v2.x)
PolylinePoints polylinePoints = PolylinePoints();
PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
googleApiKey: "YOUR_API_KEY",
request: request,
);
// NEW (v3.0)
PolylinePoints polylinePoints = PolylinePoints(apiKey: "YOUR_API_KEY");
PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
request: request,
);// Convert Routes API response to legacy format
RoutesApiResponse routesResponse = await polylinePoints.getRouteBetweenCoordinatesV2(
request: routesRequest,
);
PolylineResult legacyResult = polylinePoints.convertToLegacyResult(routesResponse);// Optimized for legacy API
PolylinePoints legacyPoints = PolylinePoints.legacy("YOUR_API_KEY");
// Optimized for Routes API
PolylinePoints enhancedPoints = PolylinePoints.enhanced("YOUR_API_KEY");
// Custom configuration
PolylinePoints customPoints = PolylinePoints.custom(
apiKey: "YOUR_API_KEY",
timeout: Duration(seconds: 60),
preferRoutesApi: true,
);class PolylineResult {
List<PointLatLng> points;
String? errorMessage;
String? status;
}class RoutesApiResponse {
List<Route> routes;
String? status;
String? errorMessage;
}
class Route {
int? duration; // Duration in seconds
int? staticDuration; // Static duration in seconds
int? distanceMeters; // Distance in meters
String? polylineEncoded; // Encoded polyline string
List<PointLatLng>? polylinePoints; // Decoded polyline points
// Convenience getters
double? get durationMinutes => duration != null ? duration! / 60.0 : null;
double? get staticDurationMinutes => staticDuration != null ? staticDuration! / 60.0 : null;
double? get distanceKm => distanceMeters != null ? distanceMeters! / 1000.0 : null;
}| Feature | Legacy Directions API | Routes API |
|---|---|---|
| Basic routing | β | β |
| Waypoints | β | β |
| Travel modes | Driving, Walking, Bicycling, Transit | + Two-Wheeler |
| Alternative routes | β | β |
| Route modifiers | Basic | Enhanced |
| Polyline quality | Standard | High-quality, Overview |
| Request format | GET with query params | POST with JSON |
| Custom parameters | β | β |
| Timing preferences | β | β |
| Field mask support | β | β |
-
API Key Issues
- Ensure your API key has the correct APIs enabled
- Check API key restrictions and quotas
- Verify billing is enabled for your Google Cloud project
-
Routes API Errors
- The Routes API requires different permissions than Directions API
- Check the Routes API documentation for requirements
-
Migration Issues
- Update constructor calls to include
apiKeyparameter - Use
convertToLegacyResult()for compatibility - Check method signatures for parameter changes
- Update constructor calls to include
try {
RoutesApiResponse response = await polylinePoints.getRouteBetweenCoordinatesV2(
request: RoutesApiRequest(
origin: PointLatLng(37.7749, -122.4194),
destination: PointLatLng(37.3382, -121.8863),
),
);
if (response.routes.isNotEmpty) {
// Success
Route route = response.routes.first;
} else {
print('Error: ${response.errorMessage ?? "No routes found"}');
}
} catch (e) {
print('Exception: $e');
}Check out the /example folder for comprehensive examples:
- Legacy API Example: Basic routing with backward compatibility
- Routes API Example: Enhanced features and custom parameters
- Two-Wheeler Example: Motorcycle/scooter optimized routing
- Advanced Configuration: Custom body parameters and timing preferences
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
This project is licensed under the MIT License - see the LICENSE file for details.
- Google Directions API Documentation
- Google Routes API Documentation
- Package on pub.dev
- GitHub Repository
- π BREAKING: Simplified API with unified
PolylinePointsclass - π BREAKING: Constructor now requires
apiKeyparameter - β¨ Enhanced Routes API integration with
RoutesApiRequest/RoutesApiResponse - π οΈ Added custom body parameters support
- ποΈ Added two-wheeler routing mode
- β° Added timing preferences (departure/arrival time)
- π― Added field mask support for response optimization
- π§ͺ Comprehensive test coverage added
- π Improved response models with convenience getters
- π§ Better error handling and type safety
- π οΈ Maintained backward compatibility for legacy API
See CHANGELOG.md for complete version history.