Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 36 additions & 12 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,14 @@ class MainScreenState extends State<MainScreen> with TickerProviderStateMixin {

// Legacy API Example (Backward Compatibility)
class LegacyMapScreen extends StatefulWidget {
final String apiKey;
final String? apiKey;
final Uri? proxy;

const LegacyMapScreen({super.key, required this.apiKey});
const LegacyMapScreen({
super.key,
this.proxy,
this.apiKey,
});

@override
LegacyMapScreenState createState() => LegacyMapScreenState();
Expand All @@ -99,7 +104,7 @@ class LegacyMapScreenState extends State<LegacyMapScreen> {
@override
void initState() {
super.initState();
polylinePoints = PolylinePoints(apiKey: widget.apiKey);
polylinePoints = PolylinePoints(proxy: widget.proxy, apiKey: widget.apiKey);

_addMarker(LatLng(_originLatitude, _originLongitude), "origin",
BitmapDescriptor.defaultMarker);
Expand Down Expand Up @@ -246,9 +251,15 @@ class LegacyMapScreenState extends State<LegacyMapScreen> {

// Custom Headers Example (Android-restricted API keys)
class CustomHeadersMapScreen extends StatefulWidget {
final String apiKey;
final String? apiKey;

const CustomHeadersMapScreen({super.key, required this.apiKey});
final Uri? proxy;

const CustomHeadersMapScreen({
super.key,
this.proxy,
this.apiKey,
});

@override
CustomHeadersMapScreenState createState() => CustomHeadersMapScreenState();
Expand All @@ -274,7 +285,8 @@ class CustomHeadersMapScreenState extends State<CustomHeadersMapScreen> {
@override
void initState() {
super.initState();
polylinePoints = PolylinePoints.enhanced(widget.apiKey);
polylinePoints =
PolylinePoints.enhanced(proxy: widget.proxy, apiKey: widget.apiKey);
_addMarker(LatLng(_originLatitude, _originLongitude), "origin",
BitmapDescriptor.defaultMarker);
_addMarker(LatLng(_destLatitude, _destLongitude), "destination",
Expand Down Expand Up @@ -497,9 +509,15 @@ class CustomHeadersMapScreenState extends State<CustomHeadersMapScreen> {

// Routes API Example (Enhanced Features)
class RoutesApiMapScreen extends StatefulWidget {
final String apiKey;
final String? apiKey;

final Uri? proxy;

const RoutesApiMapScreen({super.key, required this.apiKey});
const RoutesApiMapScreen({
super.key,
this.proxy,
this.apiKey,
});

@override
RoutesApiMapScreenState createState() => RoutesApiMapScreenState();
Expand All @@ -520,7 +538,8 @@ class RoutesApiMapScreenState extends State<RoutesApiMapScreen> {
@override
void initState() {
super.initState();
polylinePoints = PolylinePoints.enhanced(widget.apiKey);
polylinePoints =
PolylinePoints.enhanced(proxy: widget.proxy, apiKey: widget.apiKey);
_addMarker(LatLng(_originLatitude, _originLongitude), "origin",
BitmapDescriptor.defaultMarker);
_addMarker(LatLng(_destLatitude, _destLongitude), "destination",
Expand Down Expand Up @@ -762,9 +781,11 @@ class RoutesApiMapScreenState extends State<RoutesApiMapScreen> {

// Two-Wheeler Example (Routes API Exclusive Feature)
class TwoWheelerMapScreen extends StatefulWidget {
final String apiKey;
final String? apiKey;

const TwoWheelerMapScreen({super.key, required this.apiKey});
final Uri? proxy;

const TwoWheelerMapScreen({super.key, this.proxy, this.apiKey});

@override
TwoWheelerMapScreenState createState() => TwoWheelerMapScreenState();
Expand All @@ -786,7 +807,10 @@ class TwoWheelerMapScreenState extends State<TwoWheelerMapScreen> {
@override
void initState() {
super.initState();
polylinePointsV2 = PolylinePoints.enhanced(widget.apiKey);
polylinePointsV2 = PolylinePoints.enhanced(
proxy: widget.proxy,
apiKey: widget.apiKey,
);
_addMarker(LatLng(_originLatitude, _originLongitude), "origin",
BitmapDescriptor.defaultMarker);
_addMarker(LatLng(_destLatitude, _destLongitude), "destination",
Expand Down
59 changes: 47 additions & 12 deletions lib/src/network/network_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,27 @@ class NetworkProvider {
/// Get route using legacy Directions API (for backward compatibility)
/// Supports only basic features
///
/// @param [proxy] - Proxy the request to hide the google key application on your requests parameters to prevent bad actors from reaching and using the key exposed in the code.
/// @param [apiKey] - Google Maps API key
/// @param [PolylineRequest] - PolylineRequest object
/// @param [timeout] - Optional timeout for the request
/// @return [PolylineResult] object with decoded points
static Future<PolylineResult> getRouteBetweenCoordinates(
String googleApiKey,
Uri? proxy,
String? googleApiKey,
PolylineRequest request, {
Duration? timeout,
}) async {
final uri = _buildDirectionsUri(googleApiKey, request);
assert(
(proxy == null && googleApiKey != null && googleApiKey.isNotEmpty) ||
(proxy != null && googleApiKey == null),
"Google API Key cannot be empty if proxy isn't provided");

final uri = _buildDirectionsUri(
proxy: proxy,
apiKey: googleApiKey,
request: request,
);

try {
final response = await http
Expand All @@ -62,19 +73,26 @@ class NetworkProvider {
/// Get route using new Routes API with enhanced features
/// Supports all Routes API features
///
/// @param [proxy] - Proxy the request to hide the google key application on your requests parameters to prevent bad actors from reaching and using the key exposed in the code.
/// @param [apiKey] - Google Maps API key
/// @param [RoutesApiRequest] - RoutesApiRequest object
/// @param [timeout] - Optional timeout for the request
/// @return [RoutesApiResponse] object with decoded points
static Future<RoutesApiResponse> getRouteBetweenCoordinatesV2(
String googleApiKey,
Uri? proxy,
String? googleApiKey,
RoutesApiRequest request, {
Duration? timeout,
}) async {
assert(
(proxy == null && googleApiKey != null && googleApiKey.isNotEmpty) ||
(proxy != null && googleApiKey == null),
"Google API Key cannot be empty if proxy isn't provided");

try {
final response = await http
.post(
Uri.parse(_routesBaseUrl),
proxy ?? Uri.parse(_routesBaseUrl),
headers: _getRoutesHeaders(googleApiKey, request),
body: json.encode(request.toJson()),
)
Expand All @@ -97,14 +115,22 @@ class NetworkProvider {
}

/// Build URI for legacy Directions API
static Uri _buildDirectionsUri(String apiKey, PolylineRequest request) {
static Uri _buildDirectionsUri({
Uri? proxy,
String? apiKey,
required PolylineRequest request,
}) {
final queryParams = <String, String>{
'key': apiKey,
'origin': _formatLocation(request.origin),
'destination': _formatLocation(request.destination),
'mode': request.mode.name,
};

// Adds Api key to parameters if given.
if (apiKey != null) {
queryParams.addAll({'key': apiKey});
}

// Add waypoints if present
if (request.wayPoints.isNotEmpty) {
final waypoints = request.wayPoints
Expand Down Expand Up @@ -133,7 +159,8 @@ class NetworkProvider {
queryParams['optimize'] = 'true';
}

return Uri.parse(_directionsBaseUrl).replace(queryParameters: queryParams);
return (proxy ?? Uri.parse(_directionsBaseUrl))
.replace(queryParameters: queryParams);
}

/// Format location for API request
Expand Down Expand Up @@ -163,16 +190,21 @@ class NetworkProvider {

/// Get headers for Routes API
static Map<String, String> _getRoutesHeaders(
String apiKey,
String? apiKey,
RoutesApiRequest request,
) {
final headers = {
'Content-Type': 'application/json',
'X-Goog-Api-Key': apiKey,
'X-Goog-FieldMask': request.getFieldMask(),
'User-Agent': 'flutter_polyline_points/3.0.0',
};

if (apiKey != null) {
headers.addAll({
'X-Goog-Api-Key': apiKey,
});
}

// Add optional headers
if (request.languageCode != null) {
headers['Accept-Language'] = request.languageCode!;
Expand Down Expand Up @@ -238,7 +270,10 @@ class NetworkProvider {
}

/// Check API availability and capabilities
static Future<Map<String, bool>> checkApiAvailability(String apiKey) async {
static Future<Map<String, bool>> checkApiAvailability(
Uri? proxy,
String? apiKey,
) async {
final results = <String, bool>{};

// Test Directions API
Expand All @@ -249,7 +284,7 @@ class NetworkProvider {
mode: TravelMode.driving,
);

await getRouteBetweenCoordinates(apiKey, testRequest,
await getRouteBetweenCoordinates(proxy, apiKey, testRequest,
timeout: Duration(seconds: 10));
results['directions_api'] = true;
} catch (e) {
Expand All @@ -263,7 +298,7 @@ class NetworkProvider {
destination: PointLatLng(37.7849, -122.4094),
);

await getRouteBetweenCoordinatesV2(apiKey, testRequest,
await getRouteBetweenCoordinatesV2(proxy, apiKey, testRequest,
timeout: Duration(seconds: 10));
results['routes_api'] = true;
} catch (e) {
Expand Down
35 changes: 28 additions & 7 deletions lib/src/polyline_points.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ import 'routes_api/routes_response.dart';
/// Provides backward compatibility while enabling access to advanced routing features
// ignore_for_file: deprecated_member_use_from_same_package
class PolylinePoints {
/// Proxy for circumventing exposing your API key in your code.
final Uri? proxy;

/// Google API key for accessing routing services
final String apiKey;
final String? apiKey;

/// Default timeout for API requests
final Duration defaultTimeout;
Expand All @@ -25,34 +28,50 @@ class PolylinePoints {

/// Create a new PolylinePointsV2 instance
PolylinePoints({
required this.apiKey,
this.proxy,
this.apiKey,
this.defaultTimeout = const Duration(seconds: 30),
this.preferRoutesApi = true,
});
}) {
assert(
(proxy == null && apiKey != null && apiKey!.isNotEmpty) ||
(proxy != null && apiKey == null),
"Google API Key cannot be empty if proxy isn't provided");
}

/// Create instance optimized for legacy API usage
factory PolylinePoints.legacy(String apiKey) {
factory PolylinePoints.legacy({
Uri? proxy,
String? apiKey,
}) {
return PolylinePoints(
proxy: proxy,
apiKey: apiKey,
preferRoutesApi: false,
);
}

/// Create instance optimized for Routes API usage
factory PolylinePoints.enhanced(String apiKey) {
factory PolylinePoints.enhanced({
Uri? proxy,
String? apiKey,
}) {
return PolylinePoints(
proxy: proxy,
apiKey: apiKey,
preferRoutesApi: true,
);
}

/// Create instance with custom configuration
factory PolylinePoints.custom({
required String apiKey,
Uri? proxy,
String? apiKey,
Duration? timeout,
bool? preferRoutesApi,
}) {
return PolylinePoints(
proxy: proxy,
apiKey: apiKey,
defaultTimeout: timeout ?? const Duration(seconds: 30),
preferRoutesApi: preferRoutesApi ?? true,
Expand All @@ -71,6 +90,7 @@ class PolylinePoints {
Duration? timeout,
}) async {
return NetworkProvider.getRouteBetweenCoordinates(
proxy,
apiKey,
request,
timeout: timeout ?? defaultTimeout,
Expand All @@ -94,6 +114,7 @@ class PolylinePoints {
Duration? timeout,
}) async {
return NetworkProvider.getRouteBetweenCoordinatesV2(
proxy,
apiKey,
request,
timeout: timeout ?? defaultTimeout,
Expand Down Expand Up @@ -127,7 +148,7 @@ class PolylinePoints {

/// Check which APIs are available with the current API key
Future<Map<String, bool>> checkApiAvailability() async {
return NetworkProvider.checkApiAvailability(apiKey);
return NetworkProvider.checkApiAvailability(proxy, apiKey);
}

/// Get API usage recommendation for a given request
Expand Down
Loading