From 320e18fd3a6ddefb02de770eb68d5e0c4b00b30d Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Tue, 12 Aug 2025 14:53:34 +0300 Subject: [PATCH 01/19] feat: upgrade android sdk from 6.2.2 to 6.3.1 --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 5eafe88c..baa1e8a1 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -77,7 +77,7 @@ android { dependencies { implementation 'androidx.car.app:app:1.4.0' implementation 'androidx.car.app:app-projected:1.4.0' - implementation 'com.google.android.libraries.navigation:navigation:6.2.2' + implementation 'com.google.android.libraries.navigation:navigation:6.3.1' testImplementation 'org.jetbrains.kotlin:kotlin-test' testImplementation 'io.mockk:mockk:1.13.8' testImplementation 'junit:junit:4.13.2' From bfc9fc276819de79536c58b4f15676c75e978afa Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Tue, 12 Aug 2025 14:54:00 +0300 Subject: [PATCH 02/19] feat: upgrade iOS SDK from 10.0.0 to 10.1.0 --- ios/google_navigation_flutter.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/google_navigation_flutter.podspec b/ios/google_navigation_flutter.podspec index 342bb61e..c9498df6 100644 --- a/ios/google_navigation_flutter.podspec +++ b/ios/google_navigation_flutter.podspec @@ -15,7 +15,7 @@ A Google Maps Navigation Flutter plugin. s.source = { :path => '.' } s.source_files = 'google_navigation_flutter/Sources/google_navigation_flutter/**/*.swift' s.dependency 'Flutter' - s.dependency 'GoogleNavigation', '10.0.0' + s.dependency 'GoogleNavigation', '10.1.0' s.platform = :ios, '16.0' s.static_framework = true From 4b0be7d0b5114a18c60592416789500c08262af0 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Wed, 13 Aug 2025 09:54:46 +0300 Subject: [PATCH 03/19] feat: add new features --- .../navigation/GoogleMapsNavigationView.kt | 20 ++++ .../GoogleMapsViewMessageHandler.kt | 8 ++ .../maps/flutter/navigation/messages.g.kt | 74 +++++++++++++ .../t04_navigation_ui_test.dart | 46 ++++++++ example/lib/pages/navigation.dart | 48 +++++++++ .../GoogleMapsNavigationSessionManager.swift | 10 ++ .../GoogleMapsNavigationView.swift | 22 ++++ ...ogleMapsNavigationViewMessageHandler.swift | 8 ++ .../GoogleMapsNavigationViewRegistry.swift | 10 ++ .../messages.g.swift | 64 +++++++++++ lib/src/google_maps_navigation_view.dart | 11 ++ ...oogle_maps_navigation_view_controller.dart | 26 +++++ lib/src/google_navigation_flutter.dart | 3 + lib/src/method_channel/map_view_api.dart | 27 +++++ lib/src/method_channel/messages.g.dart | 100 ++++++++++++++++++ lib/src/types/navigation_view_types.dart | 10 ++ pigeons/messages.dart | 4 + pubspec.yaml | 1 + test/google_navigation_flutter_test.dart | 79 ++++++++++++++ .../google_navigation_flutter_test.mocks.dart | 14 +++ test/incident_reporting_test.dart | 1 + test/messages_test.g.dart | 88 +++++++++++++++ 22 files changed, 674 insertions(+) create mode 100644 test/incident_reporting_test.dart diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationView.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationView.kt index d4ee0ccf..09268f80 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationView.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationView.kt @@ -22,6 +22,7 @@ import android.view.View import com.google.android.gms.maps.CameraUpdateFactory import com.google.android.libraries.navigation.NavigationView import com.google.android.libraries.navigation.OnNavigationUiChangedListener +import com.google.android.libraries.navigation.PromptVisibilityChangedListener import io.flutter.plugin.platform.PlatformView class GoogleMapsNavigationView @@ -51,6 +52,8 @@ internal constructor( null private var _onNavigationUIEnabledChanged: OnNavigationUiChangedListener? = null + private var _onPromptVisibilityChanged: PromptVisibilityChangedListener? = null + override fun getView(): View { return _navigationView } @@ -110,6 +113,10 @@ internal constructor( _navigationView.removeOnNavigationUiChangedListener(_onNavigationUIEnabledChanged) _onNavigationUIEnabledChanged = null } + if (_onPromptVisibilityChanged != null) { + _navigationView.removePromptVisibilityChangedListener(_onPromptVisibilityChanged) + _onPromptVisibilityChanged = null + } // When view is disposed, all of these lifecycle functions must be // called to properly dispose navigation view and prevent leaks. @@ -171,6 +178,11 @@ internal constructor( } _navigationView.addOnNavigationUiChangedListener(_onNavigationUIEnabledChanged) + _onPromptVisibilityChanged = PromptVisibilityChangedListener { promptVisible -> + viewEventApi?.onPromptVisibilityChanged(getViewId().toLong(), promptVisible) {} + } + _navigationView.addPromptVisibilityChangedListener(_onPromptVisibilityChanged) + super.initListeners() } @@ -246,6 +258,14 @@ internal constructor( _isReportIncidentButtonEnabled = enabled } + fun isIncidentReportingAvailable(): Boolean { + return _navigationView.isIncidentReportingAvailable() + } + + fun showReportIncidentsPanel() { + _navigationView.showReportIncidentsPanel() + } + fun isTrafficPromptsEnabled(): Boolean { return _isTrafficPromptsEnabled } diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsViewMessageHandler.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsViewMessageHandler.kt index d0176148..850436db 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsViewMessageHandler.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsViewMessageHandler.kt @@ -368,6 +368,14 @@ class GoogleMapsViewMessageHandler(private val viewRegistry: GoogleMapsViewRegis getNavigationView(viewId.toInt()).setReportIncidentButtonEnabled(enabled) } + override fun isIncidentReportingAvailable(viewId: Long): Boolean { + return getNavigationView(viewId.toInt()).isIncidentReportingAvailable() + } + + override fun showReportIncidentsPanel(viewId: Long) { + getNavigationView(viewId.toInt()).showReportIncidentsPanel() + } + override fun isTrafficPromptsEnabled(viewId: Long): Boolean { return getNavigationView(viewId.toInt()).isTrafficPromptsEnabled() } diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt index 0de11556..4b7baf41 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt @@ -2797,6 +2797,10 @@ interface MapViewApi { fun setReportIncidentButtonEnabled(viewId: Long, enabled: Boolean) + fun isIncidentReportingAvailable(viewId: Long): Boolean + + fun showReportIncidentsPanel(viewId: Long) + fun getCameraPosition(viewId: Long): CameraPositionDto fun getVisibleRegion(viewId: Long): LatLngBoundsDto @@ -4120,6 +4124,53 @@ interface MapViewApi { channel.setMessageHandler(null) } } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_navigation_flutter.MapViewApi.isIncidentReportingAvailable$separatedMessageChannelSuffix", + codec, + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0] as Long + val wrapped: List = + try { + listOf(api.isIncidentReportingAvailable(viewIdArg)) + } catch (exception: Throwable) { + MessagesPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_navigation_flutter.MapViewApi.showReportIncidentsPanel$separatedMessageChannelSuffix", + codec, + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0] as Long + val wrapped: List = + try { + api.showReportIncidentsPanel(viewIdArg) + listOf(null) + } catch (exception: Throwable) { + MessagesPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel( @@ -5628,6 +5679,29 @@ class ViewEventApi( } } + fun onPromptVisibilityChanged( + viewIdArg: Long, + promptVisibleArg: Boolean, + callback: (Result) -> Unit, + ) { + val separatedMessageChannelSuffix = + if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = + "dev.flutter.pigeon.google_navigation_flutter.ViewEventApi.onPromptVisibilityChanged$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(viewIdArg, promptVisibleArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(MessagesPigeonUtils.createConnectionError(channelName))) + } + } + } + fun onMyLocationClicked(viewIdArg: Long, callback: (Result) -> Unit) { val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" diff --git a/example/integration_test/t04_navigation_ui_test.dart b/example/integration_test/t04_navigation_ui_test.dart index c3694534..b6273780 100644 --- a/example/integration_test/t04_navigation_ui_test.dart +++ b/example/integration_test/t04_navigation_ui_test.dart @@ -32,6 +32,9 @@ void main() { /// For testing NavigationUIEnabledChanged bool navigationUIisEnabled = false; + /// For testing PromptVisibilityChanged + bool? promptVisible; + await checkLocationDialogAndTosAcceptance($); /// The events are not tested because there's no reliable way to trigger them currently. @@ -41,6 +44,12 @@ void main() { $.log('Re-center button clicked event: $event.'); } + /// For testing PromptVisibilityChanged + void onPromptVisibilityChanged(bool promptVisible_) { + $.log('Prompt visibility changed event: $promptVisible_.'); + promptVisible = promptVisible_; + } + /// Display navigation view. final Key key = GlobalKey(); await pumpNavigationView( @@ -54,6 +63,7 @@ void main() { navigationUIisEnabled = isEnabled; }, onRecenterButtonClicked: onRecenterButtonClicked, + onPromptVisibilityChanged: onPromptVisibilityChanged, ), ); @@ -257,6 +267,42 @@ void main() { ); } + /// Test incident reporting availability. + final bool isIncidentReportingAvailable = + await viewController.isIncidentReportingAvailable(); + $.log('Incident reporting available: $isIncidentReportingAvailable'); + expect( + isIncidentReportingAvailable, + true, + reason: + 'Incident reporting should be available during navigation on tests.', + ); + + /// Test prompt visibility and incident reporting panel. + if (isIncidentReportingAvailable) { + // Reset prompt visibility state + promptVisible = null; + + $.log('Opening incident reporting panel...'); + await viewController.showReportIncidentsPanel(); + await $.pumpAndSettle(timeout: const Duration(seconds: 2)); + + /// Check if prompt visibility event was triggered + waitForValueMatchingPredicate( + $, + () async => promptVisible, + (bool? value) => value == true, + maxTries: 50, // Wait up to 5 seconds (50 * 100ms) + ); + + expect( + promptVisible, + true, + reason: + 'Prompt visibility should be true when incident panel is shown.', + ); + } + await GoogleMapsNavigator.cleanup(); }); } diff --git a/example/lib/pages/navigation.dart b/example/lib/pages/navigation.dart index 16fe3bd9..8cf6bee7 100644 --- a/example/lib/pages/navigation.dart +++ b/example/lib/pages/navigation.dart @@ -99,6 +99,7 @@ class _NavigationPageState extends ExamplePageState { int _onRemainingTimeOrDistanceChangedEventCallCount = 0; int _onNavigationUIEnabledChangedEventCallCount = 0; int _onNewNavigationSessionEventCallCount = 0; + int _onPromptVisibilityChangedEventCallCount = 0; bool _navigationHeaderEnabled = true; bool _navigationFooterEnabled = true; @@ -608,6 +609,15 @@ class _NavigationPageState extends ExamplePageState { } } + void _onPromptVisibilityChanged(bool promptVisible) { + if (mounted) { + setState(() { + _onPromptVisibilityChangedEventCallCount += 1; + }); + showMessage('Prompt visibility changed: $promptVisible'); + } + } + Future _startGuidedNavigation() async { assert(_navigationViewController != null); if (!_navigatorInitialized) { @@ -1203,6 +1213,7 @@ class _NavigationPageState extends ExamplePageState { _onRecenterButtonClickedEvent, onNavigationUIEnabledChanged: _onNavigationUIEnabledChanged, + onPromptVisibilityChanged: _onPromptVisibilityChanged, initialCameraPosition: CameraPosition( // Initialize map to user location. target: _userLocation!, @@ -1488,6 +1499,16 @@ class _NavigationPageState extends ExamplePageState { ), ), ), + Card( + child: ListTile( + title: const Text( + 'On prompt visibility changed event call count', + ), + trailing: Text( + _onPromptVisibilityChangedEventCallCount.toString(), + ), + ), + ), ], ), ); @@ -2001,6 +2022,33 @@ class _NavigationPageState extends ExamplePageState { ), child: const Text('Follow my location'), ), + ElevatedButton( + onPressed: () async { + final bool available = + await _navigationViewController! + .isIncidentReportingAvailable(); + if (available) { + await _navigationViewController! + .showReportIncidentsPanel(); + } else { + if (context.mounted) { + showMessage('Incident reporting is not available'); + } + } + }, + child: const Text('Report Incident'), + ), + ElevatedButton( + onPressed: () async { + final bool available = + await _navigationViewController! + .isIncidentReportingAvailable(); + showMessage('Incident reporting available: $available'); + }, + child: const Text( + 'Check incident reporting availability', + ), + ), ], ), const SizedBox(height: 10), diff --git a/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationSessionManager.swift b/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationSessionManager.swift index 41134c1f..05440346 100644 --- a/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationSessionManager.swift +++ b/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationSessionManager.swift @@ -621,4 +621,14 @@ extension GoogleMapsNavigationSessionManager: GMSNavigatorListener { ) } } + + func navigatorWillPresentPrompt(_ navigator: GMSNavigator) { + // Notify all navigation views about prompt visibility change + _viewRegistry?.sendPromptVisibilityChangedToAllViews(promptVisible: true) + } + + func navigatorDidDismissPrompt(_ navigator: GMSNavigator) { + // Notify all navigation views about prompt visibility change + _viewRegistry?.sendPromptVisibilityChangedToAllViews(promptVisible: false) + } } diff --git a/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationView.swift b/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationView.swift index a2e9016e..e753afd7 100644 --- a/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationView.swift +++ b/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationView.swift @@ -538,6 +538,20 @@ public class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettle _mapView.settings.isNavigationReportIncidentButtonEnabled = enabled } + func isIncidentReportingAvailable() -> Bool { + return _mapView.isIncidentReportingAvailable + } + + func showReportIncidentsPanel() throws { + Task { + do { + try await _mapView.presentReportIncidentsPanel(nil) + } catch { + // Handle error silently + } + } + } + func isTrafficPromptsEnabled() -> Bool { return _isTrafficPromptsEnabled } @@ -1025,6 +1039,14 @@ extension GoogleMapsNavigationView: GMSMapViewDelegate { ) } } + + func sendPromptVisibilityChangedEvent(promptVisible: Bool) { + getViewEventApi()?.onPromptVisibilityChanged( + viewId: _viewId!, + promptVisible: promptVisible, + completion: { _ in } + ) + } } extension MarkerDto { diff --git a/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationViewMessageHandler.swift b/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationViewMessageHandler.swift index c0acc666..e7055521 100644 --- a/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationViewMessageHandler.swift +++ b/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationViewMessageHandler.swift @@ -422,6 +422,14 @@ class GoogleMapsNavigationViewMessageHandler: MapViewApi { try getView(viewId).isReportIncidentButtonEnabled() } + func isIncidentReportingAvailable(viewId: Int64) throws -> Bool { + try getView(viewId).isIncidentReportingAvailable() + } + + func showReportIncidentsPanel(viewId: Int64) throws { + try getView(viewId).showReportIncidentsPanel() + } + func isTrafficPromptsEnabled(viewId: Int64) throws -> Bool { try getView(viewId).isTrafficPromptsEnabled() } diff --git a/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationViewRegistry.swift b/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationViewRegistry.swift index b298825b..d1c7bb82 100644 --- a/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationViewRegistry.swift +++ b/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationViewRegistry.swift @@ -92,4 +92,14 @@ class GoogleMapsNavigationViewRegistry { self.carPlayView } } + + func sendPromptVisibilityChangedToAllViews(promptVisible: Bool) { + queue.sync { + for view in views.values { + view.sendPromptVisibilityChangedEvent(promptVisible: promptVisible) + } + // Also send to CarPlay view if it exists + carPlayView?.sendPromptVisibilityChangedEvent(promptVisible: promptVisible) + } + } } diff --git a/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift b/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift index de9a469c..9c46089f 100644 --- a/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift +++ b/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift @@ -2554,6 +2554,8 @@ protocol MapViewApi { func setTrafficPromptsEnabled(viewId: Int64, enabled: Bool) throws func isReportIncidentButtonEnabled(viewId: Int64) throws -> Bool func setReportIncidentButtonEnabled(viewId: Int64, enabled: Bool) throws + func isIncidentReportingAvailable(viewId: Int64) throws -> Bool + func showReportIncidentsPanel(viewId: Int64) throws func getCameraPosition(viewId: Int64) throws -> CameraPositionDto func getVisibleRegion(viewId: Int64) throws -> LatLngBoundsDto func followMyLocation(viewId: Int64, perspective: CameraPerspectiveDto, zoomLevel: Double?) throws @@ -3529,6 +3531,42 @@ class MapViewApiSetup { } else { setReportIncidentButtonEnabledChannel.setMessageHandler(nil) } + let isIncidentReportingAvailableChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.google_navigation_flutter.MapViewApi.isIncidentReportingAvailable\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + isIncidentReportingAvailableChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let viewIdArg = args[0] as! Int64 + do { + let result = try api.isIncidentReportingAvailable(viewId: viewIdArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + isIncidentReportingAvailableChannel.setMessageHandler(nil) + } + let showReportIncidentsPanelChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.google_navigation_flutter.MapViewApi.showReportIncidentsPanel\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + showReportIncidentsPanelChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let viewIdArg = args[0] as! Int64 + do { + try api.showReportIncidentsPanel(viewId: viewIdArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + showReportIncidentsPanelChannel.setMessageHandler(nil) + } let getCameraPositionChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.google_navigation_flutter.MapViewApi.getCameraPosition\(channelSuffix)", @@ -4564,6 +4602,9 @@ protocol ViewEventApiProtocol { func onNavigationUIEnabledChanged( viewId viewIdArg: Int64, navigationUIEnabled navigationUIEnabledArg: Bool, completion: @escaping (Result) -> Void) + func onPromptVisibilityChanged( + viewId viewIdArg: Int64, promptVisible promptVisibleArg: Bool, + completion: @escaping (Result) -> Void) func onMyLocationClicked( viewId viewIdArg: Int64, completion: @escaping (Result) -> Void) func onMyLocationButtonClicked( @@ -4791,6 +4832,29 @@ class ViewEventApi: ViewEventApiProtocol { } } } + func onPromptVisibilityChanged( + viewId viewIdArg: Int64, promptVisible promptVisibleArg: Bool, + completion: @escaping (Result) -> Void + ) { + let channelName: String = + "dev.flutter.pigeon.google_navigation_flutter.ViewEventApi.onPromptVisibilityChanged\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([viewIdArg, promptVisibleArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } func onMyLocationClicked( viewId viewIdArg: Int64, completion: @escaping (Result) -> Void ) { diff --git a/lib/src/google_maps_navigation_view.dart b/lib/src/google_maps_navigation_view.dart index 39e5e460..a53517f2 100644 --- a/lib/src/google_maps_navigation_view.dart +++ b/lib/src/google_maps_navigation_view.dart @@ -78,6 +78,7 @@ class GoogleMapsNavigationView extends GoogleMapsBaseMapView { super.onPolylineClicked, super.onCircleClicked, this.onNavigationUIEnabledChanged, + this.onPromptVisibilityChanged, super.onMyLocationClicked, super.onMyLocationButtonClicked, super.onCameraMoveStarted, @@ -136,6 +137,9 @@ class GoogleMapsNavigationView extends GoogleMapsBaseMapView { /// On navigation UI enabled changed callback. final OnNavigationUIEnabledChanged? onNavigationUIEnabledChanged; + /// On prompt visibility changed callback. + final OnPromptVisibilityChanged? onPromptVisibilityChanged; + /// Creates a [State] for this [GoogleMapsNavigationView]. @override State createState() => GoogleMapsNavigationViewState(); @@ -214,6 +218,13 @@ class GoogleMapsNavigationViewState ); }); } + if (widget.onPromptVisibilityChanged != null) { + GoogleMapsNavigationPlatform.instance.viewAPI + .getPromptVisibilityChangedEventStream(viewId: viewId) + .listen((PromptVisibilityChangedEvent event) { + widget.onPromptVisibilityChanged?.call(event.promptVisible); + }); + } if (widget.onMyLocationButtonClicked != null) { GoogleMapsNavigationPlatform.instance.viewAPI .getMyLocationButtonClickedEventStream(viewId: viewId) diff --git a/lib/src/google_maps_navigation_view_controller.dart b/lib/src/google_maps_navigation_view_controller.dart index c20ea18a..ad4c05c8 100644 --- a/lib/src/google_maps_navigation_view_controller.dart +++ b/lib/src/google_maps_navigation_view_controller.dart @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'package:meta/meta.dart'; + import '../google_navigation_flutter.dart'; import 'google_navigation_flutter_platform_interface.dart'; @@ -33,6 +35,8 @@ class GoogleNavigationViewController extends GoogleMapViewController { /// Enable or disable the navigation trip progress bar. /// /// By default, the navigation trip progress bar is disabled. + /// This feature is experimental and may change in the future. + @experimental Future setNavigationTripProgressBarEnabled(bool enabled) { return GoogleMapsNavigationPlatform.instance.viewAPI .setNavigationTripProgressBarEnabled( @@ -131,6 +135,28 @@ class GoogleNavigationViewController extends GoogleMapViewController { .setReportIncidentButtonEnabled(viewId: getViewId(), enabled: enabled); } + /// Checks if incident reporting is currently available. + /// + /// Returns true if the user can report incidents at the current time, + /// false otherwise. + /// This feature is experimental and may change in the future. + @experimental + Future isIncidentReportingAvailable() { + return GoogleMapsNavigationPlatform.instance.viewAPI + .isIncidentReportingAvailable(viewId: getViewId()); + } + + /// Presents a panel allowing users to report an incident. + /// + /// This method displays the incident reporting UI where users can select + /// and report various types of incidents along the route. + /// This feature is experimental and may change in the future. + @experimental + Future showReportIncidentsPanel() { + return GoogleMapsNavigationPlatform.instance.viewAPI + .showReportIncidentsPanel(viewId: getViewId()); + } + /// Are the traffic prompts shown. Future isTrafficPromptsEnabled() { return GoogleMapsNavigationPlatform.instance.viewAPI diff --git a/lib/src/google_navigation_flutter.dart b/lib/src/google_navigation_flutter.dart index dc92d3cf..1a4d72cd 100644 --- a/lib/src/google_navigation_flutter.dart +++ b/lib/src/google_navigation_flutter.dart @@ -110,6 +110,9 @@ typedef OnCircleClicked = void Function(String circleId); /// Called when the [GoogleNavigationViewController.isNavigationUIEnabled] status changes. typedef OnNavigationUIEnabledChanged = void Function(bool navigationUIEnabled); +/// Called when the visibility of navigation prompts changes. +typedef OnPromptVisibilityChanged = void Function(bool promptVisible); + /// Called during my location clicked event. typedef OnMyLocationClicked = void Function(MyLocationClickedEvent); diff --git a/lib/src/method_channel/map_view_api.dart b/lib/src/method_channel/map_view_api.dart index b6a9e007..b8333adb 100644 --- a/lib/src/method_channel/map_view_api.dart +++ b/lib/src/method_channel/map_view_api.dart @@ -17,6 +17,7 @@ import 'dart:io'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; +import 'package:meta/meta.dart'; import '../../google_navigation_flutter.dart'; import '../google_navigation_flutter_platform_interface.dart'; @@ -687,6 +688,18 @@ class MapViewAPIImpl { return _viewApi.setReportIncidentButtonEnabled(viewId, enabled); } + /// Checks if incident reporting is currently available. + @experimental + Future isIncidentReportingAvailable({required int viewId}) { + return _viewApi.isIncidentReportingAvailable(viewId); + } + + /// Presents a panel allowing users to report an incident. + @experimental + Future showReportIncidentsPanel({required int viewId}) { + return _viewApi.showReportIncidentsPanel(viewId); + } + /// Are the traffic prompts displayed. Future isTrafficPromptsEnabled({required int viewId}) { return _viewApi.isTrafficPromptsEnabled(viewId); @@ -1224,6 +1237,13 @@ class MapViewAPIImpl { return _unwrapEventStream(viewId: viewId); } + /// Get prompt visibility changed event stream from the navigation view. + Stream getPromptVisibilityChangedEventStream({ + required int viewId, + }) { + return _unwrapEventStream(viewId: viewId); + } + /// Get navigation view my location clicked event stream from the navigation view. Stream getMyLocationClickedEventStream({ required int viewId, @@ -1331,6 +1351,13 @@ class ViewEventApiImpl implements ViewEventApi { ); } + @override + void onPromptVisibilityChanged(int viewId, bool promptVisible) { + _viewEventStreamController.add( + _ViewIdEventWrapper(viewId, PromptVisibilityChangedEvent(promptVisible)), + ); + } + @override void onMyLocationClicked(int viewId) { _viewEventStreamController.add( diff --git a/lib/src/method_channel/messages.g.dart b/lib/src/method_channel/messages.g.dart index fcb40666..3dd852ad 100644 --- a/lib/src/method_channel/messages.g.dart +++ b/lib/src/method_channel/messages.g.dart @@ -4470,6 +4470,65 @@ class MapViewApi { } } + Future isIncidentReportingAvailable(int viewId) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.google_navigation_flutter.MapViewApi.isIncidentReportingAvailable$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [viewId], + ); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } + + Future showReportIncidentsPanel(int viewId) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.google_navigation_flutter.MapViewApi.showReportIncidentsPanel$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [viewId], + ); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + Future getCameraPosition(int viewId) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.google_navigation_flutter.MapViewApi.getCameraPosition$pigeonVar_messageChannelSuffix'; @@ -6111,6 +6170,8 @@ abstract class ViewEventApi { void onNavigationUIEnabledChanged(int viewId, bool navigationUIEnabled); + void onPromptVisibilityChanged(int viewId, bool promptVisible); + void onMyLocationClicked(int viewId); void onMyLocationButtonClicked(int viewId); @@ -6499,6 +6560,45 @@ abstract class ViewEventApi { }); } } + { + final BasicMessageChannel + pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.google_navigation_flutter.ViewEventApi.onPromptVisibilityChanged$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert( + message != null, + 'Argument for dev.flutter.pigeon.google_navigation_flutter.ViewEventApi.onPromptVisibilityChanged was null.', + ); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert( + arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_navigation_flutter.ViewEventApi.onPromptVisibilityChanged was null, expected non-null int.', + ); + final bool? arg_promptVisible = (args[1] as bool?); + assert( + arg_promptVisible != null, + 'Argument for dev.flutter.pigeon.google_navigation_flutter.ViewEventApi.onPromptVisibilityChanged was null, expected non-null bool.', + ); + try { + api.onPromptVisibilityChanged(arg_viewId!, arg_promptVisible!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } + }); + } + } { final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( diff --git a/lib/src/types/navigation_view_types.dart b/lib/src/types/navigation_view_types.dart index 2632a575..8ad6e4da 100644 --- a/lib/src/types/navigation_view_types.dart +++ b/lib/src/types/navigation_view_types.dart @@ -151,6 +151,16 @@ class NavigationUIEnabledChangedEvent { ')'; } +/// Represents prompt visibility changed event in a view. +/// {@category Navigation View} +class PromptVisibilityChangedEvent { + /// Creates a [PromptVisibilityChangedEvent] object. + const PromptVisibilityChangedEvent(this.promptVisible); + + /// Value representing whether prompts are visible or not. + final bool promptVisible; +} + /// Represents the long click position in a Google Maps view. /// {@category Navigation View} /// {@category Map View} diff --git a/pigeons/messages.dart b/pigeons/messages.dart index e2da527c..2814f0c2 100644 --- a/pigeons/messages.dart +++ b/pigeons/messages.dart @@ -466,6 +466,9 @@ abstract class MapViewApi { bool isReportIncidentButtonEnabled(int viewId); void setReportIncidentButtonEnabled(int viewId, bool enabled); + bool isIncidentReportingAvailable(int viewId); + void showReportIncidentsPanel(int viewId); + CameraPositionDto getCameraPosition(int viewId); LatLngBoundsDto getVisibleRegion(int viewId); @@ -597,6 +600,7 @@ abstract class ViewEventApi { void onPolylineClicked(int viewId, String polylineId); void onCircleClicked(int viewId, String circleId); void onNavigationUIEnabledChanged(int viewId, bool navigationUIEnabled); + void onPromptVisibilityChanged(int viewId, bool promptVisible); void onMyLocationClicked(int viewId); void onMyLocationButtonClicked(int viewId); void onCameraChanged( diff --git a/pubspec.yaml b/pubspec.yaml index 5c34f898..402d60d9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,6 +27,7 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.16 + meta: any plugin_platform_interface: ^2.1.5 stream_transform: ^2.1.0 diff --git a/test/google_navigation_flutter_test.dart b/test/google_navigation_flutter_test.dart index e67ce6ca..9fc8781e 100644 --- a/test/google_navigation_flutter_test.dart +++ b/test/google_navigation_flutter_test.dart @@ -673,6 +673,7 @@ void main() { when(viewMockApi.isMapToolbarEnabled(any)).thenReturn(true); when(viewMockApi.isTrafficPromptsEnabled(any)).thenReturn(true); when(viewMockApi.isReportIncidentButtonEnabled(any)).thenReturn(true); + when(viewMockApi.isIncidentReportingAvailable(any)).thenReturn(true); when(viewMockApi.isNavigationHeaderEnabled(any)).thenReturn(true); when(viewMockApi.isNavigationFooterEnabled(any)).thenReturn(true); when(viewMockApi.isSpeedLimitIconEnabled(any)).thenReturn(true); @@ -697,6 +698,7 @@ void main() { expect(await controller.settings.isMapToolbarEnabled(), true); expect(await controller.isTrafficPromptsEnabled(), true); expect(await controller.isReportIncidentButtonEnabled(), true); + expect(await controller.isIncidentReportingAvailable(), true); expect(await controller.isNavigationHeaderEnabled(), true); expect(await controller.isNavigationFooterEnabled(), true); expect(await controller.isSpeedLimitIconEnabled(), true); @@ -719,6 +721,7 @@ void main() { verify(viewMockApi.isMapToolbarEnabled(captureAny)); verify(viewMockApi.isTrafficPromptsEnabled(captureAny)); verify(viewMockApi.isReportIncidentButtonEnabled(captureAny)); + verify(viewMockApi.isIncidentReportingAvailable(captureAny)); verify(viewMockApi.isNavigationHeaderEnabled(captureAny)); verify(viewMockApi.isNavigationFooterEnabled(captureAny)); verify(viewMockApi.isSpeedLimitIconEnabled(captureAny)); @@ -741,6 +744,7 @@ void main() { await controller.settings.setMapToolbarEnabled(true); await controller.setTrafficPromptsEnabled(true); await controller.setReportIncidentButtonEnabled(true); + await controller.showReportIncidentsPanel(); await controller.setNavigationHeaderEnabled(true); await controller.setNavigationFooterEnabled(true); await controller.setSpeedLimitIconEnabled(true); @@ -815,6 +819,7 @@ void main() { ), true, ); + verify(viewMockApi.showReportIncidentsPanel(captureAny)); verifyEnabled( verify( viewMockApi.setNavigationHeaderEnabled(captureAny, captureAny), @@ -852,6 +857,80 @@ void main() { ); }); + test('Test incident reporting APIs', () async { + const int viewId = 1; + final GoogleNavigationViewController controller = + GoogleNavigationViewController(viewId); + + // Mock incident reporting availability and button enabled state + when(viewMockApi.isIncidentReportingAvailable(any)).thenReturn(true); + when( + viewMockApi.isReportIncidentButtonEnabled(any), + ).thenReturn(false); + + // Test isIncidentReportingAvailable + final bool isAvailable = + await controller.isIncidentReportingAvailable(); + expect(isAvailable, true); + + // Verify the API was called + final VerificationResult availabilityResult = verify( + viewMockApi.isIncidentReportingAvailable(captureAny), + ); + expect(availabilityResult.captured[0] as int, viewId); + + // Test isReportIncidentButtonEnabled + final bool isButtonEnabled = + await controller.isReportIncidentButtonEnabled(); + expect(isButtonEnabled, false); + + // Verify the API was called + final VerificationResult buttonEnabledResult = verify( + viewMockApi.isReportIncidentButtonEnabled(captureAny), + ); + expect(buttonEnabledResult.captured[0] as int, viewId); + + // Test setReportIncidentButtonEnabled + await controller.setReportIncidentButtonEnabled(true); + + // Verify the API was called with correct parameters + final VerificationResult setButtonEnabledResult = verify( + viewMockApi.setReportIncidentButtonEnabled(captureAny, captureAny), + ); + expect(setButtonEnabledResult.captured[0] as int, viewId); + expect(setButtonEnabledResult.captured[1] as bool, true); + + // Test showReportIncidentsPanel + await controller.showReportIncidentsPanel(); + + // Verify the API was called + final VerificationResult showPanelResult = verify( + viewMockApi.showReportIncidentsPanel(captureAny), + ); + expect(showPanelResult.captured[0] as int, viewId); + }); + + test('Test prompt visibility changed event stream', () async { + const int viewId = 1; + + // Test the event stream + final Stream eventStream = + GoogleMapsNavigationPlatform.instance.viewAPI + .getPromptVisibilityChangedEventStream(viewId: viewId); + + // Verify the stream is not null + expect(eventStream, isNotNull); + + // Test that the event can be created + const PromptVisibilityChangedEvent event = + PromptVisibilityChangedEvent(true); + expect(event.promptVisible, true); + + const PromptVisibilityChangedEvent event2 = + PromptVisibilityChangedEvent(false); + expect(event2.promptVisible, false); + }); + test('set padding for map', () async { // Create padding EdgeInsets insets = const EdgeInsets.only( diff --git a/test/google_navigation_flutter_test.mocks.dart b/test/google_navigation_flutter_test.mocks.dart index c5916b3e..d9444065 100644 --- a/test/google_navigation_flutter_test.mocks.dart +++ b/test/google_navigation_flutter_test.mocks.dart @@ -741,6 +741,20 @@ class MockTestMapViewApi extends _i1.Mock implements _i3.TestMapViewApi { returnValueForMissingStub: null, ); + @override + bool isIncidentReportingAvailable(int? viewId) => + (super.noSuchMethod( + Invocation.method(#isIncidentReportingAvailable, [viewId]), + returnValue: false, + ) + as bool); + + @override + void showReportIncidentsPanel(int? viewId) => super.noSuchMethod( + Invocation.method(#showReportIncidentsPanel, [viewId]), + returnValueForMissingStub: null, + ); + @override _i2.CameraPositionDto getCameraPosition(int? viewId) => (super.noSuchMethod( diff --git a/test/incident_reporting_test.dart b/test/incident_reporting_test.dart new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/incident_reporting_test.dart @@ -0,0 +1 @@ + diff --git a/test/messages_test.g.dart b/test/messages_test.g.dart index cb992607..e6197f6f 100644 --- a/test/messages_test.g.dart +++ b/test/messages_test.g.dart @@ -492,6 +492,10 @@ abstract class TestMapViewApi { void setReportIncidentButtonEnabled(int viewId, bool enabled); + bool isIncidentReportingAvailable(int viewId); + + void showReportIncidentsPanel(int viewId); + CameraPositionDto getCameraPosition(int viewId); LatLngBoundsDto getVisibleRegion(int viewId); @@ -2682,6 +2686,90 @@ abstract class TestMapViewApi { }); } } + { + final BasicMessageChannel + pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.google_navigation_flutter.MapViewApi.isIncidentReportingAvailable$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, ( + Object? message, + ) async { + assert( + message != null, + 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.isIncidentReportingAvailable was null.', + ); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert( + arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.isIncidentReportingAvailable was null, expected non-null int.', + ); + try { + final bool output = api.isIncidentReportingAvailable( + arg_viewId!, + ); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException( + code: 'error', + message: e.toString(), + ), + ); + } + }); + } + } + { + final BasicMessageChannel + pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.google_navigation_flutter.MapViewApi.showReportIncidentsPanel$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, ( + Object? message, + ) async { + assert( + message != null, + 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.showReportIncidentsPanel was null.', + ); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert( + arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.showReportIncidentsPanel was null, expected non-null int.', + ); + try { + api.showReportIncidentsPanel(arg_viewId!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException( + code: 'error', + message: e.toString(), + ), + ); + } + }); + } + } { final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( From d739acf4fb77f1e3c1d1c3e393b36587d06b9145 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Wed, 13 Aug 2025 10:04:40 +0300 Subject: [PATCH 04/19] feat: upgrade iOS SDK from 10.1.0 to 10.2.0 --- ios/google_navigation_flutter.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/google_navigation_flutter.podspec b/ios/google_navigation_flutter.podspec index c9498df6..fb686bc0 100644 --- a/ios/google_navigation_flutter.podspec +++ b/ios/google_navigation_flutter.podspec @@ -15,7 +15,7 @@ A Google Maps Navigation Flutter plugin. s.source = { :path => '.' } s.source_files = 'google_navigation_flutter/Sources/google_navigation_flutter/**/*.swift' s.dependency 'Flutter' - s.dependency 'GoogleNavigation', '10.1.0' + s.dependency 'GoogleNavigation', '10.2.0' s.platform = :ios, '16.0' s.static_framework = true From 05bfb9247825a2d417ccd65140fb76920c39382a Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Wed, 13 Aug 2025 10:12:35 +0300 Subject: [PATCH 05/19] chore: remove duplicate line from build targetr --- example/ios/Runner.xcodeproj/project.pbxproj | 26 ++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index f8750f15..9bfc4a44 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -29,7 +29,6 @@ 9BAC8E60A263AA40A946AFCF /* Pods_Runner_RunnerUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 981DBDBFB0E241F931B1E394 /* Pods_Runner_RunnerUITests.framework */; }; C9CBB6602AD89694007C737E /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 506F57582AD8055D004AC70F /* XCTest.framework */; platformFilter = ios; }; D3A3B5ED895816BDF2A70351 /* Pods_RunnerCarPlay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B269F01B5CD151328AA7F8EB /* Pods_RunnerCarPlay.framework */; }; - DA52652595AF0077243F8014 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 64B801515FD75D36D58A86FE /* Pods_Runner.framework */; }; EDFE577D2F64CF5D3712A4E9 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 64B801515FD75D36D58A86FE /* Pods_Runner.framework */; }; 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ @@ -143,7 +142,6 @@ files = ( 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, EDFE577D2F64CF5D3712A4E9 /* Pods_Runner.framework in Frameworks */, - DA52652595AF0077243F8014 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -452,10 +450,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; @@ -522,10 +524,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner-RunnerUITests/Pods-Runner-RunnerUITests-resources-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner-RunnerUITests/Pods-Runner-RunnerUITests-resources-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner-RunnerUITests/Pods-Runner-RunnerUITests-resources.sh\"\n"; @@ -606,10 +612,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RunnerCarPlay/Pods-RunnerCarPlay-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RunnerCarPlay/Pods-RunnerCarPlay-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RunnerCarPlay/Pods-RunnerCarPlay-frameworks.sh\"\n"; @@ -623,10 +633,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner-RunnerUITests/Pods-Runner-RunnerUITests-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner-RunnerUITests/Pods-Runner-RunnerUITests-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner-RunnerUITests/Pods-Runner-RunnerUITests-frameworks.sh\"\n"; @@ -684,10 +698,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RunnerCarPlay/Pods-RunnerCarPlay-resources-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RunnerCarPlay/Pods-RunnerCarPlay-resources-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RunnerCarPlay/Pods-RunnerCarPlay-resources.sh\"\n"; @@ -723,10 +741,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; From 63af00a50f23ef50240644fa356e432bc9ee2d97 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Wed, 13 Aug 2025 10:57:32 +0300 Subject: [PATCH 06/19] feat: add support to control if 3d buildinds are enabled or disabled --- .../navigation/GoogleMapsBaseMapView.kt | 8 ++ .../GoogleMapsViewMessageHandler.kt | 8 ++ .../maps/flutter/navigation/messages.g.kt | 52 +++++++++++ .../t04_navigation_ui_test.dart | 11 +++ example/ios/Runner.xcodeproj/project.pbxproj | 24 ----- example/lib/pages/navigation.dart | 16 ++++ .../GoogleMapsNavigationView.swift | 8 ++ ...ogleMapsNavigationViewMessageHandler.swift | 8 ++ .../messages.g.swift | 39 +++++++++ lib/src/google_maps_map_view_controller.dart | 19 +++- ...oogle_maps_navigation_view_controller.dart | 8 +- lib/src/method_channel/map_view_api.dart | 31 +++++-- lib/src/method_channel/messages.g.dart | 59 +++++++++++++ pigeons/messages.dart | 3 + test/google_navigation_flutter_test.dart | 27 ++++++ .../google_navigation_flutter_test.mocks.dart | 14 +++ test/incident_reporting_test.dart | 1 - test/messages_test.g.dart | 87 +++++++++++++++++++ 18 files changed, 384 insertions(+), 39 deletions(-) delete mode 100644 test/incident_reporting_test.dart diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsBaseMapView.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsBaseMapView.kt index 5f790424..3ed66ac8 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsBaseMapView.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsBaseMapView.kt @@ -530,6 +530,14 @@ abstract class GoogleMapsBaseMapView( return getMap().isTrafficEnabled } + fun isBuildingsEnabled(): Boolean { + return getMap().isBuildingsEnabled + } + + fun setBuildingsEnabled(enabled: Boolean) { + getMap().isBuildingsEnabled = enabled + } + fun getMyLocation(): Location? { // Remove this functionality and either guide users to use separate flutter // library for geolocation or implement separate method under diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsViewMessageHandler.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsViewMessageHandler.kt index 850436db..8fe3f67c 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsViewMessageHandler.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsViewMessageHandler.kt @@ -376,6 +376,14 @@ class GoogleMapsViewMessageHandler(private val viewRegistry: GoogleMapsViewRegis getNavigationView(viewId.toInt()).showReportIncidentsPanel() } + override fun isBuildingsEnabled(viewId: Long): Boolean { + return getView(viewId.toInt()).isBuildingsEnabled() + } + + override fun setBuildingsEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setBuildingsEnabled(enabled) + } + override fun isTrafficPromptsEnabled(viewId: Long): Boolean { return getNavigationView(viewId.toInt()).isTrafficPromptsEnabled() } diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt index 4b7baf41..1e5e0a70 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt @@ -2801,6 +2801,10 @@ interface MapViewApi { fun showReportIncidentsPanel(viewId: Long) + fun isBuildingsEnabled(viewId: Long): Boolean + + fun setBuildingsEnabled(viewId: Long, enabled: Boolean) + fun getCameraPosition(viewId: Long): CameraPositionDto fun getVisibleRegion(viewId: Long): LatLngBoundsDto @@ -4171,6 +4175,54 @@ interface MapViewApi { channel.setMessageHandler(null) } } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_navigation_flutter.MapViewApi.isBuildingsEnabled$separatedMessageChannelSuffix", + codec, + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0] as Long + val wrapped: List = + try { + listOf(api.isBuildingsEnabled(viewIdArg)) + } catch (exception: Throwable) { + MessagesPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_navigation_flutter.MapViewApi.setBuildingsEnabled$separatedMessageChannelSuffix", + codec, + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0] as Long + val enabledArg = args[1] as Boolean + val wrapped: List = + try { + api.setBuildingsEnabled(viewIdArg, enabledArg) + listOf(null) + } catch (exception: Throwable) { + MessagesPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel( diff --git a/example/integration_test/t04_navigation_ui_test.dart b/example/integration_test/t04_navigation_ui_test.dart index b6273780..3402c2cb 100644 --- a/example/integration_test/t04_navigation_ui_test.dart +++ b/example/integration_test/t04_navigation_ui_test.dart @@ -267,6 +267,17 @@ void main() { ); } + /// Test enabling and disabling the 3D buildings layer. + for (final bool result in results) { + await viewController.setBuildingsEnabled(result); + final bool isEnabled = await viewController.isBuildingsEnabled(); + expect( + isEnabled, + result, + reason: buildReasonForToggle('BuildingsEnabled', result), + ); + } + /// Test incident reporting availability. final bool isIncidentReportingAvailable = await viewController.isIncidentReportingAvailable(); diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 9bfc4a44..babaf9c3 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -450,14 +450,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; @@ -524,14 +520,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner-RunnerUITests/Pods-Runner-RunnerUITests-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner-RunnerUITests/Pods-Runner-RunnerUITests-resources-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner-RunnerUITests/Pods-Runner-RunnerUITests-resources.sh\"\n"; @@ -612,14 +604,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RunnerCarPlay/Pods-RunnerCarPlay-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RunnerCarPlay/Pods-RunnerCarPlay-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RunnerCarPlay/Pods-RunnerCarPlay-frameworks.sh\"\n"; @@ -633,14 +621,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner-RunnerUITests/Pods-Runner-RunnerUITests-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner-RunnerUITests/Pods-Runner-RunnerUITests-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner-RunnerUITests/Pods-Runner-RunnerUITests-frameworks.sh\"\n"; @@ -698,14 +682,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RunnerCarPlay/Pods-RunnerCarPlay-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RunnerCarPlay/Pods-RunnerCarPlay-resources-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RunnerCarPlay/Pods-RunnerCarPlay-resources.sh\"\n"; @@ -741,14 +721,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; diff --git a/example/lib/pages/navigation.dart b/example/lib/pages/navigation.dart index 8cf6bee7..7f25cbda 100644 --- a/example/lib/pages/navigation.dart +++ b/example/lib/pages/navigation.dart @@ -111,6 +111,7 @@ class _NavigationPageState extends ExamplePageState { bool _trafficIndicentCardsEnabled = false; bool _trafficPromptsEnabled = true; bool _reportIncidentButtonEnabled = true; + bool _buildingsEnabled = true; bool _termsAndConditionsAccepted = false; bool _locationPermissionsAccepted = false; @@ -576,6 +577,8 @@ class _NavigationPageState extends ExamplePageState { await _navigationViewController!.isTrafficPromptsEnabled(); final bool reportIncidentButtonEnabled = await _navigationViewController!.isReportIncidentButtonEnabled(); + final bool buildingsEnabled = + await _navigationViewController!.isBuildingsEnabled(); setState(() { _navigationHeaderEnabled = navigationHeaderEnabled; @@ -588,6 +591,7 @@ class _NavigationPageState extends ExamplePageState { _trafficIndicentCardsEnabled = trafficIndicentCardsEnabled; _trafficPromptsEnabled = trafficPromptsEnabled; _reportIncidentButtonEnabled = reportIncidentButtonEnabled; + _buildingsEnabled = buildingsEnabled; }); } } @@ -1814,6 +1818,18 @@ class _NavigationPageState extends ExamplePageState { }); }, ), + ExampleSwitch( + title: 'Show 3D buildings', + initialValue: _buildingsEnabled, + onChanged: (bool newValue) async { + await _navigationViewController!.setBuildingsEnabled( + newValue, + ); + setState(() { + _buildingsEnabled = newValue; + }); + }, + ), Text( 'Map left padding: ${_mapPadding.left.toStringAsFixed(0)}', ), diff --git a/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationView.swift b/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationView.swift index e753afd7..e79419fa 100644 --- a/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationView.swift +++ b/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationView.swift @@ -338,6 +338,14 @@ public class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettle _mapView.isTrafficEnabled } + func isBuildingsEnabled() -> Bool { + _mapView.isBuildingsEnabled + } + + func setBuildingsEnabled(_ enabled: Bool) { + _mapView.isBuildingsEnabled = enabled + } + func showRouteOverview() { _mapView.cameraMode = .overview } diff --git a/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationViewMessageHandler.swift b/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationViewMessageHandler.swift index e7055521..521e540b 100644 --- a/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationViewMessageHandler.swift +++ b/ios/google_navigation_flutter/Sources/google_navigation_flutter/GoogleMapsNavigationViewMessageHandler.swift @@ -430,6 +430,14 @@ class GoogleMapsNavigationViewMessageHandler: MapViewApi { try getView(viewId).showReportIncidentsPanel() } + func isBuildingsEnabled(viewId: Int64) throws -> Bool { + try getView(viewId).isBuildingsEnabled() + } + + func setBuildingsEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setBuildingsEnabled(enabled) + } + func isTrafficPromptsEnabled(viewId: Int64) throws -> Bool { try getView(viewId).isTrafficPromptsEnabled() } diff --git a/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift b/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift index 9c46089f..f1dd826a 100644 --- a/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift +++ b/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift @@ -2556,6 +2556,8 @@ protocol MapViewApi { func setReportIncidentButtonEnabled(viewId: Int64, enabled: Bool) throws func isIncidentReportingAvailable(viewId: Int64) throws -> Bool func showReportIncidentsPanel(viewId: Int64) throws + func isBuildingsEnabled(viewId: Int64) throws -> Bool + func setBuildingsEnabled(viewId: Int64, enabled: Bool) throws func getCameraPosition(viewId: Int64) throws -> CameraPositionDto func getVisibleRegion(viewId: Int64) throws -> LatLngBoundsDto func followMyLocation(viewId: Int64, perspective: CameraPerspectiveDto, zoomLevel: Double?) throws @@ -3567,6 +3569,43 @@ class MapViewApiSetup { } else { showReportIncidentsPanelChannel.setMessageHandler(nil) } + let isBuildingsEnabledChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.google_navigation_flutter.MapViewApi.isBuildingsEnabled\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + isBuildingsEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let viewIdArg = args[0] as! Int64 + do { + let result = try api.isBuildingsEnabled(viewId: viewIdArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + isBuildingsEnabledChannel.setMessageHandler(nil) + } + let setBuildingsEnabledChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.google_navigation_flutter.MapViewApi.setBuildingsEnabled\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setBuildingsEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let viewIdArg = args[0] as! Int64 + let enabledArg = args[1] as! Bool + do { + try api.setBuildingsEnabled(viewId: viewIdArg, enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setBuildingsEnabledChannel.setMessageHandler(nil) + } let getCameraPositionChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.google_navigation_flutter.MapViewApi.getCameraPosition\(channelSuffix)", diff --git a/lib/src/google_maps_map_view_controller.dart b/lib/src/google_maps_map_view_controller.dart index 55225507..4328077a 100644 --- a/lib/src/google_maps_map_view_controller.dart +++ b/lib/src/google_maps_map_view_controller.dart @@ -172,7 +172,7 @@ class GoogleMapViewController { ); } - /// Is the recenter button enabled. + /// Checks if the recenter button is enabled. Future isRecenterButtonEnabled() { return GoogleMapsNavigationPlatform.instance.viewAPI .isRecenterButtonEnabled(viewId: _viewId); @@ -186,6 +186,23 @@ class GoogleMapViewController { .setRecenterButtonEnabled(viewId: _viewId, enabled: enabled); } + /// Checks if the 3D buildings layer is enabled. + Future isBuildingsEnabled() { + return GoogleMapsNavigationPlatform.instance.viewAPI.isBuildingsEnabled( + viewId: _viewId, + ); + } + + /// Enable or disable the 3D buildings layer. + /// + /// By default, the 3D buildings layer is enabled. + Future setBuildingsEnabled(bool enabled) { + return GoogleMapsNavigationPlatform.instance.viewAPI.setBuildingsEnabled( + viewId: _viewId, + enabled: enabled, + ); + } + /// Returns the minimum zoom level preference from the map view. /// If minimum zoom preference is not set previously, returns minimum possible /// zoom level for the current map type. diff --git a/lib/src/google_maps_navigation_view_controller.dart b/lib/src/google_maps_navigation_view_controller.dart index ad4c05c8..35cfc823 100644 --- a/lib/src/google_maps_navigation_view_controller.dart +++ b/lib/src/google_maps_navigation_view_controller.dart @@ -26,7 +26,7 @@ class GoogleNavigationViewController extends GoogleMapViewController { /// [GoogleMapsNavigationView.onViewCreated] callback. GoogleNavigationViewController(super.viewId); - /// Is the navigation trip progress bar enabled. + /// Checks if the navigation trip progress bar is enabled. Future isNavigationTripProgressBarEnabled() { return GoogleMapsNavigationPlatform.instance.viewAPI .isNavigationTripProgressBarEnabled(viewId: getViewId()); @@ -45,7 +45,7 @@ class GoogleNavigationViewController extends GoogleMapViewController { ); } - /// Is the navigation header enabled. + /// Checks if the navigation header is enabled. Future isNavigationHeaderEnabled() { return GoogleMapsNavigationPlatform.instance.viewAPI .isNavigationHeaderEnabled(viewId: getViewId()); @@ -59,7 +59,7 @@ class GoogleNavigationViewController extends GoogleMapViewController { .setNavigationHeaderEnabled(viewId: getViewId(), enabled: enabled); } - /// Is the navigation footer enabled. + /// Checks if the navigation footer is enabled. Future isNavigationFooterEnabled() { return GoogleMapsNavigationPlatform.instance.viewAPI .isNavigationFooterEnabled(viewId: getViewId()); @@ -121,7 +121,7 @@ class GoogleNavigationViewController extends GoogleMapViewController { .setTrafficIncidentCardsEnabled(viewId: getViewId(), enabled: enabled); } - /// Is the report incident button is shown. + /// Checks if the report incident button is shown. Future isReportIncidentButtonEnabled() { return GoogleMapsNavigationPlatform.instance.viewAPI .isReportIncidentButtonEnabled(viewId: getViewId()); diff --git a/lib/src/method_channel/map_view_api.dart b/lib/src/method_channel/map_view_api.dart index b8333adb..b5270477 100644 --- a/lib/src/method_channel/map_view_api.dart +++ b/lib/src/method_channel/map_view_api.dart @@ -584,7 +584,7 @@ class MapViewAPIImpl { return _viewApi.followMyLocation(viewId, perspective.toDto(), zoomLevel); } - /// Is the navigation trip progress bar enabled. + /// Checks if the navigation trip progress bar is enabled. Future isNavigationTripProgressBarEnabled({required int viewId}) { return _viewApi.isNavigationTripProgressBarEnabled(viewId); } @@ -597,7 +597,7 @@ class MapViewAPIImpl { return _viewApi.setNavigationTripProgressBarEnabled(viewId, enabled); } - /// Is the navigation header enabled. + /// Checks if the navigation header is enabled. Future isNavigationHeaderEnabled({required int viewId}) { return _viewApi.isNavigationHeaderEnabled(viewId); } @@ -610,7 +610,7 @@ class MapViewAPIImpl { return _viewApi.setNavigationHeaderEnabled(viewId, enabled); } - /// Is the navigation footer enabled. + /// Checks if the navigation footer is enabled. Future isNavigationFooterEnabled({required int viewId}) { return _viewApi.isNavigationFooterEnabled(viewId); } @@ -623,7 +623,7 @@ class MapViewAPIImpl { return _viewApi.setNavigationFooterEnabled(viewId, enabled); } - /// Is the recenter button enabled. + /// Checks if the recenter button is enabled. Future isRecenterButtonEnabled({required int viewId}) { return _viewApi.isRecenterButtonEnabled(viewId); } @@ -636,7 +636,7 @@ class MapViewAPIImpl { return _viewApi.setRecenterButtonEnabled(viewId, enabled); } - /// Is the speed limit displayed. + /// Checks if the speed limit icon is displayed. Future isSpeedLimitIconEnabled({required int viewId}) { return _viewApi.isSpeedLimitIconEnabled(viewId); } @@ -649,7 +649,7 @@ class MapViewAPIImpl { return _viewApi.setSpeedLimitIconEnabled(viewId, enabled); } - /// Is speedometer displayed. + /// Checks if the speedometer is displayed. Future isSpeedometerEnabled({required int viewId}) { return _viewApi.isSpeedometerEnabled(viewId); } @@ -662,7 +662,7 @@ class MapViewAPIImpl { return _viewApi.setSpeedometerEnabled(viewId, enabled); } - /// Is incident cards displayed. + /// Checks if incident cards are displayed. Future isTrafficIncidentCardsEnabled({required int viewId}) { return _viewApi.isTrafficIncidentCardsEnabled(viewId); } @@ -675,7 +675,7 @@ class MapViewAPIImpl { return _viewApi.setTrafficIncidentCardsEnabled(viewId, enabled); } - /// Is the report incident button displayed. + /// Checks if the report incident button displayed. Future isReportIncidentButtonEnabled({required int viewId}) { return _viewApi.isReportIncidentButtonEnabled(viewId); } @@ -700,6 +700,19 @@ class MapViewAPIImpl { return _viewApi.showReportIncidentsPanel(viewId); } + /// Checks if 3D buildings layer is enabled. + Future isBuildingsEnabled({required int viewId}) { + return _viewApi.isBuildingsEnabled(viewId); + } + + /// Turns the 3D buildings layer on or off. + Future setBuildingsEnabled({ + required int viewId, + required bool enabled, + }) { + return _viewApi.setBuildingsEnabled(viewId, enabled); + } + /// Are the traffic prompts displayed. Future isTrafficPromptsEnabled({required int viewId}) { return _viewApi.isTrafficPromptsEnabled(viewId); @@ -713,7 +726,7 @@ class MapViewAPIImpl { return _viewApi.setTrafficPromptsEnabled(viewId, enabled); } - /// Is navigation UI enabled. + /// Checks if navigation UI is enabled. Future isNavigationUIEnabled({required int viewId}) { return _viewApi.isNavigationUIEnabled(viewId); } diff --git a/lib/src/method_channel/messages.g.dart b/lib/src/method_channel/messages.g.dart index 3dd852ad..2a1feb0a 100644 --- a/lib/src/method_channel/messages.g.dart +++ b/lib/src/method_channel/messages.g.dart @@ -4529,6 +4529,65 @@ class MapViewApi { } } + Future isBuildingsEnabled(int viewId) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.google_navigation_flutter.MapViewApi.isBuildingsEnabled$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [viewId], + ); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } + + Future setBuildingsEnabled(int viewId, bool enabled) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.google_navigation_flutter.MapViewApi.setBuildingsEnabled$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [viewId, enabled], + ); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + Future getCameraPosition(int viewId) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.google_navigation_flutter.MapViewApi.getCameraPosition$pigeonVar_messageChannelSuffix'; diff --git a/pigeons/messages.dart b/pigeons/messages.dart index 2814f0c2..3659096e 100644 --- a/pigeons/messages.dart +++ b/pigeons/messages.dart @@ -469,6 +469,9 @@ abstract class MapViewApi { bool isIncidentReportingAvailable(int viewId); void showReportIncidentsPanel(int viewId); + bool isBuildingsEnabled(int viewId); + void setBuildingsEnabled(int viewId, bool enabled); + CameraPositionDto getCameraPosition(int viewId); LatLngBoundsDto getVisibleRegion(int viewId); diff --git a/test/google_navigation_flutter_test.dart b/test/google_navigation_flutter_test.dart index 9fc8781e..871707dc 100644 --- a/test/google_navigation_flutter_test.dart +++ b/test/google_navigation_flutter_test.dart @@ -674,6 +674,7 @@ void main() { when(viewMockApi.isTrafficPromptsEnabled(any)).thenReturn(true); when(viewMockApi.isReportIncidentButtonEnabled(any)).thenReturn(true); when(viewMockApi.isIncidentReportingAvailable(any)).thenReturn(true); + when(viewMockApi.isBuildingsEnabled(any)).thenReturn(false); when(viewMockApi.isNavigationHeaderEnabled(any)).thenReturn(true); when(viewMockApi.isNavigationFooterEnabled(any)).thenReturn(true); when(viewMockApi.isSpeedLimitIconEnabled(any)).thenReturn(true); @@ -745,6 +746,7 @@ void main() { await controller.setTrafficPromptsEnabled(true); await controller.setReportIncidentButtonEnabled(true); await controller.showReportIncidentsPanel(); + await controller.setBuildingsEnabled(true); await controller.setNavigationHeaderEnabled(true); await controller.setNavigationFooterEnabled(true); await controller.setSpeedLimitIconEnabled(true); @@ -820,6 +822,10 @@ void main() { true, ); verify(viewMockApi.showReportIncidentsPanel(captureAny)); + verifyEnabled( + verify(viewMockApi.setBuildingsEnabled(captureAny, captureAny)), + true, + ); verifyEnabled( verify( viewMockApi.setNavigationHeaderEnabled(captureAny, captureAny), @@ -867,6 +873,7 @@ void main() { when( viewMockApi.isReportIncidentButtonEnabled(any), ).thenReturn(false); + when(viewMockApi.isBuildingsEnabled(any)).thenReturn(false); // Test isIncidentReportingAvailable final bool isAvailable = @@ -900,6 +907,26 @@ void main() { expect(setButtonEnabledResult.captured[0] as int, viewId); expect(setButtonEnabledResult.captured[1] as bool, true); + // Test isBuildingsEnabled + final bool isBuildingsEnabled = await controller.isBuildingsEnabled(); + expect(isBuildingsEnabled, false); + + // Verify the API was called + final VerificationResult buildingsEnabledResult = verify( + viewMockApi.isBuildingsEnabled(captureAny), + ); + expect(buildingsEnabledResult.captured[0] as int, viewId); + + // Test setBuildingsEnabled + await controller.setBuildingsEnabled(true); + + // Verify the API was called with correct parameters + final VerificationResult setBuildingsEnabledResult = verify( + viewMockApi.setBuildingsEnabled(captureAny, captureAny), + ); + expect(setBuildingsEnabledResult.captured[0] as int, viewId); + expect(setBuildingsEnabledResult.captured[1] as bool, true); + // Test showReportIncidentsPanel await controller.showReportIncidentsPanel(); diff --git a/test/google_navigation_flutter_test.mocks.dart b/test/google_navigation_flutter_test.mocks.dart index d9444065..8db4e16e 100644 --- a/test/google_navigation_flutter_test.mocks.dart +++ b/test/google_navigation_flutter_test.mocks.dart @@ -755,6 +755,20 @@ class MockTestMapViewApi extends _i1.Mock implements _i3.TestMapViewApi { returnValueForMissingStub: null, ); + @override + bool isBuildingsEnabled(int? viewId) => + (super.noSuchMethod( + Invocation.method(#isBuildingsEnabled, [viewId]), + returnValue: false, + ) + as bool); + + @override + void setBuildingsEnabled(int? viewId, bool? enabled) => super.noSuchMethod( + Invocation.method(#setBuildingsEnabled, [viewId, enabled]), + returnValueForMissingStub: null, + ); + @override _i2.CameraPositionDto getCameraPosition(int? viewId) => (super.noSuchMethod( diff --git a/test/incident_reporting_test.dart b/test/incident_reporting_test.dart deleted file mode 100644 index 8b137891..00000000 --- a/test/incident_reporting_test.dart +++ /dev/null @@ -1 +0,0 @@ - diff --git a/test/messages_test.g.dart b/test/messages_test.g.dart index e6197f6f..740beba1 100644 --- a/test/messages_test.g.dart +++ b/test/messages_test.g.dart @@ -496,6 +496,10 @@ abstract class TestMapViewApi { void showReportIncidentsPanel(int viewId); + bool isBuildingsEnabled(int viewId); + + void setBuildingsEnabled(int viewId, bool enabled); + CameraPositionDto getCameraPosition(int viewId); LatLngBoundsDto getVisibleRegion(int viewId); @@ -2770,6 +2774,89 @@ abstract class TestMapViewApi { }); } } + { + final BasicMessageChannel + pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.google_navigation_flutter.MapViewApi.isBuildingsEnabled$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, ( + Object? message, + ) async { + assert( + message != null, + 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.isBuildingsEnabled was null.', + ); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert( + arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.isBuildingsEnabled was null, expected non-null int.', + ); + try { + final bool output = api.isBuildingsEnabled(arg_viewId!); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException( + code: 'error', + message: e.toString(), + ), + ); + } + }); + } + } + { + final BasicMessageChannel + pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.google_navigation_flutter.MapViewApi.setBuildingsEnabled$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler< + Object? + >(pigeonVar_channel, (Object? message) async { + assert( + message != null, + 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.setBuildingsEnabled was null.', + ); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert( + arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.setBuildingsEnabled was null, expected non-null int.', + ); + final bool? arg_enabled = (args[1] as bool?); + assert( + arg_enabled != null, + 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.setBuildingsEnabled was null, expected non-null bool.', + ); + try { + api.setBuildingsEnabled(arg_viewId!, arg_enabled!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } + }); + } + } { final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( From 64b90a0d5e1bead75b7bce25bde2b8e96177f431 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Fri, 3 Oct 2025 16:02:32 +0300 Subject: [PATCH 07/19] feat: update iOS SDK SPM dependencies to version 10.4.0 --- .gitignore | 1 + example/ios/Runner/Info.plist | 5 +++++ ios/google_navigation_flutter.podspec | 2 +- ios/google_navigation_flutter/Package.swift | 4 ++-- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 14fd0e15..0d4bb32f 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ ServiceDefinitions.json xcuserdata/ .swiftpm/ .last_build_id +.build/ # Android local.properties diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 519c97bf..3c37b8d8 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -18,6 +18,10 @@ $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 + CFBundleLocalizations + + en + CFBundleName google_navigation_flutter_example CFBundlePackageType @@ -45,6 +49,7 @@ location processing remote-notification + audio UILaunchStoryboardName LaunchScreen diff --git a/ios/google_navigation_flutter.podspec b/ios/google_navigation_flutter.podspec index fb686bc0..8440bfe7 100644 --- a/ios/google_navigation_flutter.podspec +++ b/ios/google_navigation_flutter.podspec @@ -15,7 +15,7 @@ A Google Maps Navigation Flutter plugin. s.source = { :path => '.' } s.source_files = 'google_navigation_flutter/Sources/google_navigation_flutter/**/*.swift' s.dependency 'Flutter' - s.dependency 'GoogleNavigation', '10.2.0' + s.dependency 'GoogleNavigation', '10.4.0' s.platform = :ios, '16.0' s.static_framework = true diff --git a/ios/google_navigation_flutter/Package.swift b/ios/google_navigation_flutter/Package.swift index d9d553e6..4064fe98 100644 --- a/ios/google_navigation_flutter/Package.swift +++ b/ios/google_navigation_flutter/Package.swift @@ -28,11 +28,11 @@ let package = Package( dependencies: [ .package( url: "https://github.com/googlemaps/ios-navigation-sdk", - exact: "10.0.0" + exact: "10.4.0" ), .package( url: "https://github.com/googlemaps/ios-maps-sdk", - exact: "10.0.0" + exact: "10.4.0" ), ], targets: [ From 7e518250972de0ee62d2737449a507305910ed03 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Fri, 3 Oct 2025 16:07:55 +0300 Subject: [PATCH 08/19] chore: update generated xcode files for example app --- example/ios/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/example/ios/.gitignore b/example/ios/.gitignore index 7a7f9873..1ee65f12 100644 --- a/example/ios/.gitignore +++ b/example/ios/.gitignore @@ -26,6 +26,7 @@ Flutter/flutter_assets/ Flutter/flutter_export_environment.sh ServiceDefinitions.json Runner/GeneratedPluginRegistrant.* +Package.resolved # Exceptions to above rules. !default.mode1v3 From 940986e1c63e28b38cd99cdc631319f5bafec5c7 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Mon, 10 Nov 2025 15:36:31 +0200 Subject: [PATCH 09/19] feat: updated Android SDK to 6.3.3 --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index baa1e8a1..5f18d245 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -77,7 +77,7 @@ android { dependencies { implementation 'androidx.car.app:app:1.4.0' implementation 'androidx.car.app:app-projected:1.4.0' - implementation 'com.google.android.libraries.navigation:navigation:6.3.1' + implementation 'com.google.android.libraries.navigation:navigation:6.3.3' testImplementation 'org.jetbrains.kotlin:kotlin-test' testImplementation 'io.mockk:mockk:1.13.8' testImplementation 'junit:junit:4.13.2' From f4b943203e72d83d8850734fa89438a6fb9c9aab Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Mon, 17 Nov 2025 15:32:00 +0200 Subject: [PATCH 10/19] feat: update Android SDK to version 7.1.0 --- README.md | 4 +- android/build.gradle | 4 +- .../google/maps/flutter/navigation/Convert.kt | 21 +++--- .../GoogleMapsNavigationSessionManager.kt | 17 +++-- .../maps/flutter/navigation/messages.g.kt | 50 +++++++------- example/android/app/build.gradle.kts | 12 ++-- .../SampleAndroidAutoScreen.kt | 10 +-- example/lib/pages/turn_by_turn.dart | 6 +- .../google_navigation_flutter/Convert.swift | 12 ++-- .../messages.g.swift | 50 +++++++------- lib/src/method_channel/convert/navinfo.dart | 6 +- lib/src/method_channel/messages.g.dart | 66 ++++++++++--------- lib/src/types/navigation_destinations.dart | 12 ++++ lib/src/types/navinfo.dart | 32 ++++----- pigeons/messages.dart | 42 +++++++----- 15 files changed, 190 insertions(+), 154 deletions(-) diff --git a/README.md b/README.md index 15d5994a..be51419b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ This repository contains a Flutter plugin that provides a [Google Navigation](ht | | Android | iOS | | ------------------------------- | ------------- | --------- | -| **Minimum mobile OS supported** | API level 23+ | iOS 16.0+ | +| **Minimum mobile OS supported** | API level 24+ | iOS 16.0+ | * A Flutter project * A Google Cloud project @@ -43,7 +43,7 @@ Set the `minSdk` in `android/app/build.gradle`: ```groovy android { defaultConfig { - minSdk 23 + minSdk 24 } } ``` diff --git a/android/build.gradle b/android/build.gradle index 5f18d245..562b4954 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -70,14 +70,14 @@ android { } defaultConfig { - minSdk = 23 + minSdk = 24 consumerProguardFiles 'proguard.txt' } dependencies { implementation 'androidx.car.app:app:1.4.0' implementation 'androidx.car.app:app-projected:1.4.0' - implementation 'com.google.android.libraries.navigation:navigation:6.3.3' + implementation 'com.google.android.libraries.navigation:navigation:7.1.0' testImplementation 'org.jetbrains.kotlin:kotlin-test' testImplementation 'io.mockk:mockk:1.13.8' testImplementation 'junit:junit:4.13.2' diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt index 7f6bac33..094dade5 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt @@ -310,8 +310,8 @@ object Convert { */ fun convertWaypointToDto(waypoint: Waypoint): NavigationWaypointDto { return NavigationWaypointDto( - waypoint.title, - convertLatLngToDto(waypoint.position), + waypoint.title ?: "", + waypoint.position?.let { convertLatLngToDto(it) }, waypoint.placeId, waypoint.preferSameSideOfRoad, waypoint.preferredHeading.takeIf { it != -1 }?.toLong(), @@ -474,6 +474,7 @@ object Convert { Navigator.RouteStatus.OK -> RouteStatusDto.STATUS_OK Navigator.RouteStatus.QUOTA_CHECK_FAILED -> RouteStatusDto.QUOTA_CHECK_FAILED Navigator.RouteStatus.WAYPOINT_ERROR -> RouteStatusDto.WAYPOINT_ERROR + Navigator.RouteStatus.DUPLICATE_WAYPOINTS_ERROR -> RouteStatusDto.DUPLICATE_WAYPOINTS_ERROR } } @@ -802,17 +803,17 @@ object Convert { private fun convertNavInfoStepInfo(stepInfo: StepInfo): StepInfoDto { return StepInfoDto( - distanceFromPrevStepMeters = stepInfo.distanceFromPrevStepMeters.toLong(), - timeFromPrevStepSeconds = stepInfo.timeFromPrevStepSeconds.toLong(), + distanceFromPrevStepMeters = stepInfo.distanceFromPrevStepMeters?.toLong() ?: 0L, + timeFromPrevStepSeconds = stepInfo.timeFromPrevStepSeconds?.toLong() ?: 0L, drivingSide = convertDrivingSide(stepInfo.drivingSide), exitNumber = stepInfo.exitNumber, - fullInstructions = stepInfo.fullInstructionText, - fullRoadName = stepInfo.fullRoadName, - simpleRoadName = stepInfo.simpleRoadName, - roundaboutTurnNumber = stepInfo.roundaboutTurnNumber.toLong(), - stepNumber = stepInfo.stepNumber.toLong(), + fullInstructions = stepInfo.fullInstructionText ?: "", + fullRoadName = stepInfo.fullRoadName ?: "", + simpleRoadName = stepInfo.simpleRoadName ?: "", + roundaboutTurnNumber = stepInfo.roundaboutTurnNumber?.toLong() ?: 0L, + stepNumber = stepInfo.stepNumber?.toLong() ?: 0L, lanes = - stepInfo.lanes.map { lane -> + stepInfo.lanes?.map { lane -> LaneDto( laneDirections = lane.laneDirections().map { laneDirection -> diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt index e084e901..aa8deccd 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt @@ -342,10 +342,17 @@ constructor( remainingTimeOrDistanceChangedListener = Navigator.RemainingTimeOrDistanceChangedListener { val timeAndDistance = getNavigator().currentTimeAndDistance - navigationSessionEventApi.onRemainingTimeOrDistanceChanged( - timeAndDistance.seconds.toDouble(), - timeAndDistance.meters.toDouble(), - ) {} + // Only send event if we have valid time and distance data + if ( + timeAndDistance != null && + timeAndDistance.seconds != null && + timeAndDistance.meters != null + ) { + navigationSessionEventApi.onRemainingTimeOrDistanceChanged( + timeAndDistance.seconds.toDouble(), + timeAndDistance.meters.toDouble(), + ) {} + } } } @@ -495,7 +502,7 @@ constructor( * @return [TimeAndDistance] object. */ fun getCurrentTimeAndDistance(): TimeAndDistance { - return getNavigator().currentTimeAndDistance + return getNavigator().currentTimeAndDistance!! } /** diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt index 1e5e0a70..9f34b3f1 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt @@ -1511,7 +1511,9 @@ data class RoutingOptionsDto( /** Generated class from Pigeon that represents data sent in messages. */ data class NavigationDisplayOptionsDto( val showDestinationMarkers: Boolean? = null, + /** Deprecated: This option now defaults to true. */ val showStopSigns: Boolean? = null, + /** Deprecated: This option now defaults to true. */ val showTrafficLights: Boolean? = null, ) { companion object { @@ -2001,45 +2003,45 @@ data class LaneDto( * Generated class from Pigeon that represents data sent in messages. */ data class StepInfoDto( - /** Distance in meters from the previous step to this step. */ - val distanceFromPrevStepMeters: Long, - /** Time in seconds from the previous step to this step. */ - val timeFromPrevStepSeconds: Long, + /** Distance in meters from the previous step to this step if available, otherwise null. */ + val distanceFromPrevStepMeters: Long? = null, + /** Time in seconds from the previous step to this step if available, otherwise null. */ + val timeFromPrevStepSeconds: Long? = null, /** Whether this step is on a drive-on-right or drive-on-left route. */ val drivingSide: DrivingSideDto, /** The exit number if it exists. */ val exitNumber: String? = null, - /** The full text of the instruction for this step. */ - val fullInstructions: String, - /** The full road name for this step. */ - val fullRoadName: String, - /** The simplified version of the road name. */ - val simpleRoadName: String, + /** The full text of the instruction for this step if available, otherwise null. */ + val fullInstructions: String? = null, + /** The full road name for this step if available, otherwise null. */ + val fullRoadName: String? = null, + /** The simplified version of the road name if available, otherwise null. */ + val simpleRoadName: String? = null, /** * The counted number of the exit to take relative to the location where the roundabout was - * entered. + * entered if available, otherwise null. */ - val roundaboutTurnNumber: Long, - /** The list of available lanes at the end of this route step. */ - val lanes: List, + val roundaboutTurnNumber: Long? = null, + /** The list of available lanes at the end of this route step if available, otherwise null. */ + val lanes: List? = null, /** The maneuver for this step. */ val maneuver: ManeuverDto, - /** The index of the step in the list of all steps in the route. */ - val stepNumber: Long, + /** The index of the step in the list of all steps in the route if available, otherwise null. */ + val stepNumber: Long? = null, ) { companion object { fun fromList(pigeonVar_list: List): StepInfoDto { - val distanceFromPrevStepMeters = pigeonVar_list[0] as Long - val timeFromPrevStepSeconds = pigeonVar_list[1] as Long + val distanceFromPrevStepMeters = pigeonVar_list[0] as Long? + val timeFromPrevStepSeconds = pigeonVar_list[1] as Long? val drivingSide = pigeonVar_list[2] as DrivingSideDto val exitNumber = pigeonVar_list[3] as String? - val fullInstructions = pigeonVar_list[4] as String - val fullRoadName = pigeonVar_list[5] as String - val simpleRoadName = pigeonVar_list[6] as String - val roundaboutTurnNumber = pigeonVar_list[7] as Long - val lanes = pigeonVar_list[8] as List + val fullInstructions = pigeonVar_list[4] as String? + val fullRoadName = pigeonVar_list[5] as String? + val simpleRoadName = pigeonVar_list[6] as String? + val roundaboutTurnNumber = pigeonVar_list[7] as Long? + val lanes = pigeonVar_list[8] as List? val maneuver = pigeonVar_list[9] as ManeuverDto - val stepNumber = pigeonVar_list[10] as Long + val stepNumber = pigeonVar_list[10] as Long? return StepInfoDto( distanceFromPrevStepMeters, timeFromPrevStepSeconds, diff --git a/example/android/app/build.gradle.kts b/example/android/app/build.gradle.kts index f6cf1de3..2948d4ca 100644 --- a/example/android/app/build.gradle.kts +++ b/example/android/app/build.gradle.kts @@ -53,6 +53,12 @@ android { jvmTarget = JavaVersion.VERSION_11.toString() } + // Set this to the languages you actually use, otherwise you'll include resource strings + // for all languages supported by the Navigation SDK. + androidResources { + localeFilters.add("en") + } + defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId = "com.google.maps.flutter.navigation_example" @@ -63,8 +69,6 @@ android { versionCode = flutter.versionCode versionName = flutter.versionName - // Set this to the languages you actually use, otherwise you'll include resource strings - // for all languages supported by the Navigation SDK. multiDexEnabled = true testInstrumentationRunner = "pl.leancode.patrol.PatrolJUnitRunner" @@ -73,8 +77,6 @@ android { // test case and uncomment the following line to clear the package data before running tests. // testInstrumentationRunnerArguments["clearPackageData"] = "true" - resourceConfigurations.add("en") - // Extract MAPS_API_KEY from Dart defines or environment variables // and use it as manifest placeholder. val mapsApiKey = System.getenv("MAPS_API_KEY") ?: findDartDefineValue("MAPS_API_KEY") ?: "" @@ -101,7 +103,7 @@ flutter { dependencies { implementation("androidx.car.app:app:1.4.0") implementation("androidx.car.app:app-projected:1.4.0") - implementation("com.google.android.libraries.navigation:navigation:6.2.2") + implementation("com.google.android.libraries.navigation:navigation:7.1.0") coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4") androidTestUtil("androidx.test:orchestrator:1.5.1") } diff --git a/example/android/app/src/main/kotlin/com/google/maps/flutter/navigation_example/SampleAndroidAutoScreen.kt b/example/android/app/src/main/kotlin/com/google/maps/flutter/navigation_example/SampleAndroidAutoScreen.kt index 2be97a23..447a585c 100644 --- a/example/android/app/src/main/kotlin/com/google/maps/flutter/navigation_example/SampleAndroidAutoScreen.kt +++ b/example/android/app/src/main/kotlin/com/google/maps/flutter/navigation_example/SampleAndroidAutoScreen.kt @@ -59,11 +59,11 @@ class SampleAndroidAutoScreen(carContext: CarContext): AndroidAutoBaseScreen(car * Converts data received from the Navigation data feed into Android-Auto compatible data * structures. */ - val currentStep: Step = buildStepFromStepInfo(navInfo.currentStep) + val currentStep: Step = buildStepFromStepInfo(navInfo.currentStep!!) val distanceToStep = Distance.create( java.lang.Double.max( - navInfo.distanceToCurrentStepMeters.toDouble(), + navInfo.distanceToCurrentStepMeters?.toDouble() ?: 0.0, 0.0 ), Distance.UNIT_METERS ) @@ -78,14 +78,14 @@ class SampleAndroidAutoScreen(carContext: CarContext): AndroidAutoBaseScreen(car val maneuver: Int = ManeuverConverter.getAndroidAutoManeuverType(stepInfo.maneuver) val maneuverBuilder = Maneuver.Builder(maneuver) if (stepInfo.maneuverBitmap != null) { - val maneuverIcon = IconCompat.createWithBitmap(stepInfo.maneuverBitmap) + val maneuverIcon = IconCompat.createWithBitmap(stepInfo.maneuverBitmap!!) val maneuverCarIcon = CarIcon.Builder(maneuverIcon).build() maneuverBuilder.setIcon(maneuverCarIcon) } val stepBuilder = Step.Builder() - .setRoad(stepInfo.fullRoadName) - .setCue(stepInfo.fullInstructionText) + .setRoad(stepInfo.fullRoadName ?: "") + .setCue(stepInfo.fullInstructionText ?: "") .setManeuver(maneuverBuilder.build()) return stepBuilder.build() } diff --git a/example/lib/pages/turn_by_turn.dart b/example/lib/pages/turn_by_turn.dart index 9e5b26bd..3fb630df 100644 --- a/example/lib/pages/turn_by_turn.dart +++ b/example/lib/pages/turn_by_turn.dart @@ -321,7 +321,7 @@ class _TurnByTurnPageState extends ExamplePageState { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Step #${stepInfo.stepNumber}', + 'Step #${stepInfo.stepNumber ?? "?"}', style: navInfoTextStyle, ), const SizedBox(height: 10), @@ -333,12 +333,12 @@ class _TurnByTurnPageState extends ExamplePageState { text: 'Road: ', style: TextStyle(fontWeight: FontWeight.bold), ), - TextSpan(text: stepInfo.fullRoadName), + TextSpan(text: stepInfo.fullRoadName ?? ''), const TextSpan( text: '\nInstructions: ', style: TextStyle(fontWeight: FontWeight.bold), ), - TextSpan(text: stepInfo.fullInstructions), + TextSpan(text: stepInfo.fullInstructions ?? ''), ], ), ), diff --git a/ios/google_navigation_flutter/Sources/google_navigation_flutter/Convert.swift b/ios/google_navigation_flutter/Sources/google_navigation_flutter/Convert.swift index f1528d13..6e2d78dd 100644 --- a/ios/google_navigation_flutter/Sources/google_navigation_flutter/Convert.swift +++ b/ios/google_navigation_flutter/Sources/google_navigation_flutter/Convert.swift @@ -132,18 +132,20 @@ enum Convert { static func convertStepInfo(_ stepInfo: GMSNavigationStepInfo) -> StepInfoDto { .init( - distanceFromPrevStepMeters: Int64(stepInfo.distanceFromPrevStepMeters), - timeFromPrevStepSeconds: Int64(stepInfo.timeFromPrevStepSeconds), + distanceFromPrevStepMeters: stepInfo.distanceFromPrevStepMeters > 0 + ? Int64(stepInfo.distanceFromPrevStepMeters) : nil, + timeFromPrevStepSeconds: stepInfo.timeFromPrevStepSeconds > 0 + ? Int64(stepInfo.timeFromPrevStepSeconds) : nil, drivingSide: convertDrivingSide(side: stepInfo.drivingSide), exitNumber: stepInfo.exitNumber, fullInstructions: stepInfo.fullInstructionText, fullRoadName: stepInfo.fullRoadName, simpleRoadName: stepInfo.simpleRoadName, roundaboutTurnNumber: stepInfo - .roundaboutTurnNumber >= 0 ? Int64(stepInfo.roundaboutTurnNumber) : 0, - lanes: [], + .roundaboutTurnNumber >= 0 ? Int64(stepInfo.roundaboutTurnNumber) : nil, + lanes: nil, maneuver: convertManeuver(maneuver: stepInfo.maneuver), - stepNumber: Int64(stepInfo.stepNumber) + stepNumber: stepInfo.stepNumber >= 0 ? Int64(stepInfo.stepNumber) : nil ) } diff --git a/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift b/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift index f1dd826a..b37d0918 100644 --- a/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift +++ b/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift @@ -1395,7 +1395,9 @@ struct RoutingOptionsDto: Hashable { /// Generated class from Pigeon that represents data sent in messages. struct NavigationDisplayOptionsDto: Hashable { var showDestinationMarkers: Bool? = nil + /// Deprecated: This option now defaults to true. var showStopSigns: Bool? = nil + /// Deprecated: This option now defaults to true. var showTrafficLights: Bool? = nil // swift-format-ignore: AlwaysUseLowerCamelCase @@ -1876,43 +1878,43 @@ struct LaneDto: Hashable { /// /// Generated class from Pigeon that represents data sent in messages. struct StepInfoDto: Hashable { - /// Distance in meters from the previous step to this step. - var distanceFromPrevStepMeters: Int64 - /// Time in seconds from the previous step to this step. - var timeFromPrevStepSeconds: Int64 + /// Distance in meters from the previous step to this step if available, otherwise null. + var distanceFromPrevStepMeters: Int64? = nil + /// Time in seconds from the previous step to this step if available, otherwise null. + var timeFromPrevStepSeconds: Int64? = nil /// Whether this step is on a drive-on-right or drive-on-left route. var drivingSide: DrivingSideDto /// The exit number if it exists. var exitNumber: String? = nil - /// The full text of the instruction for this step. - var fullInstructions: String - /// The full road name for this step. - var fullRoadName: String - /// The simplified version of the road name. - var simpleRoadName: String + /// The full text of the instruction for this step if available, otherwise null. + var fullInstructions: String? = nil + /// The full road name for this step if available, otherwise null. + var fullRoadName: String? = nil + /// The simplified version of the road name if available, otherwise null. + var simpleRoadName: String? = nil /// The counted number of the exit to take relative to the location where the - /// roundabout was entered. - var roundaboutTurnNumber: Int64 - /// The list of available lanes at the end of this route step. - var lanes: [LaneDto?] + /// roundabout was entered if available, otherwise null. + var roundaboutTurnNumber: Int64? = nil + /// The list of available lanes at the end of this route step if available, otherwise null. + var lanes: [LaneDto]? = nil /// The maneuver for this step. var maneuver: ManeuverDto - /// The index of the step in the list of all steps in the route. - var stepNumber: Int64 + /// The index of the step in the list of all steps in the route if available, otherwise null. + var stepNumber: Int64? = nil // swift-format-ignore: AlwaysUseLowerCamelCase static func fromList(_ pigeonVar_list: [Any?]) -> StepInfoDto? { - let distanceFromPrevStepMeters = pigeonVar_list[0] as! Int64 - let timeFromPrevStepSeconds = pigeonVar_list[1] as! Int64 + let distanceFromPrevStepMeters: Int64? = nilOrValue(pigeonVar_list[0]) + let timeFromPrevStepSeconds: Int64? = nilOrValue(pigeonVar_list[1]) let drivingSide = pigeonVar_list[2] as! DrivingSideDto let exitNumber: String? = nilOrValue(pigeonVar_list[3]) - let fullInstructions = pigeonVar_list[4] as! String - let fullRoadName = pigeonVar_list[5] as! String - let simpleRoadName = pigeonVar_list[6] as! String - let roundaboutTurnNumber = pigeonVar_list[7] as! Int64 - let lanes = pigeonVar_list[8] as! [LaneDto?] + let fullInstructions: String? = nilOrValue(pigeonVar_list[4]) + let fullRoadName: String? = nilOrValue(pigeonVar_list[5]) + let simpleRoadName: String? = nilOrValue(pigeonVar_list[6]) + let roundaboutTurnNumber: Int64? = nilOrValue(pigeonVar_list[7]) + let lanes: [LaneDto]? = nilOrValue(pigeonVar_list[8]) let maneuver = pigeonVar_list[9] as! ManeuverDto - let stepNumber = pigeonVar_list[10] as! Int64 + let stepNumber: Int64? = nilOrValue(pigeonVar_list[10]) return StepInfoDto( distanceFromPrevStepMeters: distanceFromPrevStepMeters, diff --git a/lib/src/method_channel/convert/navinfo.dart b/lib/src/method_channel/convert/navinfo.dart index a5801ba5..70836ac9 100644 --- a/lib/src/method_channel/convert/navinfo.dart +++ b/lib/src/method_channel/convert/navinfo.dart @@ -51,11 +51,7 @@ extension ConvertStepInfoDto on StepInfoDto { simpleRoadName: simpleRoadName, roundaboutTurnNumber: roundaboutTurnNumber, stepNumber: stepNumber, - lanes: - lanes - .whereType() - .map((LaneDto lane) => lane.toLane()) - .toList(), + lanes: lanes?.map((LaneDto lane) => lane.toLane()).toList(), maneuver: maneuver.toManeuver(), ); } diff --git a/lib/src/method_channel/messages.g.dart b/lib/src/method_channel/messages.g.dart index 2a1feb0a..a8294dd5 100644 --- a/lib/src/method_channel/messages.g.dart +++ b/lib/src/method_channel/messages.g.dart @@ -1704,8 +1704,10 @@ class NavigationDisplayOptionsDto { bool? showDestinationMarkers; + /// Deprecated: This option now defaults to true. bool? showStopSigns; + /// Deprecated: This option now defaults to true. bool? showTrafficLights; List _toList() { @@ -2385,24 +2387,24 @@ class LaneDto { /// Information about a single step along a navigation route. class StepInfoDto { StepInfoDto({ - required this.distanceFromPrevStepMeters, - required this.timeFromPrevStepSeconds, + this.distanceFromPrevStepMeters, + this.timeFromPrevStepSeconds, required this.drivingSide, this.exitNumber, - required this.fullInstructions, - required this.fullRoadName, - required this.simpleRoadName, - required this.roundaboutTurnNumber, - required this.lanes, + this.fullInstructions, + this.fullRoadName, + this.simpleRoadName, + this.roundaboutTurnNumber, + this.lanes, required this.maneuver, - required this.stepNumber, + this.stepNumber, }); - /// Distance in meters from the previous step to this step. - int distanceFromPrevStepMeters; + /// Distance in meters from the previous step to this step if available, otherwise null. + int? distanceFromPrevStepMeters; - /// Time in seconds from the previous step to this step. - int timeFromPrevStepSeconds; + /// Time in seconds from the previous step to this step if available, otherwise null. + int? timeFromPrevStepSeconds; /// Whether this step is on a drive-on-right or drive-on-left route. DrivingSideDto drivingSide; @@ -2410,27 +2412,27 @@ class StepInfoDto { /// The exit number if it exists. String? exitNumber; - /// The full text of the instruction for this step. - String fullInstructions; + /// The full text of the instruction for this step if available, otherwise null. + String? fullInstructions; - /// The full road name for this step. - String fullRoadName; + /// The full road name for this step if available, otherwise null. + String? fullRoadName; - /// The simplified version of the road name. - String simpleRoadName; + /// The simplified version of the road name if available, otherwise null. + String? simpleRoadName; /// The counted number of the exit to take relative to the location where the - /// roundabout was entered. - int roundaboutTurnNumber; + /// roundabout was entered if available, otherwise null. + int? roundaboutTurnNumber; - /// The list of available lanes at the end of this route step. - List lanes; + /// The list of available lanes at the end of this route step if available, otherwise null. + List? lanes; /// The maneuver for this step. ManeuverDto maneuver; - /// The index of the step in the list of all steps in the route. - int stepNumber; + /// The index of the step in the list of all steps in the route if available, otherwise null. + int? stepNumber; List _toList() { return [ @@ -2455,17 +2457,17 @@ class StepInfoDto { static StepInfoDto decode(Object result) { result as List; return StepInfoDto( - distanceFromPrevStepMeters: result[0]! as int, - timeFromPrevStepSeconds: result[1]! as int, + distanceFromPrevStepMeters: result[0] as int?, + timeFromPrevStepSeconds: result[1] as int?, drivingSide: result[2]! as DrivingSideDto, exitNumber: result[3] as String?, - fullInstructions: result[4]! as String, - fullRoadName: result[5]! as String, - simpleRoadName: result[6]! as String, - roundaboutTurnNumber: result[7]! as int, - lanes: (result[8] as List?)!.cast(), + fullInstructions: result[4] as String?, + fullRoadName: result[5] as String?, + simpleRoadName: result[6] as String?, + roundaboutTurnNumber: result[7] as int?, + lanes: (result[8] as List?)?.cast(), maneuver: result[9]! as ManeuverDto, - stepNumber: result[10]! as int, + stepNumber: result[10] as int?, ); } diff --git a/lib/src/types/navigation_destinations.dart b/lib/src/types/navigation_destinations.dart index 22de85c1..e1a87a72 100644 --- a/lib/src/types/navigation_destinations.dart +++ b/lib/src/types/navigation_destinations.dart @@ -200,9 +200,21 @@ class NavigationDisplayOptions { final bool? showDestinationMarkers; /// Show stop signs. + /// + /// Deprecated: This option now defaults to true and will be removed in future + /// versions. + @Deprecated( + 'This option now defaults to true and will be removed in future versions.', + ) final bool? showStopSigns; /// Show traffic lights. + /// + /// Deprecated: This option now defaults to true and will be removed in future + /// versions. + @Deprecated( + 'This option now defaults to true and will be removed in future versions.', + ) final bool? showTrafficLights; @override diff --git a/lib/src/types/navinfo.dart b/lib/src/types/navinfo.dart index cfc826ec..cbdf2bb6 100644 --- a/lib/src/types/navinfo.dart +++ b/lib/src/types/navinfo.dart @@ -318,11 +318,11 @@ class StepInfo { required this.maneuver, }); - /// Distance in meters from the previous step to this step. - final int distanceFromPrevStepMeters; + /// Distance in meters from the previous step to this step if available, otherwise null. + final int? distanceFromPrevStepMeters; - /// Time in seconds from the previous step to this step. - final int timeFromPrevStepSeconds; + /// Time in seconds from the previous step to this step if available, otherwise null. + final int? timeFromPrevStepSeconds; /// Whether this step is on a drive-on-right or drive-on-left route. final DrivingSide drivingSide; @@ -330,29 +330,29 @@ class StepInfo { /// The exit number if it exists. final String? exitNumber; - /// The full text of the instruction for this step. - final String fullInstructions; + /// The full text of the instruction for this step if available, otherwise null. + final String? fullInstructions; - /// The full road name for this step. - final String fullRoadName; + /// The full road name for this step if available, otherwise null. + final String? fullRoadName; - /// The simplified version of the road name. - final String simpleRoadName; + /// The simplified version of the road name if available, otherwise null. + final String? simpleRoadName; /// The counted number of the exit to take relative to the location where the - /// roundabout was entered. - final int roundaboutTurnNumber; + /// roundabout was entered if available, otherwise null. + final int? roundaboutTurnNumber; - /// The list of available lanes at the end of this route step. + /// The list of available lanes at the end of this route step if available, otherwise null. /// /// Android only. - final List lanes; + final List? lanes; /// The maneuver for this step. final Maneuver maneuver; - /// The index of the step in the list of all steps in the route. - final int stepNumber; + /// The index of the step in the list of all steps in the route if available, otherwise null. + final int? stepNumber; @override String toString() => diff --git a/pigeons/messages.dart b/pigeons/messages.dart index 3659096e..e75b7396 100644 --- a/pigeons/messages.dart +++ b/pigeons/messages.dart @@ -669,7 +669,17 @@ class NavigationDisplayOptionsDto { ); final bool? showDestinationMarkers; + + /// Deprecated: This option now defaults to true. + @Deprecated( + 'This option now defaults to true and will be removed in Navigation SDK 8.0', + ) final bool? showStopSigns; + + /// Deprecated: This option now defaults to true. + @Deprecated( + 'This option now defaults to true and will be removed in Navigation SDK 8.0', + ) final bool? showTrafficLights; } @@ -1122,11 +1132,11 @@ class StepInfoDto { required this.maneuver, }); - /// Distance in meters from the previous step to this step. - final int distanceFromPrevStepMeters; + /// Distance in meters from the previous step to this step if available, otherwise null. + final int? distanceFromPrevStepMeters; - /// Time in seconds from the previous step to this step. - final int timeFromPrevStepSeconds; + /// Time in seconds from the previous step to this step if available, otherwise null. + final int? timeFromPrevStepSeconds; /// Whether this step is on a drive-on-right or drive-on-left route. final DrivingSideDto drivingSide; @@ -1134,27 +1144,27 @@ class StepInfoDto { /// The exit number if it exists. final String? exitNumber; - /// The full text of the instruction for this step. - final String fullInstructions; + /// The full text of the instruction for this step if available, otherwise null. + final String? fullInstructions; - /// The full road name for this step. - final String fullRoadName; + /// The full road name for this step if available, otherwise null. + final String? fullRoadName; - /// The simplified version of the road name. - final String simpleRoadName; + /// The simplified version of the road name if available, otherwise null. + final String? simpleRoadName; /// The counted number of the exit to take relative to the location where the - /// roundabout was entered. - final int roundaboutTurnNumber; + /// roundabout was entered if available, otherwise null. + final int? roundaboutTurnNumber; - /// The list of available lanes at the end of this route step. - final List lanes; + /// The list of available lanes at the end of this route step if available, otherwise null. + final List? lanes; /// The maneuver for this step. final ManeuverDto maneuver; - /// The index of the step in the list of all steps in the route. - final int stepNumber; + /// The index of the step in the list of all steps in the route if available, otherwise null. + final int? stepNumber; } /// Contains information about the state of navigation, the current nav step if From 81327f523beccf9256dd1155c80d0d15831fe3f6 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Mon, 17 Nov 2025 15:44:41 +0200 Subject: [PATCH 11/19] chore: upgrade androidx.car.app libs to version 1.7.0 --- android/build.gradle | 4 ++-- example/android/app/build.gradle.kts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 562b4954..2a7b58cf 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -75,8 +75,8 @@ android { } dependencies { - implementation 'androidx.car.app:app:1.4.0' - implementation 'androidx.car.app:app-projected:1.4.0' + implementation 'androidx.car.app:app:1.7.0' + implementation 'androidx.car.app:app-projected:1.7.0' implementation 'com.google.android.libraries.navigation:navigation:7.1.0' testImplementation 'org.jetbrains.kotlin:kotlin-test' testImplementation 'io.mockk:mockk:1.13.8' diff --git a/example/android/app/build.gradle.kts b/example/android/app/build.gradle.kts index 2948d4ca..c70b8783 100644 --- a/example/android/app/build.gradle.kts +++ b/example/android/app/build.gradle.kts @@ -101,8 +101,8 @@ flutter { } dependencies { - implementation("androidx.car.app:app:1.4.0") - implementation("androidx.car.app:app-projected:1.4.0") + implementation("androidx.car.app:app:1.7.0") + implementation("androidx.car.app:app-projected:1.7.0") implementation("com.google.android.libraries.navigation:navigation:7.1.0") coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4") androidTestUtil("androidx.test:orchestrator:1.5.1") From a92e86310898e782e5417d7bbc821af20f457a93 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Tue, 18 Nov 2025 13:09:29 +0200 Subject: [PATCH 12/19] feat: add support for mapID --- README.md | 26 +++++++ .../google/maps/flutter/navigation/Convert.kt | 1 + .../maps/flutter/navigation/messages.g.kt | 8 ++ example/README.md | 5 ++ example/integration_test/t06_map_test.dart | 44 +++++++++++ example/ios/RunnerTests/ConvertTests.swift | 4 +- example/lib/main.dart | 73 ++++++++++------- example/lib/pages/map.dart | 16 +++- example/lib/pages/multiple_views.dart | 6 +- example/lib/pages/navigation.dart | 1 + example/lib/pages/widget_initialization.dart | 13 ++++ example/lib/utils/map_id_manager.dart | 44 +++++++++++ example/lib/utils/utils.dart | 1 + example/lib/widgets/map_id_dialog.dart | 78 +++++++++++++++++++ example/lib/widgets/widgets.dart | 1 + .../Convert+MapConfiguration.swift | 3 +- .../MapConfiguration.swift | 4 + .../messages.g.swift | 8 +- lib/src/google_maps_map_view.dart | 16 ++++ lib/src/google_maps_navigation_view.dart | 2 + lib/src/method_channel/map_view_api.dart | 1 + lib/src/method_channel/messages.g.dart | 7 ++ .../types/view_initialization_options.dart | 17 +++- pigeons/messages.dart | 5 ++ 24 files changed, 350 insertions(+), 34 deletions(-) create mode 100644 example/lib/utils/map_id_manager.dart create mode 100644 example/lib/widgets/map_id_dialog.dart diff --git a/README.md b/README.md index be51419b..8b9e4e3a 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,32 @@ This parameter has only an effect on Android. ``` +#### Using Map IDs +You can configure your map by providing a `mapId` parameter during map initialization. Map IDs are created in the [Google Cloud Console](https://console.cloud.google.com/google/maps-apis/studio/maps) and allow you to [enable various Google Maps Platform features](https://developers.google.com/maps/documentation/android-sdk/map-ids/mapid-over#features-available), such as cloud-based map styling. + +> [!NOTE] +> The `mapId` can only be set once during map initialization and cannot be changed afterwards. Both `GoogleMapsMapView` and `GoogleMapsNavigationView` support the `mapId` parameter. + +For `GoogleMapsMapView`: + +```dart +GoogleMapsMapView( + mapId: 'YOUR_MAP_ID', // Can only be set during initialization + ... +) +``` + +For `GoogleMapsNavigationView`: + +```dart +GoogleMapsNavigationView( + mapId: 'YOUR_MAP_ID', // Can only be set during initialization + ... +) +``` + +For more information about map IDs and how to create them, see the [Google Maps Platform documentation](https://developers.google.com/maps/documentation/get-map-id). + See the [example](./example) directory for a complete navigation sample app. ### Requesting and handling permissions diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt index 094dade5..ae09828f 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt @@ -82,6 +82,7 @@ object Convert { options.minZoomPreference?.let { googleMapOptions.minZoomPreference(it.toFloat()) } options.maxZoomPreference?.let { googleMapOptions.maxZoomPreference(it.toFloat()) } googleMapOptions.zoomControlsEnabled(options.zoomControlsEnabled) + options.mapId?.let { googleMapOptions.mapId(it) } return MapOptions(googleMapOptions, options.padding) } diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt index 9f34b3f1..9b809b2d 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt @@ -594,6 +594,11 @@ data class MapOptionsDto( val cameraTargetBounds: LatLngBoundsDto? = null, /** Specifies the padding for the map. */ val padding: MapPaddingDto? = null, + /** + * The map ID for advanced map options eg. cloud-based map styling. This value can only be set on + * map initialization and cannot be changed afterwards. + */ + val mapId: String? = null, ) { companion object { fun fromList(pigeonVar_list: List): MapOptionsDto { @@ -611,6 +616,7 @@ data class MapOptionsDto( val zoomControlsEnabled = pigeonVar_list[11] as Boolean val cameraTargetBounds = pigeonVar_list[12] as LatLngBoundsDto? val padding = pigeonVar_list[13] as MapPaddingDto? + val mapId = pigeonVar_list[14] as String? return MapOptionsDto( cameraPosition, mapType, @@ -626,6 +632,7 @@ data class MapOptionsDto( zoomControlsEnabled, cameraTargetBounds, padding, + mapId, ) } } @@ -646,6 +653,7 @@ data class MapOptionsDto( zoomControlsEnabled, cameraTargetBounds, padding, + mapId, ) } diff --git a/example/README.md b/example/README.md index e6d1b9fd..9b486d78 100644 --- a/example/README.md +++ b/example/README.md @@ -17,6 +17,11 @@ Run the app with the API key as a Dart define. flutter run --dart-define MAPS_API_KEY=YOUR_API_KEY ``` +You can also optionally provide a Map ID with another Dart define: +```bash +flutter run --dart-define MAPS_API_KEY=YOUR_API_KEY --dart-define MAP_ID=YOUR_MAP_ID +``` + The example app demonstrates multiple ways to provide the Maps API key for platforms. ### Android specific API key diff --git a/example/integration_test/t06_map_test.dart b/example/integration_test/t06_map_test.dart index 86ba62f6..0744c122 100644 --- a/example/integration_test/t06_map_test.dart +++ b/example/integration_test/t06_map_test.dart @@ -26,6 +26,8 @@ import 'package:flutter/material.dart'; import 'shared.dart'; +const String testMapId = 'DEMO_MAP_ID'; + void main() { final mapTypeVariants = getMapTypeVariants(); patrol('Test map types', (PatrolIntegrationTester $) async { @@ -123,6 +125,48 @@ void main() { expect(await controller.settings.isZoomControlsEnabled(), false); expect(await controller.settings.isMapToolbarEnabled(), false); } + + // Test that view can be created with a mapID. + // Note: mapID cannot be fetched back from the map, so we only test + // that creation succeeds. + final ControllerCompleter + controllerCompleterWithMapId = + ControllerCompleter(); + + switch (mapTypeVariants.currentValue!) { + case TestMapType.mapView: + final Key key = GlobalKey(); + await pumpMapView( + $, + GoogleMapsMapView( + key: key, + mapId: testMapId, + onViewCreated: (GoogleMapViewController viewController) { + controllerCompleterWithMapId.complete(viewController); + }, + ), + ); + break; + case TestMapType.navigationView: + final Key key = GlobalKey(); + await pumpNavigationView( + $, + GoogleMapsNavigationView( + key: key, + mapId: testMapId, + onViewCreated: (GoogleNavigationViewController viewController) { + controllerCompleterWithMapId.complete(viewController); + }, + ), + ); + break; + } + + final GoogleMapViewController controllerWithMapId = + await controllerCompleterWithMapId.future; + + // Verify the controller was created successfully + expect(controllerWithMapId, isNotNull); }, variant: mapTypeVariants); patrol('Test map UI settings', (PatrolIntegrationTester $) async { diff --git a/example/ios/RunnerTests/ConvertTests.swift b/example/ios/RunnerTests/ConvertTests.swift index 4fca29e3..aea3c097 100644 --- a/example/ios/RunnerTests/ConvertTests.swift +++ b/example/ios/RunnerTests/ConvertTests.swift @@ -551,7 +551,8 @@ class ConvertTests: XCTestCase { zoomGesturesEnabled: false, scrollGesturesEnabledDuringRotateOrZoom: false, mapToolbarEnabled: false, - zoomControlsEnabled: false + zoomControlsEnabled: false, + mapId: "test-map-id" ) let navigationViewOptions = @@ -584,6 +585,7 @@ class ConvertTests: XCTestCase { mapOptions.scrollGesturesEnabledDuringRotateOrZoom, configuration.scrollGesturesEnabledDuringRotateOrZoom ) + XCTAssertEqual(mapOptions.mapId, configuration.mapId) } func testConvertPath() { diff --git a/example/lib/main.dart b/example/lib/main.dart index f09e7f44..180bd109 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -22,6 +22,7 @@ import 'package:google_navigation_flutter/google_navigation_flutter.dart'; import 'package:permission_handler/permission_handler.dart'; import 'pages/circles.dart'; import 'pages/pages.dart'; +import 'utils/utils.dart'; import 'widgets/widgets.dart'; /// The list of pages to show in the Google Maps Navigation demo. @@ -106,33 +107,51 @@ class _NavigationDemoState extends State { body: SafeArea( top: false, minimum: const EdgeInsets.all(8.0), - child: ListView.builder( - itemCount: _allPages.length + 1, - itemBuilder: (_, int index) { - if (index == 0) { - return Card( - child: Container( - padding: const EdgeInsets.symmetric(vertical: 4.0), - alignment: Alignment.center, - child: Column( - children: [ - Text( - Platform.isIOS - ? 'Location ${_locationPermitted ? 'granted' : 'denied'} • Notifications ${_notificationsPermitted ? 'granted' : 'denied'}' - : 'Location ${_locationPermitted ? 'granted' : 'denied'} ', - ), - Text('Navigation SDK version: $_navSDKVersion'), - ], - ), + child: Column( + children: [ + Card( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 4.0), + alignment: Alignment.center, + child: Column( + children: [ + Text( + Platform.isIOS + ? 'Location ${_locationPermitted ? 'granted' : 'denied'} • Notifications ${_notificationsPermitted ? 'granted' : 'denied'}' + : 'Location ${_locationPermitted ? 'granted' : 'denied'} ', + ), + Text('Navigation SDK version: $_navSDKVersion'), + Text( + 'Current map ID: ${MapIdManager.instance.mapIdDisplay}', + ), + ], ), - ); - } - return ListTile( - leading: _allPages[index - 1].leading, - title: Text(_allPages[index - 1].title), - onTap: () => _pushPage(context, _allPages[index - 1]), - ); - }, + ), + ), + Expanded( + child: Scrollbar( + thumbVisibility: true, + child: ListView.builder( + itemCount: _allPages.length, + itemBuilder: (_, int index) { + return ListTile( + leading: _allPages[index].leading, + title: Text(_allPages[index].title), + onTap: () => _pushPage(context, _allPages[index]), + ); + }, + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + onPressed: + () => showMapIdDialog(context, () => setState(() {})), + child: const Text('Set Map ID'), + ), + ), + ], ), ), ); @@ -140,6 +159,8 @@ class _NavigationDemoState extends State { } void main() { + MapIdManager.instance.initialize(); + final ElevatedButtonThemeData exampleButtonDefaultTheme = ElevatedButtonThemeData( style: ElevatedButton.styleFrom(minimumSize: const Size(160, 36)), diff --git a/example/lib/pages/map.dart b/example/lib/pages/map.dart index fc58ef2c..cad9affb 100644 --- a/example/lib/pages/map.dart +++ b/example/lib/pages/map.dart @@ -113,6 +113,7 @@ class _MapPageState extends ExamplePageState { onViewCreated: _onViewCreated, onMyLocationClicked: _onMyLocationClicked, onMyLocationButtonClicked: _onMyLocationButtonClicked, + mapId: MapIdManager.instance.mapId, ), Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), @@ -165,17 +166,26 @@ class _MapPageState extends ExamplePageState { children: [ ElevatedButton( style: mapTypeStyle, - onPressed: () => setMapStyleDefault(), + onPressed: + MapIdManager.instance.mapId != null + ? null + : () => setMapStyleDefault(), child: const Text('Default style'), ), ElevatedButton( style: mapTypeStyle, - onPressed: () => setMapStyleNight(), + onPressed: + MapIdManager.instance.mapId != null + ? null + : () => setMapStyleNight(), child: const Text('Night style'), ), ElevatedButton( style: mapTypeStyle, - onPressed: () => setMapStyleSepia(), + onPressed: + MapIdManager.instance.mapId != null + ? null + : () => setMapStyleSepia(), child: const Text('Sepia style'), ), ], diff --git a/example/lib/pages/multiple_views.dart b/example/lib/pages/multiple_views.dart index 279c346c..3c4c4324 100644 --- a/example/lib/pages/multiple_views.dart +++ b/example/lib/pages/multiple_views.dart @@ -237,7 +237,10 @@ class _MultiplexState extends ExamplePageState { children: [ SizedBox( height: 200, - child: GoogleMapsMapView(onViewCreated: _onViewCreated), + child: GoogleMapsMapView( + onViewCreated: _onViewCreated, + mapId: MapIdManager.instance.mapId, + ), ), const SizedBox(height: 20), SizedBox( @@ -251,6 +254,7 @@ class _MultiplexState extends ExamplePageState { target: _userLocation!, zoom: 15, ), + mapId: MapIdManager.instance.mapId, ) : const Center( child: Column( diff --git a/example/lib/pages/navigation.dart b/example/lib/pages/navigation.dart index 7f25cbda..27ee59b2 100644 --- a/example/lib/pages/navigation.dart +++ b/example/lib/pages/navigation.dart @@ -1228,6 +1228,7 @@ class _NavigationPageState extends ExamplePageState { ? NavigationUIEnabledPreference.automatic : NavigationUIEnabledPreference.disabled, initialPadding: const EdgeInsets.all(0), + mapId: MapIdManager.instance.mapId, ) : const Center( child: Column( diff --git a/example/lib/pages/widget_initialization.dart b/example/lib/pages/widget_initialization.dart index 62e82820..532a24c8 100644 --- a/example/lib/pages/widget_initialization.dart +++ b/example/lib/pages/widget_initialization.dart @@ -158,6 +158,18 @@ class _ViewInitializationPageState style: TextStyle(fontWeight: FontWeight.bold), ), children: [ + ListTile( + title: const Text('Map ID'), + subtitle: Text(MapIdManager.instance.mapIdDisplay), + trailing: ElevatedButton( + onPressed: + () => showMapIdDialog( + context, + () => setState(() {}), + ), + child: const Text('Set'), + ), + ), ExampleDropdownButton( title: 'Map Type', value: _initialMapType, @@ -474,6 +486,7 @@ class _InitializedViewPage extends StatelessWidget { initialPadding: initialPadding, initialNavigationUIEnabledPreference: initialNavigationUIEnabledPreference, + mapId: MapIdManager.instance.mapId, ), ); } diff --git a/example/lib/utils/map_id_manager.dart b/example/lib/utils/map_id_manager.dart new file mode 100644 index 00000000..47a4d8ec --- /dev/null +++ b/example/lib/utils/map_id_manager.dart @@ -0,0 +1,44 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ignore_for_file: public_member_api_docs + +/// Manager class for handling map ID configuration. +class MapIdManager { + MapIdManager._(); + + static final MapIdManager _instance = MapIdManager._(); + static MapIdManager get instance => _instance; + + String? _mapId; + + /// Initialize map ID from dart-define or use default value. + /// This should be called once at app startup. + void initialize() { + // Read from dart-define (compile-time constant) + const String dartDefineMapId = String.fromEnvironment('MAP_ID'); + _mapId = dartDefineMapId.isNotEmpty ? dartDefineMapId : null; + } + + /// Get the current map ID. + String? get mapId => _mapId; + + /// Get display string for map ID (for UI display). + String get mapIdDisplay => _mapId ?? ''; + + /// Set map ID manually (used by the UI button). + void setMapId(String? mapId) { + _mapId = mapId?.trim().isEmpty ?? true ? null : mapId; + } +} diff --git a/example/lib/utils/utils.dart b/example/lib/utils/utils.dart index bdd6abca..75ee70a8 100644 --- a/example/lib/utils/utils.dart +++ b/example/lib/utils/utils.dart @@ -14,6 +14,7 @@ export 'constants.dart'; export 'location_permissions.dart'; +export 'map_id_manager.dart'; export 'overlay_options.dart'; export 'remaining_distance.dart'; export 'remaining_time.dart'; diff --git a/example/lib/widgets/map_id_dialog.dart b/example/lib/widgets/map_id_dialog.dart new file mode 100644 index 00000000..0f8a512f --- /dev/null +++ b/example/lib/widgets/map_id_dialog.dart @@ -0,0 +1,78 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; + +import '../utils/utils.dart'; + +/// Shows a dialog to set or clear the Map ID. +Future showMapIdDialog( + BuildContext context, + VoidCallback onMapIdChanged, +) async { + final TextEditingController controller = TextEditingController(); + await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Set Map ID'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextField( + controller: controller, + decoration: const InputDecoration( + hintText: 'Enter Map ID (leave empty to clear)', + ), + ), + const SizedBox(height: 16), + const Text( + 'Tip: You can also set this at compile time using:', + style: TextStyle(fontSize: 12, fontStyle: FontStyle.italic), + ), + const SizedBox(height: 4), + const Text( + '--dart-define=MAP_ID=your_map_id', + style: TextStyle(fontSize: 11, fontFamily: 'monospace'), + ), + const SizedBox(height: 8), + const Text( + 'See README for more details on Map ID configuration.', + style: TextStyle(fontSize: 12, fontStyle: FontStyle.italic), + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + MapIdManager.instance.setMapId( + controller.text.isEmpty ? null : controller.text, + ); + Navigator.of(context).pop(); + onMapIdChanged(); + }, + child: const Text('Set'), + ), + ], + ); + }, + ); +} diff --git a/example/lib/widgets/widgets.dart b/example/lib/widgets/widgets.dart index 0e7ee9fb..858ce626 100644 --- a/example/lib/widgets/widgets.dart +++ b/example/lib/widgets/widgets.dart @@ -16,6 +16,7 @@ export 'camera_position_editor.dart'; export 'dropdown_button.dart'; export 'latlng_bound_editor.dart'; export 'latlng_editor.dart'; +export 'map_id_dialog.dart'; export 'page.dart'; export 'slider_editor.dart'; export 'switch.dart'; diff --git a/ios/google_navigation_flutter/Sources/google_navigation_flutter/Convert+MapConfiguration.swift b/ios/google_navigation_flutter/Sources/google_navigation_flutter/Convert+MapConfiguration.swift index df1ed27e..6002ba35 100644 --- a/ios/google_navigation_flutter/Sources/google_navigation_flutter/Convert+MapConfiguration.swift +++ b/ios/google_navigation_flutter/Sources/google_navigation_flutter/Convert+MapConfiguration.swift @@ -56,7 +56,8 @@ extension Convert { left: CGFloat(mapOptions.padding?.left ?? 0), bottom: CGFloat(mapOptions.padding?.bottom ?? 0), right: CGFloat(mapOptions.padding?.right ?? 0) - ) + ), + mapId: mapOptions.mapId ) } } diff --git a/ios/google_navigation_flutter/Sources/google_navigation_flutter/MapConfiguration.swift b/ios/google_navigation_flutter/Sources/google_navigation_flutter/MapConfiguration.swift index 80aa055a..105b29c7 100644 --- a/ios/google_navigation_flutter/Sources/google_navigation_flutter/MapConfiguration.swift +++ b/ios/google_navigation_flutter/Sources/google_navigation_flutter/MapConfiguration.swift @@ -38,6 +38,7 @@ struct MapConfiguration { var minZoomPreference: Float? var maxZoomPreference: Float? var padding: UIEdgeInsets? + var mapId: String? } extension MapConfiguration { @@ -70,6 +71,9 @@ extension MapConfiguration { func apply(to mapViewOptions: GMSMapViewOptions, withFrame frame: CGRect) { mapViewOptions.camera = cameraPosition mapViewOptions.frame = frame + if let mapId { + mapViewOptions.mapID = GMSMapID(identifier: mapId) + } } // Applies camera position from the configuration to the given GMSMapView. diff --git a/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift b/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift index b37d0918..dcf877ab 100644 --- a/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift +++ b/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift @@ -497,6 +497,9 @@ struct MapOptionsDto: Hashable { var cameraTargetBounds: LatLngBoundsDto? = nil /// Specifies the padding for the map. var padding: MapPaddingDto? = nil + /// The map ID for advanced map options eg. cloud-based map styling. + /// This value can only be set on map initialization and cannot be changed afterwards. + var mapId: String? = nil // swift-format-ignore: AlwaysUseLowerCamelCase static func fromList(_ pigeonVar_list: [Any?]) -> MapOptionsDto? { @@ -514,6 +517,7 @@ struct MapOptionsDto: Hashable { let zoomControlsEnabled = pigeonVar_list[11] as! Bool let cameraTargetBounds: LatLngBoundsDto? = nilOrValue(pigeonVar_list[12]) let padding: MapPaddingDto? = nilOrValue(pigeonVar_list[13]) + let mapId: String? = nilOrValue(pigeonVar_list[14]) return MapOptionsDto( cameraPosition: cameraPosition, @@ -529,7 +533,8 @@ struct MapOptionsDto: Hashable { maxZoomPreference: maxZoomPreference, zoomControlsEnabled: zoomControlsEnabled, cameraTargetBounds: cameraTargetBounds, - padding: padding + padding: padding, + mapId: mapId ) } func toList() -> [Any?] { @@ -548,6 +553,7 @@ struct MapOptionsDto: Hashable { zoomControlsEnabled, cameraTargetBounds, padding, + mapId, ] } static func == (lhs: MapOptionsDto, rhs: MapOptionsDto) -> Bool { diff --git a/lib/src/google_maps_map_view.dart b/lib/src/google_maps_map_view.dart index bd933946..bcda2465 100644 --- a/lib/src/google_maps_map_view.dart +++ b/lib/src/google_maps_map_view.dart @@ -44,6 +44,7 @@ abstract class GoogleMapsBaseMapView extends StatefulWidget { this.initialZoomControlsEnabled = true, this.initialCameraTargetBounds, this.initialPadding, + this.mapId, this.layoutDirection, this.gestureRecognizers = const >{}, this.onRecenterButtonClicked, @@ -153,6 +154,19 @@ abstract class GoogleMapsBaseMapView extends StatefulWidget { /// Null by default (no padding). final EdgeInsets? initialPadding; + /// The map ID for enabling various Google Maps Platform features. + /// + /// A map ID is a unique identifier that represents a single map instance. + /// This value can only be set once during map initialization and cannot be changed afterwards. + /// Map IDs are created in Google Cloud Console and can be used to enable various features + /// such as cloud-based map styling, among others. + /// + /// See https://developers.google.com/maps/documentation/get-map-id + /// for more information about map IDs and how to create them. + /// + /// Null by default (no map ID). + final String? mapId; + /// Which gestures should be forwarded to the PlatformView. /// /// When this set is empty, the map will only handle pointer events for gestures that @@ -259,6 +273,7 @@ class GoogleMapsMapView extends GoogleMapsBaseMapView { super.initialZoomControlsEnabled = true, super.initialCameraTargetBounds, super.initialPadding, + super.mapId, super.layoutDirection, super.gestureRecognizers = const >{}, super.onRecenterButtonClicked, @@ -440,6 +455,7 @@ class GoogleMapsMapViewState extends MapViewState { zoomControlsEnabled: widget.initialZoomControlsEnabled, cameraTargetBounds: widget.initialCameraTargetBounds, padding: widget.initialPadding, + mapId: widget.mapId, ), ), onPlatformViewCreated: _onPlatformViewCreated, diff --git a/lib/src/google_maps_navigation_view.dart b/lib/src/google_maps_navigation_view.dart index a53517f2..412445a5 100644 --- a/lib/src/google_maps_navigation_view.dart +++ b/lib/src/google_maps_navigation_view.dart @@ -60,6 +60,7 @@ class GoogleMapsNavigationView extends GoogleMapsBaseMapView { super.initialZoomControlsEnabled = true, super.initialCameraTargetBounds, super.initialPadding, + super.mapId, this.initialNavigationUIEnabledPreference = NavigationUIEnabledPreference.automatic, super.layoutDirection, @@ -174,6 +175,7 @@ class GoogleMapsNavigationViewState zoomControlsEnabled: widget.initialZoomControlsEnabled, cameraTargetBounds: widget.initialCameraTargetBounds, padding: widget.initialPadding, + mapId: widget.mapId, ), navigationViewOptions: NavigationViewOptions( navigationUIEnabledPreference: diff --git a/lib/src/method_channel/map_view_api.dart b/lib/src/method_channel/map_view_api.dart index b5270477..52bcd28e 100644 --- a/lib/src/method_channel/map_view_api.dart +++ b/lib/src/method_channel/map_view_api.dart @@ -131,6 +131,7 @@ class MapViewAPIImpl { right: mapOptions.padding!.right.toInt(), ) : null, + mapId: mapOptions.mapId, ); // Initialize navigation view options if given diff --git a/lib/src/method_channel/messages.g.dart b/lib/src/method_channel/messages.g.dart index a8294dd5..d597d664 100644 --- a/lib/src/method_channel/messages.g.dart +++ b/lib/src/method_channel/messages.g.dart @@ -430,6 +430,7 @@ class MapOptionsDto { required this.zoomControlsEnabled, this.cameraTargetBounds, this.padding, + this.mapId, }); /// The initial positioning of the camera in the map view. @@ -475,6 +476,10 @@ class MapOptionsDto { /// Specifies the padding for the map. MapPaddingDto? padding; + /// The map ID for advanced map options eg. cloud-based map styling. + /// This value can only be set on map initialization and cannot be changed afterwards. + String? mapId; + List _toList() { return [ cameraPosition, @@ -491,6 +496,7 @@ class MapOptionsDto { zoomControlsEnabled, cameraTargetBounds, padding, + mapId, ]; } @@ -515,6 +521,7 @@ class MapOptionsDto { zoomControlsEnabled: result[11]! as bool, cameraTargetBounds: result[12] as LatLngBoundsDto?, padding: result[13] as MapPaddingDto?, + mapId: result[14] as String?, ); } diff --git a/lib/src/types/view_initialization_options.dart b/lib/src/types/view_initialization_options.dart index 2b5c07ed..1c14b548 100644 --- a/lib/src/types/view_initialization_options.dart +++ b/lib/src/types/view_initialization_options.dart @@ -110,6 +110,7 @@ class MapOptions { this.zoomControlsEnabled = true, this.cameraTargetBounds, this.padding, + this.mapId, }) : assert( minZoomPreference == null || maxZoomPreference == null || @@ -200,6 +201,19 @@ class MapOptions { /// Null by default (no padding). final EdgeInsets? padding; + /// The map ID for cloud-based map styling. + /// + /// A map ID is a unique identifier that represents a single map instance. + /// This value can only be set at map initialization and cannot be changed afterwards. + /// Map IDs are created in Google Cloud Console and can be used to configure + /// advanced features like cloud-based map styling. + /// + /// See https://developers.google.com/maps/documentation/get-map-id + /// for more information about map IDs and how to create them. + /// + /// Null by default (no map ID). + final String? mapId; + @override String toString() => 'MapOptions(' @@ -216,7 +230,8 @@ class MapOptions { 'maxZoomPreference: $maxZoomPreference, ' 'zoomControlsEnabled: $zoomControlsEnabled, ' 'cameraTargetBounds: $cameraTargetBounds, ' - 'padding: $padding' + 'padding: $padding, ' + 'mapId: $mapId' ')'; } diff --git a/pigeons/messages.dart b/pigeons/messages.dart index e75b7396..f5a395c7 100644 --- a/pigeons/messages.dart +++ b/pigeons/messages.dart @@ -53,6 +53,7 @@ class MapOptionsDto { required this.zoomControlsEnabled, required this.cameraTargetBounds, required this.padding, + required this.mapId, }); /// The initial positioning of the camera in the map view. @@ -97,6 +98,10 @@ class MapOptionsDto { /// Specifies the padding for the map. final MapPaddingDto? padding; + + /// The map ID for advanced map options eg. cloud-based map styling. + /// This value can only be set on map initialization and cannot be changed afterwards. + final String? mapId; } /// Determines the initial visibility of the navigation UI on map initialization. From 7cb3ad59faa19244db04c81fc23a7846878aa9dd Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Wed, 19 Nov 2025 12:12:24 +0200 Subject: [PATCH 13/19] fix: convert test with new defaults --- .../kotlin/com/google/maps/flutter/navigation/Convert.kt | 1 + .../kotlin/com/google/maps/flutter/navigation/ConvertTest.kt | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt index ae09828f..95ee3621 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt @@ -363,6 +363,7 @@ object Convert { */ fun convertDisplayOptionsFromDto(displayOptions: NavigationDisplayOptionsDto): DisplayOptions { return DisplayOptions().apply { + // Only set if explicitly provided, otherwise SDK defaults are used. if (displayOptions.showDestinationMarkers != null) { this.hideDestinationMarkers(!displayOptions.showDestinationMarkers) } diff --git a/android/src/test/kotlin/com/google/maps/flutter/navigation/ConvertTest.kt b/android/src/test/kotlin/com/google/maps/flutter/navigation/ConvertTest.kt index 297cb746..4fe19f12 100644 --- a/android/src/test/kotlin/com/google/maps/flutter/navigation/ConvertTest.kt +++ b/android/src/test/kotlin/com/google/maps/flutter/navigation/ConvertTest.kt @@ -153,8 +153,9 @@ internal class ConvertTest { ) val convertedNone = Convert.convertDisplayOptionsFromDto(none) - assertEquals(false, convertedNone.showStopSigns) - assertEquals(false, convertedNone.showTrafficLights) + // When not specified, SDK defaults are used: stop signs and traffic lights are shown + assertEquals(true, convertedNone.showStopSigns) + assertEquals(true, convertedNone.showTrafficLights) assertEquals(false, convertedNone.hideDestinationMarkers) val convertedAllFalse = Convert.convertDisplayOptionsFromDto(allFalse) From bc6f66cb6d8c725634ba51c63a8be58dc031214f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Tue, 2 Dec 2025 16:40:55 +0800 Subject: [PATCH 14/19] feat: upgrade ios sdk to 10.6.0 and android sdk to 7.2.0 --- android/build.gradle | 2 +- .../GoogleMapsNavigationSessionManager.kt | 5 ++++ example/android/app/build.gradle.kts | 4 ++-- .../xcshareddata/swiftpm/Package.resolved | 23 ------------------- ios/google_navigation_flutter.podspec | 2 +- .../Package.resolved | 8 +++---- ios/google_navigation_flutter/Package.swift | 4 ++-- 7 files changed, 15 insertions(+), 33 deletions(-) delete mode 100644 example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved rename {example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm => ios/google_navigation_flutter}/Package.resolved (68%) diff --git a/android/build.gradle b/android/build.gradle index 2a7b58cf..ad4af5ce 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -77,7 +77,7 @@ android { dependencies { implementation 'androidx.car.app:app:1.7.0' implementation 'androidx.car.app:app-projected:1.7.0' - implementation 'com.google.android.libraries.navigation:navigation:7.1.0' + implementation 'com.google.android.libraries.navigation:navigation:7.2.0' testImplementation 'org.jetbrains.kotlin:kotlin-test' testImplementation 'io.mockk:mockk:1.13.8' testImplementation 'junit:junit:4.13.2' diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt index aa8deccd..9d997337 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt @@ -26,6 +26,7 @@ import com.google.android.gms.maps.model.LatLng import com.google.android.libraries.mapsplatform.turnbyturn.model.NavInfo import com.google.android.libraries.navigation.CustomRoutesOptions import com.google.android.libraries.navigation.DisplayOptions +import com.google.android.libraries.navigation.GpsAvailabilityChangeEvent import com.google.android.libraries.navigation.NavigationApi import com.google.android.libraries.navigation.NavigationApi.NavigatorListener import com.google.android.libraries.navigation.Navigator @@ -756,6 +757,10 @@ constructor( override fun onGpsAvailabilityUpdate(isGpsAvailable: Boolean) { navigationSessionEventApi.onGpsAvailabilityUpdate(isGpsAvailable) {} } + + override fun onGpsAvailabilityChange(p0: GpsAvailabilityChangeEvent?) { + TODO("Not yet implemented") + } } getRoadSnappedLocationProvider()?.addLocationListener(roadSnappedLocationListener) } diff --git a/example/android/app/build.gradle.kts b/example/android/app/build.gradle.kts index c70b8783..0e1159dc 100644 --- a/example/android/app/build.gradle.kts +++ b/example/android/app/build.gradle.kts @@ -39,7 +39,7 @@ fun findDartDefineValue(key: String): String? { android { namespace = "com.google.maps.flutter.navigation_example" compileSdk = flutter.compileSdkVersion - ndkVersion = "27.0.12077973" + ndkVersion = "28.2.13676358" compileOptions { // Flag to enable support for the new language APIs @@ -103,7 +103,7 @@ flutter { dependencies { implementation("androidx.car.app:app:1.7.0") implementation("androidx.car.app:app-projected:1.7.0") - implementation("com.google.android.libraries.navigation:navigation:7.1.0") + implementation("com.google.android.libraries.navigation:navigation:7.2.0") coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4") androidTestUtil("androidx.test:orchestrator:1.5.1") } diff --git a/example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 6a17a6f5..00000000 --- a/example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,23 +0,0 @@ -{ - "pins" : [ - { - "identity" : "ios-maps-sdk", - "kind" : "remoteSourceControl", - "location" : "https://github.com/googlemaps/ios-maps-sdk", - "state" : { - "revision" : "9c540f3b475a800e947a09b8903b212a6634cf30", - "version" : "10.0.0" - } - }, - { - "identity" : "ios-navigation-sdk", - "kind" : "remoteSourceControl", - "location" : "https://github.com/googlemaps/ios-navigation-sdk", - "state" : { - "revision" : "a3faa12da9a957420da8e1b448022f365fbc8400", - "version" : "10.0.0" - } - } - ], - "version" : 2 -} diff --git a/ios/google_navigation_flutter.podspec b/ios/google_navigation_flutter.podspec index 8440bfe7..ad0f1a92 100644 --- a/ios/google_navigation_flutter.podspec +++ b/ios/google_navigation_flutter.podspec @@ -15,7 +15,7 @@ A Google Maps Navigation Flutter plugin. s.source = { :path => '.' } s.source_files = 'google_navigation_flutter/Sources/google_navigation_flutter/**/*.swift' s.dependency 'Flutter' - s.dependency 'GoogleNavigation', '10.4.0' + s.dependency 'GoogleNavigation', '10.6.0' s.platform = :ios, '16.0' s.static_framework = true diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/google_navigation_flutter/Package.resolved similarity index 68% rename from example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved rename to ios/google_navigation_flutter/Package.resolved index 6a17a6f5..3e19e318 100644 --- a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ios/google_navigation_flutter/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/googlemaps/ios-maps-sdk", "state" : { - "revision" : "9c540f3b475a800e947a09b8903b212a6634cf30", - "version" : "10.0.0" + "revision" : "71e09fe4c751d59d7b1e524e998f786b429b1b14", + "version" : "10.6.0" } }, { @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/googlemaps/ios-navigation-sdk", "state" : { - "revision" : "a3faa12da9a957420da8e1b448022f365fbc8400", - "version" : "10.0.0" + "revision" : "bcee02b433831d29d1ac12a62038fd009c333f7b", + "version" : "10.6.0" } } ], diff --git a/ios/google_navigation_flutter/Package.swift b/ios/google_navigation_flutter/Package.swift index 4064fe98..297a2af7 100644 --- a/ios/google_navigation_flutter/Package.swift +++ b/ios/google_navigation_flutter/Package.swift @@ -28,11 +28,11 @@ let package = Package( dependencies: [ .package( url: "https://github.com/googlemaps/ios-navigation-sdk", - exact: "10.4.0" + exact: "10.6.0" ), .package( url: "https://github.com/googlemaps/ios-maps-sdk", - exact: "10.4.0" + exact: "10.6.0" ), ], targets: [ From e954f3171c731226d71c12d85a2ad5d3bca3b74b Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Wed, 3 Dec 2025 09:21:52 +0200 Subject: [PATCH 15/19] feat: implements GpsAvailabilityChangeEvent listener --- .../navigation/GoogleMapsBaseMapView.kt | 5 +- .../GoogleMapsNavigationSessionManager.kt | 11 +- .../maps/flutter/navigation/messages.g.kt | 96 +++++++++++--- .../integration_test/t02_session_test.dart | 3 + .../t07_event_listener_test.dart | 79 ++++++++++-- example/lib/pages/navigation.dart | 28 ++-- .../messages.g.swift | 96 +++++++++++--- lib/src/google_navigation_flutter.dart | 5 + lib/src/method_channel/messages.g.dart | 121 +++++++++++++++--- lib/src/method_channel/session_api.dart | 19 +++ .../google_navigation_flutter_navigator.dart | 58 ++++++++- lib/src/types/simulation.dart | 32 +++++ pigeons/messages.dart | 13 ++ test/messages_test.g.dart | 39 +++--- 14 files changed, 510 insertions(+), 95 deletions(-) diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsBaseMapView.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsBaseMapView.kt index 3ed66ac8..a7bf85ca 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsBaseMapView.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsBaseMapView.kt @@ -544,7 +544,10 @@ abstract class GoogleMapsBaseMapView( // [GoogleMapsNavigationSessionManager] to fetch the location // using the [FusedLocationProviderApi]. @Suppress("DEPRECATION") - return getMap().myLocation + val location = getMap().myLocation + // Return null explicitly if location is not available to avoid NullPointerException + // when the platform channel tries to serialize the Location object + return if (location != null && location.provider != null) location else null } fun getCameraPosition(): CameraPosition { diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt index 9d997337..b4f9eddc 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt @@ -758,8 +758,15 @@ constructor( navigationSessionEventApi.onGpsAvailabilityUpdate(isGpsAvailable) {} } - override fun onGpsAvailabilityChange(p0: GpsAvailabilityChangeEvent?) { - TODO("Not yet implemented") + override fun onGpsAvailabilityChange(event: GpsAvailabilityChangeEvent?) { + if (event != null) { + navigationSessionEventApi.onGpsAvailabilityChange( + GpsAvailabilityChangeEventDto( + isGpsLost = event.isGpsLost, + isGpsValidForNavigation = event.isGpsValidForNavigation, + ) + ) {} + } } } getRoadSnappedLocationProvider()?.addLocationListener(roadSnappedLocationListener) diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt index 9b809b2d..e4317645 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt @@ -1765,6 +1765,36 @@ data class SpeedingUpdatedEventDto( override fun hashCode(): Int = toList().hashCode() } +/** Generated class from Pigeon that represents data sent in messages. */ +data class GpsAvailabilityChangeEventDto( + val isGpsLost: Boolean, + val isGpsValidForNavigation: Boolean, +) { + companion object { + fun fromList(pigeonVar_list: List): GpsAvailabilityChangeEventDto { + val isGpsLost = pigeonVar_list[0] as Boolean + val isGpsValidForNavigation = pigeonVar_list[1] as Boolean + return GpsAvailabilityChangeEventDto(isGpsLost, isGpsValidForNavigation) + } + } + + fun toList(): List { + return listOf(isGpsLost, isGpsValidForNavigation) + } + + override fun equals(other: Any?): Boolean { + if (other !is GpsAvailabilityChangeEventDto) { + return false + } + if (this === other) { + return true + } + return MessagesPigeonUtils.deepEquals(toList(), other.toList()) + } + + override fun hashCode(): Int = toList().hashCode() +} + /** Generated class from Pigeon that represents data sent in messages. */ data class SpeedAlertOptionsThresholdPercentageDto( val percentage: Double, @@ -2364,33 +2394,38 @@ private open class messagesPigeonCodec : StandardMessageCodec() { } 182.toByte() -> { return (readValue(buffer) as? List)?.let { - SpeedAlertOptionsThresholdPercentageDto.fromList(it) + GpsAvailabilityChangeEventDto.fromList(it) } } 183.toByte() -> { - return (readValue(buffer) as? List)?.let { SpeedAlertOptionsDto.fromList(it) } + return (readValue(buffer) as? List)?.let { + SpeedAlertOptionsThresholdPercentageDto.fromList(it) + } } 184.toByte() -> { + return (readValue(buffer) as? List)?.let { SpeedAlertOptionsDto.fromList(it) } + } + 185.toByte() -> { return (readValue(buffer) as? List)?.let { RouteSegmentTrafficDataRoadStretchRenderingDataDto.fromList(it) } } - 185.toByte() -> { + 186.toByte() -> { return (readValue(buffer) as? List)?.let { RouteSegmentTrafficDataDto.fromList(it) } } - 186.toByte() -> { + 187.toByte() -> { return (readValue(buffer) as? List)?.let { RouteSegmentDto.fromList(it) } } - 187.toByte() -> { + 188.toByte() -> { return (readValue(buffer) as? List)?.let { LaneDirectionDto.fromList(it) } } - 188.toByte() -> { + 189.toByte() -> { return (readValue(buffer) as? List)?.let { LaneDto.fromList(it) } } - 189.toByte() -> { + 190.toByte() -> { return (readValue(buffer) as? List)?.let { StepInfoDto.fromList(it) } } - 190.toByte() -> { + 191.toByte() -> { return (readValue(buffer) as? List)?.let { NavInfoDto.fromList(it) } } else -> super.readValueOfType(type, buffer) @@ -2611,42 +2646,46 @@ private open class messagesPigeonCodec : StandardMessageCodec() { stream.write(181) writeValue(stream, value.toList()) } - is SpeedAlertOptionsThresholdPercentageDto -> { + is GpsAvailabilityChangeEventDto -> { stream.write(182) writeValue(stream, value.toList()) } - is SpeedAlertOptionsDto -> { + is SpeedAlertOptionsThresholdPercentageDto -> { stream.write(183) writeValue(stream, value.toList()) } - is RouteSegmentTrafficDataRoadStretchRenderingDataDto -> { + is SpeedAlertOptionsDto -> { stream.write(184) writeValue(stream, value.toList()) } - is RouteSegmentTrafficDataDto -> { + is RouteSegmentTrafficDataRoadStretchRenderingDataDto -> { stream.write(185) writeValue(stream, value.toList()) } - is RouteSegmentDto -> { + is RouteSegmentTrafficDataDto -> { stream.write(186) writeValue(stream, value.toList()) } - is LaneDirectionDto -> { + is RouteSegmentDto -> { stream.write(187) writeValue(stream, value.toList()) } - is LaneDto -> { + is LaneDirectionDto -> { stream.write(188) writeValue(stream, value.toList()) } - is StepInfoDto -> { + is LaneDto -> { stream.write(189) writeValue(stream, value.toList()) } - is NavInfoDto -> { + is StepInfoDto -> { stream.write(190) writeValue(stream, value.toList()) } + is NavInfoDto -> { + stream.write(191) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -6922,6 +6961,29 @@ class NavigationSessionEventApi( } } + /** Android-only event. */ + fun onGpsAvailabilityChange( + eventArg: GpsAvailabilityChangeEventDto, + callback: (Result) -> Unit, + ) { + val separatedMessageChannelSuffix = + if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = + "dev.flutter.pigeon.google_navigation_flutter.NavigationSessionEventApi.onGpsAvailabilityChange$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(eventArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(MessagesPigeonUtils.createConnectionError(channelName))) + } + } + } + /** Turn-by-Turn navigation events. */ fun onNavInfo(navInfoArg: NavInfoDto, callback: (Result) -> Unit) { val separatedMessageChannelSuffix = diff --git a/example/integration_test/t02_session_test.dart b/example/integration_test/t02_session_test.dart index 17a1c69e..b560233e 100644 --- a/example/integration_test/t02_session_test.dart +++ b/example/integration_test/t02_session_test.dart @@ -28,6 +28,9 @@ void main() { patrol('Test terms and conditions (TOS) dialog acceptance', ( PatrolIntegrationTester $, ) async { + // Be sure location is enabled. + await $.native.enableLocation(); + // Grant the location permission. await checkLocationDialogAcceptance($); diff --git a/example/integration_test/t07_event_listener_test.dart b/example/integration_test/t07_event_listener_test.dart index 890c4e4b..97646e46 100644 --- a/example/integration_test/t07_event_listener_test.dart +++ b/example/integration_test/t07_event_listener_test.dart @@ -398,9 +398,12 @@ void main() { }); patrol( - 'Test navigation onRerouting and onGpsAvailability event listeners', + 'Test navigation onRerouting, onGpsAvailability and onGpsAvailabilityChange event listeners', (PatrolIntegrationTester $) async { - final Completer eventReceived = Completer(); + final Completer reroutingEventReceived = Completer(); + final Completer gpsAvailabilityEventReceived = Completer(); + final Completer gpsAvailabilityChangeEventReceived = + Completer(); /// Set up navigation. await startNavigationWithoutDestination($); @@ -409,26 +412,46 @@ void main() { final StreamSubscription onReroutingSubscription = GoogleMapsNavigator.setOnReroutingListener( expectAsync0(() { + $.log('Rerouting event received'); + /// Complete the eventReceived completer only once. - if (!eventReceived.isCompleted) { - eventReceived.complete(); + if (!reroutingEventReceived.isCompleted) { + reroutingEventReceived.complete(); } }, max: -1), ); await $.pumpAndSettle(); - /// The events are not tested because there's currently no reliable way to trigger them. + /// Set up GPS availability listeners with completers. void onGpsAvailability(GpsAvailabilityUpdatedEvent event) { - $.log('GpsAvailabilityEvent: $event'); + $.log('GpsAvailabilityEvent (deprecated): $event'); + if (!gpsAvailabilityEventReceived.isCompleted) { + gpsAvailabilityEventReceived.complete(); + } + } + + void onGpsAvailabilityChange(GpsAvailabilityChangeEvent event) { + $.log( + 'GpsAvailabilityChangeEvent: isGpsLost=${event.isGpsLost}, isGpsValidForNavigation=${event.isGpsValidForNavigation}', + ); + if (!gpsAvailabilityChangeEventReceived.isCompleted) { + gpsAvailabilityChangeEventReceived.complete(); + } } - /// Set up the gpsAvailability listener with the test. + /// Set up both the old (deprecated) and new gpsAvailability listeners. final StreamSubscription onGpsAvailabilitySubscription = await GoogleMapsNavigator.setOnGpsAvailabilityListener( onGpsAvailability, ); + final StreamSubscription + onGpsAvailabilityChangeSubscription = + await GoogleMapsNavigator.setOnGpsAvailabilityChangeListener( + onGpsAvailabilityChange, + ); + /// Simulate location. await GoogleMapsNavigator.simulator.setUserLocation( const LatLng(latitude: 37.790693, longitude: -122.4132157), @@ -463,6 +486,7 @@ void main() { /// Start guidance. await GoogleMapsNavigator.startGuidance(); + await $.pumpAndSettle(); /// Start simulation to a different destination. @@ -474,10 +498,43 @@ void main() { ); await $.pumpAndSettle(); - /// Wait until the event is received and then test cancelling the subscriptions. - await eventReceived.future; - await onReroutingSubscription.cancel(); - await onGpsAvailabilitySubscription.cancel(); + Future cancelSubscriptionsAndResetState() async { + await $.native.enableLocation(); + await onReroutingSubscription.cancel(); + await onGpsAvailabilitySubscription.cancel(); + await onGpsAvailabilityChangeSubscription.cancel(); + } + + /// Wait for rerouting event (this should fire reliably). + await reroutingEventReceived.future.timeout( + const Duration(seconds: 10), + onTimeout: () { + cancelSubscriptionsAndResetState(); + fail('Rerouting event timed out'); + }, + ); + + await $.native.disableLocation(); + + /// Wait for GPS availability events with timeout. + await gpsAvailabilityEventReceived.future.timeout( + const Duration(seconds: 20), + onTimeout: () { + cancelSubscriptionsAndResetState(); + fail('GpsAvailabilityEvent timed out'); + }, + ); + + await gpsAvailabilityChangeEventReceived.future.timeout( + const Duration(seconds: 20), + onTimeout: () { + cancelSubscriptionsAndResetState(); + fail('GpsAvailabilityChangeEvent timed out'); + }, + ); + + /// Cancel all subscriptions. + await cancelSubscriptionsAndResetState(); }, skip: !Platform.isAndroid, ); diff --git a/example/lib/pages/navigation.dart b/example/lib/pages/navigation.dart index 27ee59b2..c2b3c77b 100644 --- a/example/lib/pages/navigation.dart +++ b/example/lib/pages/navigation.dart @@ -92,7 +92,7 @@ class _NavigationPageState extends ExamplePageState { int _onRoadSnappedRawLocationUpdatedEventCallCount = 0; int _onTrafficUpdatedEventCallCount = 0; int _onReroutingEventCallCount = 0; - int _onGpsAvailabilityEventCallCount = 0; + int _onGpsAvailabilityChangeEventCallCount = 0; int _onArrivalEventCallCount = 0; int _onSpeedingUpdatedEventCallCount = 0; int _onRecenterButtonClickedEventCallCount = 0; @@ -141,7 +141,8 @@ class _NavigationPageState extends ExamplePageState { StreamSubscription? _speedUpdatedSubscription; StreamSubscription? _onArrivalSubscription; StreamSubscription? _onReRoutingSubscription; - StreamSubscription? _onGpsAvailabilitySubscription; + StreamSubscription? + _onGpsAvailabilityChangeSubscription; StreamSubscription? _trafficUpdatedSubscription; StreamSubscription? _onRouteChangedSubscription; StreamSubscription? @@ -359,9 +360,9 @@ class _NavigationPageState extends ExamplePageState { _onReRoutingSubscription = GoogleMapsNavigator.setOnReroutingListener( _onReroutingEvent, ); - _onGpsAvailabilitySubscription = - await GoogleMapsNavigator.setOnGpsAvailabilityListener( - _onGpsAvailabilityEvent, + _onGpsAvailabilityChangeSubscription = + await GoogleMapsNavigator.setOnGpsAvailabilityChangeListener( + _onGpsAvailabilityChangeEvent, ); _trafficUpdatedSubscription = GoogleMapsNavigator.setTrafficUpdatedListener( _onTrafficUpdatedEvent, @@ -400,8 +401,8 @@ class _NavigationPageState extends ExamplePageState { _onReRoutingSubscription?.cancel(); _onReRoutingSubscription = null; - _onGpsAvailabilitySubscription?.cancel(); - _onGpsAvailabilitySubscription = null; + _onGpsAvailabilityChangeSubscription?.cancel(); + _onGpsAvailabilityChangeSubscription = null; _trafficUpdatedSubscription?.cancel(); _trafficUpdatedSubscription = null; @@ -505,9 +506,10 @@ class _NavigationPageState extends ExamplePageState { }); } - void _onGpsAvailabilityEvent(GpsAvailabilityUpdatedEvent event) { + void _onGpsAvailabilityChangeEvent(GpsAvailabilityChangeEvent event) { + debugPrint('GPS availability change event: $event'); setState(() { - _onGpsAvailabilityEventCallCount += 1; + _onGpsAvailabilityChangeEventCallCount += 1; }); } @@ -1450,8 +1452,12 @@ class _NavigationPageState extends ExamplePageState { if (Platform.isAndroid) Card( child: ListTile( - title: const Text('On GPS availability event call count'), - trailing: Text(_onGpsAvailabilityEventCallCount.toString()), + title: const Text( + 'On GPS availability change event call count', + ), + trailing: Text( + _onGpsAvailabilityChangeEventCallCount.toString(), + ), ), ), Card( diff --git a/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift b/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift index dcf877ab..6fb465f2 100644 --- a/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift +++ b/ios/google_navigation_flutter/Sources/google_navigation_flutter/messages.g.swift @@ -1650,6 +1650,35 @@ struct SpeedingUpdatedEventDto: Hashable { } } +/// Generated class from Pigeon that represents data sent in messages. +struct GpsAvailabilityChangeEventDto: Hashable { + var isGpsLost: Bool + var isGpsValidForNavigation: Bool + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> GpsAvailabilityChangeEventDto? { + let isGpsLost = pigeonVar_list[0] as! Bool + let isGpsValidForNavigation = pigeonVar_list[1] as! Bool + + return GpsAvailabilityChangeEventDto( + isGpsLost: isGpsLost, + isGpsValidForNavigation: isGpsValidForNavigation + ) + } + func toList() -> [Any?] { + return [ + isGpsLost, + isGpsValidForNavigation, + ] + } + static func == (lhs: GpsAvailabilityChangeEventDto, rhs: GpsAvailabilityChangeEventDto) -> Bool { + return deepEqualsmessages(lhs.toList(), rhs.toList()) + } + func hash(into hasher: inout Hasher) { + deepHashmessages(value: toList(), hasher: &hasher) + } +} + /// Generated class from Pigeon that represents data sent in messages. struct SpeedAlertOptionsThresholdPercentageDto: Hashable { var percentage: Double @@ -2239,23 +2268,25 @@ private class MessagesPigeonCodecReader: FlutterStandardReader { case 181: return SpeedingUpdatedEventDto.fromList(self.readValue() as! [Any?]) case 182: - return SpeedAlertOptionsThresholdPercentageDto.fromList(self.readValue() as! [Any?]) + return GpsAvailabilityChangeEventDto.fromList(self.readValue() as! [Any?]) case 183: - return SpeedAlertOptionsDto.fromList(self.readValue() as! [Any?]) + return SpeedAlertOptionsThresholdPercentageDto.fromList(self.readValue() as! [Any?]) case 184: + return SpeedAlertOptionsDto.fromList(self.readValue() as! [Any?]) + case 185: return RouteSegmentTrafficDataRoadStretchRenderingDataDto.fromList( self.readValue() as! [Any?]) - case 185: - return RouteSegmentTrafficDataDto.fromList(self.readValue() as! [Any?]) case 186: - return RouteSegmentDto.fromList(self.readValue() as! [Any?]) + return RouteSegmentTrafficDataDto.fromList(self.readValue() as! [Any?]) case 187: - return LaneDirectionDto.fromList(self.readValue() as! [Any?]) + return RouteSegmentDto.fromList(self.readValue() as! [Any?]) case 188: - return LaneDto.fromList(self.readValue() as! [Any?]) + return LaneDirectionDto.fromList(self.readValue() as! [Any?]) case 189: - return StepInfoDto.fromList(self.readValue() as! [Any?]) + return LaneDto.fromList(self.readValue() as! [Any?]) case 190: + return StepInfoDto.fromList(self.readValue() as! [Any?]) + case 191: return NavInfoDto.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -2424,33 +2455,36 @@ private class MessagesPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? SpeedingUpdatedEventDto { super.writeByte(181) super.writeValue(value.toList()) - } else if let value = value as? SpeedAlertOptionsThresholdPercentageDto { + } else if let value = value as? GpsAvailabilityChangeEventDto { super.writeByte(182) super.writeValue(value.toList()) - } else if let value = value as? SpeedAlertOptionsDto { + } else if let value = value as? SpeedAlertOptionsThresholdPercentageDto { super.writeByte(183) super.writeValue(value.toList()) - } else if let value = value as? RouteSegmentTrafficDataRoadStretchRenderingDataDto { + } else if let value = value as? SpeedAlertOptionsDto { super.writeByte(184) super.writeValue(value.toList()) - } else if let value = value as? RouteSegmentTrafficDataDto { + } else if let value = value as? RouteSegmentTrafficDataRoadStretchRenderingDataDto { super.writeByte(185) super.writeValue(value.toList()) - } else if let value = value as? RouteSegmentDto { + } else if let value = value as? RouteSegmentTrafficDataDto { super.writeByte(186) super.writeValue(value.toList()) - } else if let value = value as? LaneDirectionDto { + } else if let value = value as? RouteSegmentDto { super.writeByte(187) super.writeValue(value.toList()) - } else if let value = value as? LaneDto { + } else if let value = value as? LaneDirectionDto { super.writeByte(188) super.writeValue(value.toList()) - } else if let value = value as? StepInfoDto { + } else if let value = value as? LaneDto { super.writeByte(189) super.writeValue(value.toList()) - } else if let value = value as? NavInfoDto { + } else if let value = value as? StepInfoDto { super.writeByte(190) super.writeValue(value.toList()) + } else if let value = value as? NavInfoDto { + super.writeByte(191) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -5663,6 +5697,10 @@ protocol NavigationSessionEventApiProtocol { /// Android-only event. func onGpsAvailabilityUpdate( available availableArg: Bool, completion: @escaping (Result) -> Void) + /// Android-only event. + func onGpsAvailabilityChange( + event eventArg: GpsAvailabilityChangeEventDto, + completion: @escaping (Result) -> Void) /// Turn-by-Turn navigation events. func onNavInfo( navInfo navInfoArg: NavInfoDto, completion: @escaping (Result) -> Void) @@ -5877,6 +5915,30 @@ class NavigationSessionEventApi: NavigationSessionEventApiProtocol { } } } + /// Android-only event. + func onGpsAvailabilityChange( + event eventArg: GpsAvailabilityChangeEventDto, + completion: @escaping (Result) -> Void + ) { + let channelName: String = + "dev.flutter.pigeon.google_navigation_flutter.NavigationSessionEventApi.onGpsAvailabilityChange\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([eventArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } /// Turn-by-Turn navigation events. func onNavInfo( navInfo navInfoArg: NavInfoDto, completion: @escaping (Result) -> Void diff --git a/lib/src/google_navigation_flutter.dart b/lib/src/google_navigation_flutter.dart index 1a4d72cd..a2bbe355 100644 --- a/lib/src/google_navigation_flutter.dart +++ b/lib/src/google_navigation_flutter.dart @@ -46,9 +46,14 @@ typedef OnArrivalEventCallback = void Function(OnArrivalEvent onArrivalEvent); typedef OnReroutingEventCallback = void Function(); /// Called during GPS availability event. (Android only). +@Deprecated('Use OnGpsAvailabilityChangeEventCallback instead') typedef OnGpsAvailabilityEventCallback = void Function(GpsAvailabilityUpdatedEvent gpsAvailabilityUpdatedEvent); +/// Called during GPS availability change event. (Android only). +typedef OnGpsAvailabilityChangeEventCallback = + void Function(GpsAvailabilityChangeEvent gpsAvailabilityChangeEvent); + /// Called during traffic updated event. (Android only) typedef OnTrafficUpdatedEventCallback = void Function(); diff --git a/lib/src/method_channel/messages.g.dart b/lib/src/method_channel/messages.g.dart index d597d664..1f215bb3 100644 --- a/lib/src/method_channel/messages.g.dart +++ b/lib/src/method_channel/messages.g.dart @@ -2060,6 +2060,50 @@ class SpeedingUpdatedEventDto { int get hashCode => Object.hashAll(_toList()); } +class GpsAvailabilityChangeEventDto { + GpsAvailabilityChangeEventDto({ + required this.isGpsLost, + required this.isGpsValidForNavigation, + }); + + bool isGpsLost; + + bool isGpsValidForNavigation; + + List _toList() { + return [isGpsLost, isGpsValidForNavigation]; + } + + Object encode() { + return _toList(); + } + + static GpsAvailabilityChangeEventDto decode(Object result) { + result as List; + return GpsAvailabilityChangeEventDto( + isGpsLost: result[0]! as bool, + isGpsValidForNavigation: result[1]! as bool, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! GpsAvailabilityChangeEventDto || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); +} + class SpeedAlertOptionsThresholdPercentageDto { SpeedAlertOptionsThresholdPercentageDto({ required this.percentage, @@ -2768,33 +2812,36 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is SpeedingUpdatedEventDto) { buffer.putUint8(181); writeValue(buffer, value.encode()); - } else if (value is SpeedAlertOptionsThresholdPercentageDto) { + } else if (value is GpsAvailabilityChangeEventDto) { buffer.putUint8(182); writeValue(buffer, value.encode()); - } else if (value is SpeedAlertOptionsDto) { + } else if (value is SpeedAlertOptionsThresholdPercentageDto) { buffer.putUint8(183); writeValue(buffer, value.encode()); - } else if (value is RouteSegmentTrafficDataRoadStretchRenderingDataDto) { + } else if (value is SpeedAlertOptionsDto) { buffer.putUint8(184); writeValue(buffer, value.encode()); - } else if (value is RouteSegmentTrafficDataDto) { + } else if (value is RouteSegmentTrafficDataRoadStretchRenderingDataDto) { buffer.putUint8(185); writeValue(buffer, value.encode()); - } else if (value is RouteSegmentDto) { + } else if (value is RouteSegmentTrafficDataDto) { buffer.putUint8(186); writeValue(buffer, value.encode()); - } else if (value is LaneDirectionDto) { + } else if (value is RouteSegmentDto) { buffer.putUint8(187); writeValue(buffer, value.encode()); - } else if (value is LaneDto) { + } else if (value is LaneDirectionDto) { buffer.putUint8(188); writeValue(buffer, value.encode()); - } else if (value is StepInfoDto) { + } else if (value is LaneDto) { buffer.putUint8(189); writeValue(buffer, value.encode()); - } else if (value is NavInfoDto) { + } else if (value is StepInfoDto) { buffer.putUint8(190); writeValue(buffer, value.encode()); + } else if (value is NavInfoDto) { + buffer.putUint8(191); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -2939,26 +2986,28 @@ class _PigeonCodec extends StandardMessageCodec { case 181: return SpeedingUpdatedEventDto.decode(readValue(buffer)!); case 182: + return GpsAvailabilityChangeEventDto.decode(readValue(buffer)!); + case 183: return SpeedAlertOptionsThresholdPercentageDto.decode( readValue(buffer)!, ); - case 183: - return SpeedAlertOptionsDto.decode(readValue(buffer)!); case 184: + return SpeedAlertOptionsDto.decode(readValue(buffer)!); + case 185: return RouteSegmentTrafficDataRoadStretchRenderingDataDto.decode( readValue(buffer)!, ); - case 185: - return RouteSegmentTrafficDataDto.decode(readValue(buffer)!); case 186: - return RouteSegmentDto.decode(readValue(buffer)!); + return RouteSegmentTrafficDataDto.decode(readValue(buffer)!); case 187: - return LaneDirectionDto.decode(readValue(buffer)!); + return RouteSegmentDto.decode(readValue(buffer)!); case 188: - return LaneDto.decode(readValue(buffer)!); + return LaneDirectionDto.decode(readValue(buffer)!); case 189: - return StepInfoDto.decode(readValue(buffer)!); + return LaneDto.decode(readValue(buffer)!); case 190: + return StepInfoDto.decode(readValue(buffer)!); + case 191: return NavInfoDto.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -7797,6 +7846,9 @@ abstract class NavigationSessionEventApi { /// Android-only event. void onGpsAvailabilityUpdate(bool available); + /// Android-only event. + void onGpsAvailabilityChange(GpsAvailabilityChangeEventDto event); + /// Turn-by-Turn navigation events. void onNavInfo(NavInfoDto navInfo); @@ -8097,6 +8149,41 @@ abstract class NavigationSessionEventApi { }); } } + { + final BasicMessageChannel + pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.google_navigation_flutter.NavigationSessionEventApi.onGpsAvailabilityChange$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert( + message != null, + 'Argument for dev.flutter.pigeon.google_navigation_flutter.NavigationSessionEventApi.onGpsAvailabilityChange was null.', + ); + final List args = (message as List?)!; + final GpsAvailabilityChangeEventDto? arg_event = + (args[0] as GpsAvailabilityChangeEventDto?); + assert( + arg_event != null, + 'Argument for dev.flutter.pigeon.google_navigation_flutter.NavigationSessionEventApi.onGpsAvailabilityChange was null, expected non-null GpsAvailabilityChangeEventDto.', + ); + try { + api.onGpsAvailabilityChange(arg_event!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } + }); + } + } { final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( diff --git a/lib/src/method_channel/session_api.dart b/lib/src/method_channel/session_api.dart index c74093c7..e791f5dc 100644 --- a/lib/src/method_channel/session_api.dart +++ b/lib/src/method_channel/session_api.dart @@ -626,12 +626,21 @@ class NavigationSessionAPIImpl { } /// Get navigation on GPS availability update event stream from the navigation session. + @Deprecated('Use getNavigationOnGpsAvailabilityChangeEventStream instead') Stream getNavigationOnGpsAvailabilityUpdateEventStream() { return _sessionEventStreamController.stream .whereType(); } + /// Get navigation on GPS availability change event stream from the navigation session. + /// Android only. + Stream + getNavigationOnGpsAvailabilityChangeEventStream() { + return _sessionEventStreamController.stream + .whereType(); + } + /// Get navigation traffic updated event stream from the navigation session. Stream getNavigationTrafficUpdatedEventStream() { return _sessionEventStreamController.stream @@ -711,6 +720,16 @@ class NavigationSessionEventApiImpl implements NavigationSessionEventApi { ); } + @override + void onGpsAvailabilityChange(GpsAvailabilityChangeEventDto event) { + sessionEventStreamController.add( + GpsAvailabilityChangeEvent( + isGpsLost: event.isGpsLost, + isGpsValidForNavigation: event.isGpsValidForNavigation, + ), + ); + } + @override void onRouteChanged() { sessionEventStreamController.add(_RouteChangedEvent()); diff --git a/lib/src/navigator/google_navigation_flutter_navigator.dart b/lib/src/navigator/google_navigation_flutter_navigator.dart index dde17263..7c3762f5 100644 --- a/lib/src/navigator/google_navigation_flutter_navigator.dart +++ b/lib/src/navigator/google_navigation_flutter_navigator.dart @@ -63,7 +63,8 @@ class GoogleMapsNavigator { // Enable road-snapped location updates if there are subscriptions to them. if ((_roadSnappedLocationUpdatedController?.hasListener ?? false) || (_roadSnappedRawLocationUpdatedController?.hasListener ?? false) || - (_gpsAvailabilityUpdatedController?.hasListener ?? false)) { + (_gpsAvailabilityUpdatedController?.hasListener ?? false) || + (_gpsAvailabilityChangeController?.hasListener ?? false)) { await GoogleMapsNavigationPlatform.instance.navigationSessionAPI .enableRoadSnappedLocationUpdates(); } @@ -208,6 +209,7 @@ class GoogleMapsNavigator { /// // When done with the subscription /// await subscription.cancel(); /// ``` + @Deprecated('Use setOnGpsAvailabilityChangeListener instead') static Future> setOnGpsAvailabilityListener(OnGpsAvailabilityEventCallback listener) async { if (_gpsAvailabilityUpdatedController == null) { @@ -235,6 +237,57 @@ class GoogleMapsNavigator { static StreamController? _gpsAvailabilityUpdatedController; + /// Sets the event channel listener for the GPS availability change events. + /// (Android only). + /// + /// Setting this listener will also register road snapped location listener + /// on native side. + /// + /// DISCLAIMER: This is an EXPERIMENTAL API and its behaviors may be subject + /// to removal or breaking changes in future releases. + /// + /// Returns a [StreamSubscription] for GPS availability change events. + /// This subscription must be canceled using `cancel()` when it is no longer + /// needed to stop receiving events and allow the stream to perform necessary + /// cleanup, such as releasing resources or shutting down event sources. The + /// cleanup is asynchronous, and the `cancel()` method returns a Future that + /// completes once the cleanup is done. + /// + /// Example usage: + /// ```dart + /// final subscription = setOnGpsAvailabilityChangeListener(yourEventHandler); + /// // When done with the subscription + /// await subscription.cancel(); + /// ``` + static Future> + setOnGpsAvailabilityChangeListener( + OnGpsAvailabilityChangeEventCallback listener, + ) async { + if (_gpsAvailabilityChangeController == null) { + _gpsAvailabilityChangeController = + StreamController.broadcast( + onCancel: () { + _disableRoadSnappedLocationUpdatesIfNoActiveListeners(); + }, + onListen: () { + GoogleMapsNavigationPlatform.instance.navigationSessionAPI + .enableRoadSnappedLocationUpdates(); + }, + ); + unawaited( + _gpsAvailabilityChangeController!.addStream( + GoogleMapsNavigationPlatform.instance.navigationSessionAPI + .getNavigationOnGpsAvailabilityChangeEventStream(), + ), + ); + } + + return _gpsAvailabilityChangeController!.stream.listen(listener); + } + + static StreamController? + _gpsAvailabilityChangeController; + /// Sets the event channel listener for the traffic updated events. (Android only) /// /// Returns a [StreamSubscription] for traffic updated events. @@ -468,7 +521,8 @@ class GoogleMapsNavigator { static void _disableRoadSnappedLocationUpdatesIfNoActiveListeners() { if (!(_roadSnappedLocationUpdatedController?.hasListener ?? false) && !(_roadSnappedRawLocationUpdatedController?.hasListener ?? false) && - !(_gpsAvailabilityUpdatedController?.hasListener ?? false)) { + !(_gpsAvailabilityUpdatedController?.hasListener ?? false) && + !(_gpsAvailabilityChangeController?.hasListener ?? false)) { GoogleMapsNavigationPlatform.instance.navigationSessionAPI .disableRoadSnappedLocationUpdates(); } diff --git a/lib/src/types/simulation.dart b/lib/src/types/simulation.dart index 7f98b57b..05a373c6 100644 --- a/lib/src/types/simulation.dart +++ b/lib/src/types/simulation.dart @@ -82,6 +82,9 @@ class RoadSnappedRawLocationUpdatedEvent { /// GpsAvailabilityUpdated event message (Android only). /// {@category Navigation} +@Deprecated( + 'Use getNavigationOnGpsAvailabilityChangeEventStream and GpsAvailabilityChangeEvent instead', +) class GpsAvailabilityUpdatedEvent { /// Initialize GPS availability updated event message. GpsAvailabilityUpdatedEvent({required this.available}); @@ -93,6 +96,35 @@ class GpsAvailabilityUpdatedEvent { String toString() => 'GpsAvailabilityUpdatedEvent(available: $available)'; } +/// GpsAvailabilityChange event message (Android only). +/// {@category Navigation} +class GpsAvailabilityChangeEvent { + /// Initialize GPS availability change event message. + GpsAvailabilityChangeEvent({ + required this.isGpsLost, + required this.isGpsValidForNavigation, + }); + + /// Indicates a GPS signal or other sensors good enough for a reasonably certain location have been lost. + /// + /// This state is triggered after a short timeout (10 seconds) and serves as an early warning of potential signal issues. + /// For example, the "Searching for GPS" UI message may be shown when this value is true. + final bool isGpsLost; + + /// Indicates a GPS signal or other sensors are in general good enough for use in navigation. + /// + /// Note that this value takes into account the frequent failure of GPS at the start of nav, + /// and doesn't become true until some time later. + final bool isGpsValidForNavigation; + + @override + String toString() => + 'GpsAvailabilityChangeEvent(' + 'isGpsLost: $isGpsLost, ' + 'isGpsValidForNavigation: $isGpsValidForNavigation' + ')'; +} + /// Navigation simulation options. /// {@category Navigation} class SimulationOptions { diff --git a/pigeons/messages.dart b/pigeons/messages.dart index f5a395c7..3a76be44 100644 --- a/pigeons/messages.dart +++ b/pigeons/messages.dart @@ -774,6 +774,16 @@ class SpeedingUpdatedEventDto { final SpeedAlertSeverityDto severity; } +class GpsAvailabilityChangeEventDto { + GpsAvailabilityChangeEventDto({ + required this.isGpsLost, + required this.isGpsValidForNavigation, + }); + + final bool isGpsLost; + final bool isGpsValidForNavigation; +} + class SpeedAlertOptionsThresholdPercentageDto { SpeedAlertOptionsThresholdPercentageDto({ required this.percentage, @@ -1330,6 +1340,9 @@ abstract class NavigationSessionEventApi { /// Android-only event. void onGpsAvailabilityUpdate(bool available); + /// Android-only event. + void onGpsAvailabilityChange(GpsAvailabilityChangeEventDto event); + /// Turn-by-Turn navigation events. void onNavInfo(NavInfoDto navInfo); diff --git a/test/messages_test.g.dart b/test/messages_test.g.dart index 740beba1..27e6d24d 100644 --- a/test/messages_test.g.dart +++ b/test/messages_test.g.dart @@ -191,33 +191,36 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is SpeedingUpdatedEventDto) { buffer.putUint8(181); writeValue(buffer, value.encode()); - } else if (value is SpeedAlertOptionsThresholdPercentageDto) { + } else if (value is GpsAvailabilityChangeEventDto) { buffer.putUint8(182); writeValue(buffer, value.encode()); - } else if (value is SpeedAlertOptionsDto) { + } else if (value is SpeedAlertOptionsThresholdPercentageDto) { buffer.putUint8(183); writeValue(buffer, value.encode()); - } else if (value is RouteSegmentTrafficDataRoadStretchRenderingDataDto) { + } else if (value is SpeedAlertOptionsDto) { buffer.putUint8(184); writeValue(buffer, value.encode()); - } else if (value is RouteSegmentTrafficDataDto) { + } else if (value is RouteSegmentTrafficDataRoadStretchRenderingDataDto) { buffer.putUint8(185); writeValue(buffer, value.encode()); - } else if (value is RouteSegmentDto) { + } else if (value is RouteSegmentTrafficDataDto) { buffer.putUint8(186); writeValue(buffer, value.encode()); - } else if (value is LaneDirectionDto) { + } else if (value is RouteSegmentDto) { buffer.putUint8(187); writeValue(buffer, value.encode()); - } else if (value is LaneDto) { + } else if (value is LaneDirectionDto) { buffer.putUint8(188); writeValue(buffer, value.encode()); - } else if (value is StepInfoDto) { + } else if (value is LaneDto) { buffer.putUint8(189); writeValue(buffer, value.encode()); - } else if (value is NavInfoDto) { + } else if (value is StepInfoDto) { buffer.putUint8(190); writeValue(buffer, value.encode()); + } else if (value is NavInfoDto) { + buffer.putUint8(191); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -362,26 +365,28 @@ class _PigeonCodec extends StandardMessageCodec { case 181: return SpeedingUpdatedEventDto.decode(readValue(buffer)!); case 182: + return GpsAvailabilityChangeEventDto.decode(readValue(buffer)!); + case 183: return SpeedAlertOptionsThresholdPercentageDto.decode( readValue(buffer)!, ); - case 183: - return SpeedAlertOptionsDto.decode(readValue(buffer)!); case 184: + return SpeedAlertOptionsDto.decode(readValue(buffer)!); + case 185: return RouteSegmentTrafficDataRoadStretchRenderingDataDto.decode( readValue(buffer)!, ); - case 185: - return RouteSegmentTrafficDataDto.decode(readValue(buffer)!); case 186: - return RouteSegmentDto.decode(readValue(buffer)!); + return RouteSegmentTrafficDataDto.decode(readValue(buffer)!); case 187: - return LaneDirectionDto.decode(readValue(buffer)!); + return RouteSegmentDto.decode(readValue(buffer)!); case 188: - return LaneDto.decode(readValue(buffer)!); + return LaneDirectionDto.decode(readValue(buffer)!); case 189: - return StepInfoDto.decode(readValue(buffer)!); + return LaneDto.decode(readValue(buffer)!); case 190: + return StepInfoDto.decode(readValue(buffer)!); + case 191: return NavInfoDto.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); From eed98532b3b3f386633d01c5cab65fe74b1363cb Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Wed, 3 Dec 2025 10:24:01 +0200 Subject: [PATCH 16/19] test: do not try to enable location on iOS platform --- example/integration_test/t02_session_test.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/example/integration_test/t02_session_test.dart b/example/integration_test/t02_session_test.dart index b560233e..2560e3d2 100644 --- a/example/integration_test/t02_session_test.dart +++ b/example/integration_test/t02_session_test.dart @@ -28,8 +28,10 @@ void main() { patrol('Test terms and conditions (TOS) dialog acceptance', ( PatrolIntegrationTester $, ) async { - // Be sure location is enabled. - await $.native.enableLocation(); + if (!Platform.isIOS) { + // Be sure location is enabled. + await $.native.enableLocation(); + } // Grant the location permission. await checkLocationDialogAcceptance($); From b24bf74a56499272f42c6b512122a4a3fab2bab5 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Wed, 3 Dec 2025 10:25:07 +0200 Subject: [PATCH 17/19] chore: Updates minimum supported SDK version to Flutter 3.32/Dart 3.8. --- pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 402d60d9..a32b4d54 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,8 +19,8 @@ issue_tracker: https://github.com/googlemaps/flutter-navigation-sdk/issues version: 0.7.0 environment: - sdk: ^3.7.0 - flutter: ">=3.29.0" + sdk: ^3.8.0 + flutter: ">=3.32.0" dependencies: collection: ^1.17.2 From a37ecf98078edd6eff0b83d6e922f7c8f66bca76 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Wed, 3 Dec 2025 10:39:48 +0200 Subject: [PATCH 18/19] chore: format code --- .../navigation/GoogleMapsBaseMapView.kt | 3 +- lib/src/method_channel/auto_view_api.dart | 167 ++++++++------- lib/src/method_channel/convert/circle.dart | 9 +- .../method_channel/convert/destinations.dart | 7 +- .../method_channel/convert/navigation.dart | 39 ++-- lib/src/method_channel/convert/navinfo.dart | 23 +-- lib/src/method_channel/convert/polygon.dart | 14 +- lib/src/method_channel/convert/polyline.dart | 23 +-- lib/src/method_channel/image_api.dart | 4 +- lib/src/method_channel/map_view_api.dart | 191 +++++++++--------- lib/src/method_channel/messages.g.dart | 52 +++-- lib/src/method_channel/session_api.dart | 20 +- test/google_navigation_flutter_test.dart | 12 +- test/messages_test.g.dart | 77 +++---- test/navigation_types_test.dart | 49 +++-- 15 files changed, 337 insertions(+), 353 deletions(-) diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsBaseMapView.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsBaseMapView.kt index a7bf85ca..6038b3aa 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsBaseMapView.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsBaseMapView.kt @@ -543,8 +543,7 @@ abstract class GoogleMapsBaseMapView( // library for geolocation or implement separate method under // [GoogleMapsNavigationSessionManager] to fetch the location // using the [FusedLocationProviderApi]. - @Suppress("DEPRECATION") - val location = getMap().myLocation + @Suppress("DEPRECATION") val location = getMap().myLocation // Return null explicitly if location is not available to avoid NullPointerException // when the platform channel tries to serialize the Location object return if (location != null && location.provider != null) location else null diff --git a/lib/src/method_channel/auto_view_api.dart b/lib/src/method_channel/auto_view_api.dart index 750c31d8..713e299f 100644 --- a/lib/src/method_channel/auto_view_api.dart +++ b/lib/src/method_channel/auto_view_api.dart @@ -310,10 +310,9 @@ class AutoMapViewAPIImpl { duration, ) .then( - (bool success) => - onFinished != null && Platform.isAndroid - ? onFinished(success) - : null, + (bool success) => onFinished != null && Platform.isAndroid + ? onFinished(success) + : null, ), ); case CameraUpdateType.latLng: @@ -321,10 +320,9 @@ class AutoMapViewAPIImpl { _viewApi .animateCameraToLatLng(cameraUpdate.latLng!.toDto(), duration) .then( - (bool success) => - onFinished != null && Platform.isAndroid - ? onFinished(success) - : null, + (bool success) => onFinished != null && Platform.isAndroid + ? onFinished(success) + : null, ), ); case CameraUpdateType.latLngBounds: @@ -336,10 +334,9 @@ class AutoMapViewAPIImpl { duration, ) .then( - (bool success) => - onFinished != null && Platform.isAndroid - ? onFinished(success) - : null, + (bool success) => onFinished != null && Platform.isAndroid + ? onFinished(success) + : null, ), ); case CameraUpdateType.latLngZoom: @@ -351,10 +348,9 @@ class AutoMapViewAPIImpl { duration, ) .then( - (bool success) => - onFinished != null && Platform.isAndroid - ? onFinished(success) - : null, + (bool success) => onFinished != null && Platform.isAndroid + ? onFinished(success) + : null, ), ); case CameraUpdateType.scrollBy: @@ -366,10 +362,9 @@ class AutoMapViewAPIImpl { duration, ) .then( - (bool success) => - onFinished != null && Platform.isAndroid - ? onFinished(success) - : null, + (bool success) => onFinished != null && Platform.isAndroid + ? onFinished(success) + : null, ), ); case CameraUpdateType.zoomBy: @@ -382,10 +377,9 @@ class AutoMapViewAPIImpl { duration, ) .then( - (bool success) => - onFinished != null && Platform.isAndroid - ? onFinished(success) - : null, + (bool success) => onFinished != null && Platform.isAndroid + ? onFinished(success) + : null, ), ); case CameraUpdateType.zoomTo: @@ -393,10 +387,9 @@ class AutoMapViewAPIImpl { _viewApi .animateCameraToZoom(cameraUpdate.zoom!, duration) .then( - (bool success) => - onFinished != null && Platform.isAndroid - ? onFinished(success) - : null, + (bool success) => onFinished != null && Platform.isAndroid + ? onFinished(success) + : null, ), ); } @@ -494,17 +487,17 @@ class AutoMapViewAPIImpl { required List markerOptions, }) async { // Convert options to pigeon format - final List options = - markerOptions.map((MarkerOptions opt) => opt.toDto()).toList(); + final List options = markerOptions + .map((MarkerOptions opt) => opt.toDto()) + .toList(); // Create marker objects with new ID's - final List markersToAdd = - options - .map( - (MarkerOptionsDto options) => - MarkerDto(markerId: _createMarkerId(), options: options), - ) - .toList(); + final List markersToAdd = options + .map( + (MarkerOptionsDto options) => + MarkerDto(markerId: _createMarkerId(), options: options), + ) + .toList(); // Add markers to map final List markersAdded = await _viewApi.addMarkers( @@ -524,8 +517,9 @@ class AutoMapViewAPIImpl { /// Update markers on the map view. Future> updateMarkers({required List markers}) async { try { - final List markerDtos = - markers.map((Marker marker) => marker.toDto()).toList(); + final List markerDtos = markers + .map((Marker marker) => marker.toDto()) + .toList(); final List updatedMarkers = await _viewApi.updateMarkers( markerDtos, ); @@ -545,8 +539,9 @@ class AutoMapViewAPIImpl { /// Remove markers from map view. Future removeMarkers({required List markers}) async { try { - final List markerDtos = - markers.map((Marker marker) => marker.toDto()).toList(); + final List markerDtos = markers + .map((Marker marker) => marker.toDto()) + .toList(); return await _viewApi.removeMarkers(markerDtos); } on PlatformException catch (error) { if (error.code == 'markerNotFound') { @@ -582,17 +577,17 @@ class AutoMapViewAPIImpl { required List polygonOptions, }) async { // Convert options to pigeon format - final List options = - polygonOptions.map((PolygonOptions opt) => opt.toDto()).toList(); + final List options = polygonOptions + .map((PolygonOptions opt) => opt.toDto()) + .toList(); // Create polygon objects with new ID's - final List polygonsToAdd = - options - .map( - (PolygonOptionsDto options) => - PolygonDto(polygonId: _createPolygonId(), options: options), - ) - .toList(); + final List polygonsToAdd = options + .map( + (PolygonOptionsDto options) => + PolygonDto(polygonId: _createPolygonId(), options: options), + ) + .toList(); // Add polygons to map final List polygonsAdded = await _viewApi.addPolygons( @@ -614,8 +609,9 @@ class AutoMapViewAPIImpl { required List polygons, }) async { try { - final List navigationViewPolygons = - polygons.map((Polygon polygon) => polygon.toDto()).toList(); + final List navigationViewPolygons = polygons + .map((Polygon polygon) => polygon.toDto()) + .toList(); final List updatedPolygons = await _viewApi.updatePolygons( navigationViewPolygons, ); @@ -635,8 +631,9 @@ class AutoMapViewAPIImpl { /// Remove polygons from map view. Future removePolygons({required List polygons}) async { try { - final List navigationViewPolygons = - polygons.map((Polygon polygon) => polygon.toDto()).toList(); + final List navigationViewPolygons = polygons + .map((Polygon polygon) => polygon.toDto()) + .toList(); return await _viewApi.removePolygons(navigationViewPolygons); } on PlatformException catch (error) { if (error.code == 'polygonNotFound') { @@ -667,19 +664,17 @@ class AutoMapViewAPIImpl { required List polylineOptions, }) async { // Convert options to pigeon format - final List options = - polylineOptions.map((PolylineOptions opt) => opt.toDto()).toList(); + final List options = polylineOptions + .map((PolylineOptions opt) => opt.toDto()) + .toList(); // Create polyline objects with new ID's - final List polylinesToAdd = - options - .map( - (PolylineOptionsDto options) => PolylineDto( - polylineId: _createPolylineId(), - options: options, - ), - ) - .toList(); + final List polylinesToAdd = options + .map( + (PolylineOptionsDto options) => + PolylineDto(polylineId: _createPolylineId(), options: options), + ) + .toList(); // Add polylines to map final List polylinesAdded = await _viewApi.addPolylines( @@ -701,10 +696,9 @@ class AutoMapViewAPIImpl { required List polylines, }) async { try { - final List navigationViewPolylines = - polylines - .map((Polyline polyline) => polyline.toNavigationViewPolyline()) - .toList(); + final List navigationViewPolylines = polylines + .map((Polyline polyline) => polyline.toNavigationViewPolyline()) + .toList(); final List updatedPolylines = await _viewApi .updatePolylines(navigationViewPolylines); return updatedPolylines @@ -723,10 +717,9 @@ class AutoMapViewAPIImpl { /// Remove polylines from map view. Future removePolylines({required List polylines}) async { try { - final List navigationViewPolylines = - polylines - .map((Polyline polyline) => polyline.toNavigationViewPolyline()) - .toList(); + final List navigationViewPolylines = polylines + .map((Polyline polyline) => polyline.toNavigationViewPolyline()) + .toList(); return await _viewApi.removePolylines(navigationViewPolylines); } on PlatformException catch (error) { if (error.code == 'polylineNotFound') { @@ -757,17 +750,17 @@ class AutoMapViewAPIImpl { required List options, }) async { // Convert options to pigeon format - final List optionsDto = - options.map((CircleOptions opt) => opt.toDto()).toList(); + final List optionsDto = options + .map((CircleOptions opt) => opt.toDto()) + .toList(); // Create circle objects with new ID's - final List circlesToAdd = - optionsDto - .map( - (CircleOptionsDto options) => - CircleDto(circleId: _createCircleId(), options: options), - ) - .toList(); + final List circlesToAdd = optionsDto + .map( + (CircleOptionsDto options) => + CircleDto(circleId: _createCircleId(), options: options), + ) + .toList(); // Add circles to map final List circlesAdded = await _viewApi.addCircles( @@ -787,8 +780,9 @@ class AutoMapViewAPIImpl { /// Update circles on the map view. Future> updateCircles({required List circles}) async { try { - final List navigationViewCircles = - circles.map((Circle circle) => circle.toDto()).toList(); + final List navigationViewCircles = circles + .map((Circle circle) => circle.toDto()) + .toList(); final List updatedCircles = await _viewApi.updateCircles( navigationViewCircles, ); @@ -809,8 +803,9 @@ class AutoMapViewAPIImpl { /// Remove circles from map view. Future removeCircles({required List circles}) async { try { - final List navigationViewCircles = - circles.map((Circle circle) => circle.toDto()).toList(); + final List navigationViewCircles = circles + .map((Circle circle) => circle.toDto()) + .toList(); return await _viewApi.removeCircles(navigationViewCircles); } on PlatformException catch (error) { if (error.code == 'circleNotFound') { diff --git a/lib/src/method_channel/convert/circle.dart b/lib/src/method_channel/convert/circle.dart index f8998461..8d571522 100644 --- a/lib/src/method_channel/convert/circle.dart +++ b/lib/src/method_channel/convert/circle.dart @@ -47,11 +47,10 @@ extension ConvertCircleOptionsDto on CircleOptionsDto { fillColor: Color(fillColor), strokeColor: Color(strokeColor), strokeWidth: strokeWidth, - strokePattern: - strokePattern - .map((PatternItemDto? e) => e?.toPatternItem()) - .whereType() - .toList(), + strokePattern: strokePattern + .map((PatternItemDto? e) => e?.toPatternItem()) + .whereType() + .toList(), visible: visible, zIndex: zIndex, ); diff --git a/lib/src/method_channel/convert/destinations.dart b/lib/src/method_channel/convert/destinations.dart index 95e54a9a..68bdcb12 100644 --- a/lib/src/method_channel/convert/destinations.dart +++ b/lib/src/method_channel/convert/destinations.dart @@ -22,10 +22,9 @@ import 'navigation_waypoint.dart'; extension ConvertDestinations on Destinations { /// Converts [Destinations] to [DestinationsDto] DestinationsDto toDto() => DestinationsDto( - waypoints: - waypoints.map((NavigationWaypoint e) { - return e.toDto(); - }).toList(), + waypoints: waypoints.map((NavigationWaypoint e) { + return e.toDto(); + }).toList(), displayOptions: displayOptions.toDto(), routingOptions: routingOptions?.toDto(), routeTokenOptions: routeTokenOptions?.toDto(), diff --git a/lib/src/method_channel/convert/navigation.dart b/lib/src/method_channel/convert/navigation.dart index cb0881eb..0132a294 100644 --- a/lib/src/method_channel/convert/navigation.dart +++ b/lib/src/method_channel/convert/navigation.dart @@ -161,18 +161,17 @@ extension ConvertRouteSegmentTrafficDataDto on RouteSegmentTrafficDataDto { return RouteSegmentTrafficDataStatus.unavailable; } }(), - roadStretchRenderingDataList: - roadStretchRenderingDataList - .where( - (RouteSegmentTrafficDataRoadStretchRenderingDataDto? d) => - d != null, - ) - .cast() - .map( - (RouteSegmentTrafficDataRoadStretchRenderingDataDto d) => - d.toRouteSegmentTrafficDataRoadStretchRenderingData(), - ) - .toList(), + roadStretchRenderingDataList: roadStretchRenderingDataList + .where( + (RouteSegmentTrafficDataRoadStretchRenderingDataDto? d) => + d != null, + ) + .cast() + .map( + (RouteSegmentTrafficDataRoadStretchRenderingDataDto d) => + d.toRouteSegmentTrafficDataRoadStretchRenderingData(), + ) + .toList(), ); } @@ -186,15 +185,13 @@ extension ConvertRouteSegmentDto on RouteSegmentDto { longitude: destinationLatLng.longitude, ), destinationWaypoint: destinationWaypoint?.toNavigationWaypoint(), - latLngs: - latLngs - ?.where((LatLngDto? p) => p != null) - .cast() - .map( - (LatLngDto p) => - LatLng(latitude: p.latitude, longitude: p.longitude), - ) - .toList(), + latLngs: latLngs + ?.where((LatLngDto? p) => p != null) + .cast() + .map( + (LatLngDto p) => LatLng(latitude: p.latitude, longitude: p.longitude), + ) + .toList(), trafficData: trafficData?.toRouteSegmentTrafficData(), ); } diff --git a/lib/src/method_channel/convert/navinfo.dart b/lib/src/method_channel/convert/navinfo.dart index 70836ac9..a80c57b2 100644 --- a/lib/src/method_channel/convert/navinfo.dart +++ b/lib/src/method_channel/convert/navinfo.dart @@ -21,11 +21,10 @@ extension ConvertNavInfoDto on NavInfoDto { /// Converts [NavInfoDto] to [NavInfo] NavInfo toNavInfo() => NavInfo( currentStep: currentStep?.toStepInfo(), - remainingSteps: - remainingSteps - .whereType() - .map((StepInfoDto stepinfo) => stepinfo.toStepInfo()) - .toList(), + remainingSteps: remainingSteps + .whereType() + .map((StepInfoDto stepinfo) => stepinfo.toStepInfo()) + .toList(), routeChanged: routeChanged, distanceToCurrentStepMeters: distanceToCurrentStepMeters, distanceToNextDestinationMeters: distanceToNextDestinationMeters, @@ -237,14 +236,12 @@ extension ConvertManeuverDto on ManeuverDto { extension ConvertLaneDto on LaneDto { /// Converts [LaneDto] to [Lane] Lane toLane() => Lane( - laneDirections: - laneDirections - .whereType() - .map( - (LaneDirectionDto laneDirection) => - laneDirection.toLaneDirection(), - ) - .toList(), + laneDirections: laneDirections + .whereType() + .map( + (LaneDirectionDto laneDirection) => laneDirection.toLaneDirection(), + ) + .toList(), ); } diff --git a/lib/src/method_channel/convert/polygon.dart b/lib/src/method_channel/convert/polygon.dart index 56167cd7..70cf7734 100644 --- a/lib/src/method_channel/convert/polygon.dart +++ b/lib/src/method_channel/convert/polygon.dart @@ -34,14 +34,12 @@ extension ConvertPolygonOptions on PolygonOptions { PolygonOptionsDto toDto() { return PolygonOptionsDto( points: points.map((LatLng point) => point.toDto()).toList(), - holes: - holes - .map( - (List e) => PolygonHoleDto( - points: e.map((LatLng e) => e.toDto()).toList(), - ), - ) - .toList(), + holes: holes + .map( + (List e) => + PolygonHoleDto(points: e.map((LatLng e) => e.toDto()).toList()), + ) + .toList(), clickable: clickable, fillColor: colorToInt(fillColor)!, geodesic: geodesic, diff --git a/lib/src/method_channel/convert/polyline.dart b/lib/src/method_channel/convert/polyline.dart index 67f42da1..c69e6ca9 100644 --- a/lib/src/method_channel/convert/polyline.dart +++ b/lib/src/method_channel/convert/polyline.dart @@ -38,8 +38,9 @@ extension ConvertPolylineOptions on PolylineOptions { geodesic: geodesic, strokeColor: colorToInt(strokeColor), strokeJointType: strokeJointType?.toStrokeJointTypeDto(), - strokePattern: - strokePattern?.map((PatternItem pi) => pi.toDto()).toList(), + strokePattern: strokePattern + ?.map((PatternItem pi) => pi.toDto()) + .toList(), strokeWidth: strokeWidth, visible: visible, zIndex: zIndex, @@ -100,20 +101,18 @@ extension ConvertPolylineOptionsDto on PolylineOptionsDto { /// Convert [PolylineOptionsDto] to [PolylineOptions]. PolylineOptions toPolylineOptions() { return PolylineOptions( - points: - points - ?.map((LatLngDto? point) => point?.toLatLng()) - .whereType() - .toList(), + points: points + ?.map((LatLngDto? point) => point?.toLatLng()) + .whereType() + .toList(), clickable: clickable, geodesic: geodesic, strokeColor: strokeColor != null ? Color(strokeColor!) : null, strokeJointType: strokeJointType?.toStrokeJointType(), - strokePattern: - strokePattern - ?.map((PatternItemDto? pidto) => pidto?.toPatternItem()) - .whereType() - .toList(), + strokePattern: strokePattern + ?.map((PatternItemDto? pidto) => pidto?.toPatternItem()) + .whereType() + .toList(), strokeWidth: strokeWidth, visible: visible, zIndex: zIndex, diff --git a/lib/src/method_channel/image_api.dart b/lib/src/method_channel/image_api.dart index c79edc5d..af431045 100644 --- a/lib/src/method_channel/image_api.dart +++ b/lib/src/method_channel/image_api.dart @@ -64,8 +64,8 @@ class ImageRegistryAPIImpl { /// Get all registered bitmaps from image registry. Future> getRegisteredImages() async { - final List registeredImages = - await _imageApi.getRegisteredImages(); + final List registeredImages = await _imageApi + .getRegisteredImages(); return registeredImages .whereType() .map((ImageDescriptorDto e) => e.toImageDescriptor()) diff --git a/lib/src/method_channel/map_view_api.dart b/lib/src/method_channel/map_view_api.dart index 52bcd28e..34465fed 100644 --- a/lib/src/method_channel/map_view_api.dart +++ b/lib/src/method_channel/map_view_api.dart @@ -122,15 +122,14 @@ class MapViewAPIImpl { maxZoomPreference: mapOptions.maxZoomPreference, zoomControlsEnabled: mapOptions.zoomControlsEnabled, cameraTargetBounds: mapOptions.cameraTargetBounds?.toDto(), - padding: - mapOptions.padding != null - ? MapPaddingDto( - top: mapOptions.padding!.top.toInt(), - left: mapOptions.padding!.left.toInt(), - bottom: mapOptions.padding!.bottom.toInt(), - right: mapOptions.padding!.right.toInt(), - ) - : null, + padding: mapOptions.padding != null + ? MapPaddingDto( + top: mapOptions.padding!.top.toInt(), + left: mapOptions.padding!.left.toInt(), + bottom: mapOptions.padding!.bottom.toInt(), + right: mapOptions.padding!.right.toInt(), + ) + : null, mapId: mapOptions.mapId, ); @@ -155,10 +154,9 @@ class MapViewAPIImpl { // Build ViewCreationMessage return ViewCreationOptionsDto( - mapViewType: - mapViewType == MapViewType.navigation - ? MapViewTypeDto.navigation - : MapViewTypeDto.map, + mapViewType: mapViewType == MapViewType.navigation + ? MapViewTypeDto.navigation + : MapViewTypeDto.map, mapOptions: mapOptionsMessage, navigationViewOptions: navigationOptionsMessage, ); @@ -428,10 +426,9 @@ class MapViewAPIImpl { duration, ) .then( - (bool success) => - onFinished != null && Platform.isAndroid - ? onFinished(success) - : null, + (bool success) => onFinished != null && Platform.isAndroid + ? onFinished(success) + : null, ), ); case CameraUpdateType.latLng: @@ -443,10 +440,9 @@ class MapViewAPIImpl { duration, ) .then( - (bool success) => - onFinished != null && Platform.isAndroid - ? onFinished(success) - : null, + (bool success) => onFinished != null && Platform.isAndroid + ? onFinished(success) + : null, ), ); case CameraUpdateType.latLngBounds: @@ -459,10 +455,9 @@ class MapViewAPIImpl { duration, ) .then( - (bool success) => - onFinished != null && Platform.isAndroid - ? onFinished(success) - : null, + (bool success) => onFinished != null && Platform.isAndroid + ? onFinished(success) + : null, ), ); case CameraUpdateType.latLngZoom: @@ -475,10 +470,9 @@ class MapViewAPIImpl { duration, ) .then( - (bool success) => - onFinished != null && Platform.isAndroid - ? onFinished(success) - : null, + (bool success) => onFinished != null && Platform.isAndroid + ? onFinished(success) + : null, ), ); case CameraUpdateType.scrollBy: @@ -491,10 +485,9 @@ class MapViewAPIImpl { duration, ) .then( - (bool success) => - onFinished != null && Platform.isAndroid - ? onFinished(success) - : null, + (bool success) => onFinished != null && Platform.isAndroid + ? onFinished(success) + : null, ), ); case CameraUpdateType.zoomBy: @@ -508,10 +501,9 @@ class MapViewAPIImpl { duration, ) .then( - (bool success) => - onFinished != null && Platform.isAndroid - ? onFinished(success) - : null, + (bool success) => onFinished != null && Platform.isAndroid + ? onFinished(success) + : null, ), ); case CameraUpdateType.zoomTo: @@ -519,10 +511,9 @@ class MapViewAPIImpl { _viewApi .animateCameraToZoom(viewId, cameraUpdate.zoom!, duration) .then( - (bool success) => - onFinished != null && Platform.isAndroid - ? onFinished(success) - : null, + (bool success) => onFinished != null && Platform.isAndroid + ? onFinished(success) + : null, ), ); } @@ -815,17 +806,17 @@ class MapViewAPIImpl { required List markerOptions, }) async { // Convert options to pigeon format - final List options = - markerOptions.map((MarkerOptions opt) => opt.toDto()).toList(); + final List options = markerOptions + .map((MarkerOptions opt) => opt.toDto()) + .toList(); // Create marker objects with new ID's - final List markersToAdd = - options - .map( - (MarkerOptionsDto options) => - MarkerDto(markerId: _createMarkerId(), options: options), - ) - .toList(); + final List markersToAdd = options + .map( + (MarkerOptionsDto options) => + MarkerDto(markerId: _createMarkerId(), options: options), + ) + .toList(); // Add markers to map final List markersAdded = await _viewApi.addMarkers( @@ -849,8 +840,9 @@ class MapViewAPIImpl { required List markers, }) async { try { - final List markerDtos = - markers.map((Marker marker) => marker.toDto()).toList(); + final List markerDtos = markers + .map((Marker marker) => marker.toDto()) + .toList(); final List updatedMarkers = await _viewApi.updateMarkers( viewId, markerDtos, @@ -874,8 +866,9 @@ class MapViewAPIImpl { required List markers, }) async { try { - final List markerDtos = - markers.map((Marker marker) => marker.toDto()).toList(); + final List markerDtos = markers + .map((Marker marker) => marker.toDto()) + .toList(); return await _viewApi.removeMarkers(viewId, markerDtos); } on PlatformException catch (error) { if (error.code == 'markerNotFound') { @@ -912,17 +905,17 @@ class MapViewAPIImpl { required List polygonOptions, }) async { // Convert options to pigeon format - final List options = - polygonOptions.map((PolygonOptions opt) => opt.toDto()).toList(); + final List options = polygonOptions + .map((PolygonOptions opt) => opt.toDto()) + .toList(); // Create polygon objects with new ID's - final List polygonsToAdd = - options - .map( - (PolygonOptionsDto options) => - PolygonDto(polygonId: _createPolygonId(), options: options), - ) - .toList(); + final List polygonsToAdd = options + .map( + (PolygonOptionsDto options) => + PolygonDto(polygonId: _createPolygonId(), options: options), + ) + .toList(); // Add polygons to map final List polygonsAdded = await _viewApi.addPolygons( @@ -946,8 +939,9 @@ class MapViewAPIImpl { required List polygons, }) async { try { - final List navigationViewPolygons = - polygons.map((Polygon polygon) => polygon.toDto()).toList(); + final List navigationViewPolygons = polygons + .map((Polygon polygon) => polygon.toDto()) + .toList(); final List updatedPolygons = await _viewApi.updatePolygons( viewId, navigationViewPolygons, @@ -971,8 +965,9 @@ class MapViewAPIImpl { required List polygons, }) async { try { - final List navigationViewPolygons = - polygons.map((Polygon polygon) => polygon.toDto()).toList(); + final List navigationViewPolygons = polygons + .map((Polygon polygon) => polygon.toDto()) + .toList(); return await _viewApi.removePolygons(viewId, navigationViewPolygons); } on PlatformException catch (error) { if (error.code == 'polygonNotFound') { @@ -1004,19 +999,17 @@ class MapViewAPIImpl { required List polylineOptions, }) async { // Convert options to pigeon format - final List options = - polylineOptions.map((PolylineOptions opt) => opt.toDto()).toList(); + final List options = polylineOptions + .map((PolylineOptions opt) => opt.toDto()) + .toList(); // Create polyline objects with new ID's - final List polylinesToAdd = - options - .map( - (PolylineOptionsDto options) => PolylineDto( - polylineId: _createPolylineId(), - options: options, - ), - ) - .toList(); + final List polylinesToAdd = options + .map( + (PolylineOptionsDto options) => + PolylineDto(polylineId: _createPolylineId(), options: options), + ) + .toList(); // Add polylines to map final List polylinesAdded = await _viewApi.addPolylines( @@ -1040,10 +1033,9 @@ class MapViewAPIImpl { required List polylines, }) async { try { - final List navigationViewPolylines = - polylines - .map((Polyline polyline) => polyline.toNavigationViewPolyline()) - .toList(); + final List navigationViewPolylines = polylines + .map((Polyline polyline) => polyline.toNavigationViewPolyline()) + .toList(); final List updatedPolylines = await _viewApi .updatePolylines(viewId, navigationViewPolylines); return updatedPolylines @@ -1065,10 +1057,9 @@ class MapViewAPIImpl { required List polylines, }) async { try { - final List navigationViewPolylines = - polylines - .map((Polyline polyline) => polyline.toNavigationViewPolyline()) - .toList(); + final List navigationViewPolylines = polylines + .map((Polyline polyline) => polyline.toNavigationViewPolyline()) + .toList(); return await _viewApi.removePolylines(viewId, navigationViewPolylines); } on PlatformException catch (error) { if (error.code == 'polylineNotFound') { @@ -1100,17 +1091,17 @@ class MapViewAPIImpl { required List options, }) async { // Convert options to pigeon format - final List optionsDto = - options.map((CircleOptions opt) => opt.toDto()).toList(); + final List optionsDto = options + .map((CircleOptions opt) => opt.toDto()) + .toList(); // Create circle objects with new ID's - final List circlesToAdd = - optionsDto - .map( - (CircleOptionsDto options) => - CircleDto(circleId: _createCircleId(), options: options), - ) - .toList(); + final List circlesToAdd = optionsDto + .map( + (CircleOptionsDto options) => + CircleDto(circleId: _createCircleId(), options: options), + ) + .toList(); // Add circles to map final List circlesAdded = await _viewApi.addCircles( @@ -1134,8 +1125,9 @@ class MapViewAPIImpl { required List circles, }) async { try { - final List navigationViewCircles = - circles.map((Circle circle) => circle.toDto()).toList(); + final List navigationViewCircles = circles + .map((Circle circle) => circle.toDto()) + .toList(); final List updatedCircles = await _viewApi.updateCircles( viewId, navigationViewCircles, @@ -1160,8 +1152,9 @@ class MapViewAPIImpl { required List circles, }) async { try { - final List navigationViewCircles = - circles.map((Circle circle) => circle.toDto()).toList(); + final List navigationViewCircles = circles + .map((Circle circle) => circle.toDto()) + .toList(); return await _viewApi.removeCircles(viewId, navigationViewCircles); } on PlatformException catch (error) { if (error.code == 'circleNotFound') { diff --git a/lib/src/method_channel/messages.g.dart b/lib/src/method_channel/messages.g.dart index 1f215bb3..0d93812a 100644 --- a/lib/src/method_channel/messages.g.dart +++ b/lib/src/method_channel/messages.g.dart @@ -2273,9 +2273,8 @@ class RouteSegmentTrafficDataDto { result as List; return RouteSegmentTrafficDataDto( status: result[0]! as RouteSegmentTrafficDataStatusDto, - roadStretchRenderingDataList: - (result[1] as List?)! - .cast(), + roadStretchRenderingDataList: (result[1] as List?)! + .cast(), ); } @@ -2907,7 +2906,7 @@ class _PigeonCodec extends StandardMessageCodec { return value == null ? null : RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto - .values[value]; + .values[value]; case 146: final int? value = readValue(buffer) as int?; return value == null ? null : ManeuverDto.values[value]; @@ -3029,8 +3028,9 @@ class ViewCreationApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -3073,8 +3073,9 @@ class MapViewApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -6131,8 +6132,9 @@ class ImageRegistryApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -6304,8 +6306,9 @@ abstract class ViewEventApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; { final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( @@ -6841,8 +6844,9 @@ class NavigationSessionApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -7861,8 +7865,9 @@ abstract class NavigationSessionEventApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; { final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( @@ -8253,8 +8258,9 @@ class AutoMapViewApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -10461,8 +10467,9 @@ abstract class AutoViewEventApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; { final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( @@ -10547,8 +10554,9 @@ class NavigationInspector { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); diff --git a/lib/src/method_channel/session_api.dart b/lib/src/method_channel/session_api.dart index e791f5dc..7c60fa5b 100644 --- a/lib/src/method_channel/session_api.dart +++ b/lib/src/method_channel/session_api.dart @@ -248,8 +248,8 @@ class NavigationSessionAPIImpl { @Deprecated('Use setDestinations with an updated list of waypoints instead') Future continueToNextDestination() async { try { - final NavigationWaypointDto? waypointDto = - await _sessionApi.continueToNextDestination(); + final NavigationWaypointDto? waypointDto = await _sessionApi + .continueToNextDestination(); if (waypointDto == null) { return null; } @@ -267,8 +267,8 @@ class NavigationSessionAPIImpl { /// Gets current time and distance left. Future getCurrentTimeAndDistance() async { try { - final NavigationTimeAndDistanceDto timeAndDistance = - await _sessionApi.getCurrentTimeAndDistance(); + final NavigationTimeAndDistanceDto timeAndDistance = await _sessionApi + .getCurrentTimeAndDistance(); return timeAndDistance.toNavigationTimeAndDistance(); } on PlatformException catch (e) { switch (e.code) { @@ -535,8 +535,8 @@ class NavigationSessionAPIImpl { /// Get route segments. Future> getRouteSegments() async { try { - final List routeSegments = - await _sessionApi.getRouteSegments(); + final List routeSegments = await _sessionApi + .getRouteSegments(); return routeSegments .where((RouteSegmentDto? p) => p != null) .cast() @@ -555,8 +555,8 @@ class NavigationSessionAPIImpl { /// Get traveled route. Future> getTraveledRoute() async { try { - final List traveledRoute = - await _sessionApi.getTraveledRoute(); + final List traveledRoute = await _sessionApi + .getTraveledRoute(); return traveledRoute .where((LatLngDto? p) => p != null) .cast() @@ -578,8 +578,8 @@ class NavigationSessionAPIImpl { /// Get current route segment. Future getCurrentRouteSegment() async { try { - final RouteSegmentDto? currentRouteSegment = - await _sessionApi.getCurrentRouteSegment(); + final RouteSegmentDto? currentRouteSegment = await _sessionApi + .getCurrentRouteSegment(); return currentRouteSegment?.toRouteSegment(); } on PlatformException catch (e) { switch (e.code) { diff --git a/test/google_navigation_flutter_test.dart b/test/google_navigation_flutter_test.dart index 871707dc..ff19d434 100644 --- a/test/google_navigation_flutter_test.dart +++ b/test/google_navigation_flutter_test.dart @@ -132,8 +132,8 @@ void main() { // Get camera position - final CameraPosition positionOut = - await controller.getCameraPosition(); + final CameraPosition positionOut = await controller + .getCameraPosition(); final VerificationResult result = verify( viewMockApi.getCameraPosition(captureAny), ); @@ -876,8 +876,8 @@ void main() { when(viewMockApi.isBuildingsEnabled(any)).thenReturn(false); // Test isIncidentReportingAvailable - final bool isAvailable = - await controller.isIncidentReportingAvailable(); + final bool isAvailable = await controller + .isIncidentReportingAvailable(); expect(isAvailable, true); // Verify the API was called @@ -887,8 +887,8 @@ void main() { expect(availabilityResult.captured[0] as int, viewId); // Test isReportIncidentButtonEnabled - final bool isButtonEnabled = - await controller.isReportIncidentButtonEnabled(); + final bool isButtonEnabled = await controller + .isReportIncidentButtonEnabled(); expect(isButtonEnabled, false); // Verify the API was called diff --git a/test/messages_test.g.dart b/test/messages_test.g.dart index 27e6d24d..f0f1f17f 100644 --- a/test/messages_test.g.dart +++ b/test/messages_test.g.dart @@ -286,7 +286,7 @@ class _PigeonCodec extends StandardMessageCodec { return value == null ? null : RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto - .values[value]; + .values[value]; case 146: final int? value = readValue(buffer) as int?; return value == null ? null : ManeuverDto.values[value]; @@ -646,8 +646,9 @@ abstract class TestMapViewApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; { final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( @@ -3982,8 +3983,8 @@ abstract class TestMapViewApi { arg_viewId != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.addMarkers was null, expected non-null int.', ); - final List? arg_markers = - (args[1] as List?)?.cast(); + final List? arg_markers = (args[1] as List?) + ?.cast(); assert( arg_markers != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.addMarkers was null, expected non-null List.', @@ -4028,8 +4029,8 @@ abstract class TestMapViewApi { arg_viewId != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.updateMarkers was null, expected non-null int.', ); - final List? arg_markers = - (args[1] as List?)?.cast(); + final List? arg_markers = (args[1] as List?) + ?.cast(); assert( arg_markers != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.updateMarkers was null, expected non-null List.', @@ -4074,8 +4075,8 @@ abstract class TestMapViewApi { arg_viewId != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.removeMarkers was null, expected non-null int.', ); - final List? arg_markers = - (args[1] as List?)?.cast(); + final List? arg_markers = (args[1] as List?) + ?.cast(); assert( arg_markers != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.removeMarkers was null, expected non-null List.', @@ -4240,8 +4241,8 @@ abstract class TestMapViewApi { arg_viewId != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.addPolygons was null, expected non-null int.', ); - final List? arg_polygons = - (args[1] as List?)?.cast(); + final List? arg_polygons = (args[1] as List?) + ?.cast(); assert( arg_polygons != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.addPolygons was null, expected non-null List.', @@ -4286,8 +4287,8 @@ abstract class TestMapViewApi { arg_viewId != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.updatePolygons was null, expected non-null int.', ); - final List? arg_polygons = - (args[1] as List?)?.cast(); + final List? arg_polygons = (args[1] as List?) + ?.cast(); assert( arg_polygons != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.updatePolygons was null, expected non-null List.', @@ -4332,8 +4333,8 @@ abstract class TestMapViewApi { arg_viewId != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.removePolygons was null, expected non-null int.', ); - final List? arg_polygons = - (args[1] as List?)?.cast(); + final List? arg_polygons = (args[1] as List?) + ?.cast(); assert( arg_polygons != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.removePolygons was null, expected non-null List.', @@ -4457,8 +4458,8 @@ abstract class TestMapViewApi { arg_viewId != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.addPolylines was null, expected non-null int.', ); - final List? arg_polylines = - (args[1] as List?)?.cast(); + final List? arg_polylines = (args[1] as List?) + ?.cast(); assert( arg_polylines != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.addPolylines was null, expected non-null List.', @@ -4503,8 +4504,8 @@ abstract class TestMapViewApi { arg_viewId != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.updatePolylines was null, expected non-null int.', ); - final List? arg_polylines = - (args[1] as List?)?.cast(); + final List? arg_polylines = (args[1] as List?) + ?.cast(); assert( arg_polylines != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.updatePolylines was null, expected non-null List.', @@ -4549,8 +4550,8 @@ abstract class TestMapViewApi { arg_viewId != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.removePolylines was null, expected non-null int.', ); - final List? arg_polylines = - (args[1] as List?)?.cast(); + final List? arg_polylines = (args[1] as List?) + ?.cast(); assert( arg_polylines != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.removePolylines was null, expected non-null List.', @@ -4674,8 +4675,8 @@ abstract class TestMapViewApi { arg_viewId != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.addCircles was null, expected non-null int.', ); - final List? arg_circles = - (args[1] as List?)?.cast(); + final List? arg_circles = (args[1] as List?) + ?.cast(); assert( arg_circles != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.addCircles was null, expected non-null List.', @@ -4720,8 +4721,8 @@ abstract class TestMapViewApi { arg_viewId != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.updateCircles was null, expected non-null int.', ); - final List? arg_circles = - (args[1] as List?)?.cast(); + final List? arg_circles = (args[1] as List?) + ?.cast(); assert( arg_circles != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.updateCircles was null, expected non-null List.', @@ -4766,8 +4767,8 @@ abstract class TestMapViewApi { arg_viewId != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.removeCircles was null, expected non-null int.', ); - final List? arg_circles = - (args[1] as List?)?.cast(); + final List? arg_circles = (args[1] as List?) + ?.cast(); assert( arg_circles != null, 'Argument for dev.flutter.pigeon.google_navigation_flutter.MapViewApi.removeCircles was null, expected non-null List.', @@ -4977,8 +4978,9 @@ abstract class TestImageRegistryApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; { final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( @@ -5092,8 +5094,8 @@ abstract class TestImageRegistryApi { Object? message, ) async { try { - final List output = - api.getRegisteredImages(); + final List output = api + .getRegisteredImages(); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -5243,8 +5245,9 @@ abstract class TestNavigationSessionApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; { final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( @@ -5693,8 +5696,8 @@ abstract class TestNavigationSessionApi { Object? message, ) async { try { - final NavigationWaypointDto? output = - api.continueToNextDestination(); + final NavigationWaypointDto? output = api + .continueToNextDestination(); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -5725,8 +5728,8 @@ abstract class TestNavigationSessionApi { Object? message, ) async { try { - final NavigationTimeAndDistanceDto output = - api.getCurrentTimeAndDistance(); + final NavigationTimeAndDistanceDto output = api + .getCurrentTimeAndDistance(); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/test/navigation_types_test.dart b/test/navigation_types_test.dart index e855f9bd..22c9f13c 100644 --- a/test/navigation_types_test.dart +++ b/test/navigation_types_test.dart @@ -64,8 +64,8 @@ void main() { group('NavigationWaypoint tests', () { test('tests Navigation Waypoint conversion from DTO', () { - final NavigationWaypoint targetGmsWaypoint = - targetWaypointDto.toNavigationWaypoint(); + final NavigationWaypoint targetGmsWaypoint = targetWaypointDto + .toNavigationWaypoint(); expect(targetGmsWaypoint.title, targetWaypointDto.title); expect( targetGmsWaypoint.target?.latitude, @@ -85,8 +85,8 @@ void main() { targetWaypointDto.preferredSegmentHeading, ); - final NavigationWaypoint placeIDGmsWaypoint = - placeIDWaypointDto.toNavigationWaypoint(); + final NavigationWaypoint placeIDGmsWaypoint = placeIDWaypointDto + .toNavigationWaypoint(); expect(placeIDGmsWaypoint.title, placeIDWaypointDto.title); expect( placeIDGmsWaypoint.target?.latitude, @@ -180,8 +180,8 @@ void main() { group('Navigation Options tests', () { test('tests Navigation Display options conversion to Pigeon DTO', () { - final NavigationDisplayOptionsDto pigeonDtoDisplayOptions = - displayOptions.toDto(); + final NavigationDisplayOptionsDto pigeonDtoDisplayOptions = displayOptions + .toDto(); expect( pigeonDtoDisplayOptions.showDestinationMarkers, @@ -232,8 +232,9 @@ void main() { }); test('tests Navigation Routing strategy conversion to Pigeon DTO', () { - final RoutingStrategyDto pigeonDtoStrategy = - NavigationRoutingStrategy.defaultBest.toDto(); + final RoutingStrategyDto pigeonDtoStrategy = NavigationRoutingStrategy + .defaultBest + .toDto(); expect( pigeonDtoStrategy.toString().split('.').last, @@ -257,8 +258,8 @@ void main() { group('Navigation tests', () { test('Navigation RouteStatus conversion from Pigeon DTO', () { - final NavigationRouteStatus status = - RouteStatusDto.apiKeyNotAuthorized.toNavigationRouteStatus(); + final NavigationRouteStatus status = RouteStatusDto.apiKeyNotAuthorized + .toNavigationRouteStatus(); expect( status.toString().split('.').last, @@ -267,11 +268,10 @@ void main() { }); test('Navigation time and distance conversion from Pigeon DTO', () { - final NavigationTimeAndDistance td = - NavigationTimeAndDistanceDto( - time: 5.0, - distance: 6.0, - ).toNavigationTimeAndDistance(); + final NavigationTimeAndDistance td = NavigationTimeAndDistanceDto( + time: 5.0, + distance: 6.0, + ).toNavigationTimeAndDistance(); expect(td.time, 5.0); expect(td.distance, 6.0); @@ -361,15 +361,14 @@ void main() { test('Road stretch rendering data from Pigeon DTO', () { final RouteSegmentTrafficDataRoadStretchRenderingDataDto data = RouteSegmentTrafficDataRoadStretchRenderingDataDto( - style: - RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto - .slowerTraffic, + style: RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto + .slowerTraffic, lengthMeters: 500, offsetMeters: 600, ); - final RouteSegmentTrafficDataRoadStretchRenderingData gmsData = - data.toRouteSegmentTrafficDataRoadStretchRenderingData(); + final RouteSegmentTrafficDataRoadStretchRenderingData gmsData = data + .toRouteSegmentTrafficDataRoadStretchRenderingData(); expect(data.lengthMeters, gmsData.lengthMeters); expect(data.offsetMeters, gmsData.offsetMeters); @@ -382,9 +381,8 @@ void main() { test('Road segment traffic data from Pigeon DTO', () { final RouteSegmentTrafficDataRoadStretchRenderingDataDto renderingData = RouteSegmentTrafficDataRoadStretchRenderingDataDto( - style: - RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto - .slowerTraffic, + style: RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto + .slowerTraffic, lengthMeters: 500, offsetMeters: 600, ); @@ -422,9 +420,8 @@ void main() { test('Navigation route segment from Pigeon DTO', () { final RouteSegmentTrafficDataRoadStretchRenderingDataDto renderingData = RouteSegmentTrafficDataRoadStretchRenderingDataDto( - style: - RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto - .slowerTraffic, + style: RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto + .slowerTraffic, lengthMeters: 500, offsetMeters: 600, ); From 05cc7d8ba7fc4441862f95564affda4bcc8f4fc3 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Wed, 3 Dec 2025 12:48:53 +0200 Subject: [PATCH 19/19] chore: re-organize types --- lib/src/types/navigation.dart | 163 ++++++++++++++++++++++++++++++++++ lib/src/types/simulation.dart | 149 ------------------------------- lib/src/types/types.dart | 1 + 3 files changed, 164 insertions(+), 149 deletions(-) create mode 100644 lib/src/types/navigation.dart diff --git a/lib/src/types/navigation.dart b/lib/src/types/navigation.dart new file mode 100644 index 00000000..b129b4cc --- /dev/null +++ b/lib/src/types/navigation.dart @@ -0,0 +1,163 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'lat_lng.dart'; +import 'navigation_destinations.dart'; + +/// Type for speed alert severity. +/// {@category Navigation} +enum SpeedAlertSeverity { + /// Unknown severity. + unknown, + + /// Not speeding severity. + notSpeeding, + + /// Minor speeding severity. + minor, + + /// Major speeding severity. + major, +} + +/// SpeedingUpdated event message. +/// {@category Navigation} +class SpeedingUpdatedEvent { + /// Initialize speeding updated event message. + SpeedingUpdatedEvent({ + required this.percentageAboveLimit, + required this.severity, + }); + + /// Percentage above speed limit. + final double percentageAboveLimit; + + /// Severity of the speeding. + final SpeedAlertSeverity severity; + + @override + String toString() => + 'SpeedingUpdatedEvent(' + 'percentageAboveLimit: $percentageAboveLimit, ' + 'severity: $severity' + ')'; +} + +/// RoadSnappedLocationUpdated event message. +/// {@category Navigation} +class RoadSnappedLocationUpdatedEvent { + /// Initialize road snapped location updated event message. + RoadSnappedLocationUpdatedEvent({required this.location}); + + /// Coordinate of the updated location. + final LatLng location; + + @override + String toString() => 'RoadSnappedLocationUpdatedEvent(location: $location)'; +} + +/// RoadSnappedRawLocationUpdated event message (Android only). +/// {@category Navigation} +class RoadSnappedRawLocationUpdatedEvent { + /// Initialize road snapped raw location updated event message. + RoadSnappedRawLocationUpdatedEvent({required this.location}); + + /// Coordinate of the updated location. + final LatLng location; + + @override + String toString() => + 'RoadSnappedRawLocationUpdatedEvent(location: $location)'; +} + +/// GpsAvailabilityUpdated event message (Android only). +/// {@category Navigation} +@Deprecated( + 'Use getNavigationOnGpsAvailabilityChangeEventStream and GpsAvailabilityChangeEvent instead', +) +class GpsAvailabilityUpdatedEvent { + /// Initialize GPS availability updated event message. + GpsAvailabilityUpdatedEvent({required this.available}); + + /// GPS availability. + final bool available; + + @override + String toString() => 'GpsAvailabilityUpdatedEvent(available: $available)'; +} + +/// GpsAvailabilityChange event message (Android only). +/// {@category Navigation} +class GpsAvailabilityChangeEvent { + /// Initialize GPS availability change event message. + GpsAvailabilityChangeEvent({ + required this.isGpsLost, + required this.isGpsValidForNavigation, + }); + + /// Indicates a GPS signal or other sensors good enough for a reasonably certain location have been lost. + /// + /// This state is triggered after a short timeout (10 seconds) and serves as an early warning of potential signal issues. + /// For example, the "Searching for GPS" UI message may be shown when this value is true. + final bool isGpsLost; + + /// Indicates a GPS signal or other sensors are in general good enough for use in navigation. + /// + /// Note that this value takes into account the frequent failure of GPS at the start of nav, + /// and doesn't become true until some time later. + final bool isGpsValidForNavigation; + + @override + String toString() => + 'GpsAvailabilityChangeEvent(' + 'isGpsLost: $isGpsLost, ' + 'isGpsValidForNavigation: $isGpsValidForNavigation' + ')'; +} + +/// Remaining time or distance change event message. +/// {@category Navigation} +class RemainingTimeOrDistanceChangedEvent { + /// Initialize with remaining distance in meters and remaining time in seconds. + RemainingTimeOrDistanceChangedEvent({ + required this.remainingDistance, + required this.remainingTime, + }); + + /// Remaining distance in meters. + final double remainingDistance; + + /// Remaining time in seconds. + final double remainingTime; + + @override + String toString() => + 'RemainingTimeOrDistanceChangedEvent(' + 'remainingDistance: $remainingDistance, ' + 'remainingTime: $remainingTime' + ')'; +} + +/// On arrival event message +/// {@category Navigation} +class OnArrivalEvent { + /// Initialize with arrival waypoint. + OnArrivalEvent({required this.waypoint}); + + /// Arrival waypoint. + final NavigationWaypoint waypoint; + + @override + String toString() => 'OnArrivalEvent(waypoint: $waypoint)'; +} diff --git a/lib/src/types/simulation.dart b/lib/src/types/simulation.dart index 05a373c6..6b35f79d 100644 --- a/lib/src/types/simulation.dart +++ b/lib/src/types/simulation.dart @@ -12,119 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -import '../../google_navigation_flutter.dart'; - -/// Type for speed alert severity. -/// {@category Navigation} -enum SpeedAlertSeverity { - /// Unknown severity. - unknown, - - /// Not speeding severity. - notSpeeding, - - /// Minor speeding severity. - minor, - - /// Major speeding severity. - major, -} - -/// SpeedingUpdated event message. -/// {@category Navigation} -class SpeedingUpdatedEvent { - /// Initialize speeding updated event message. - SpeedingUpdatedEvent({ - required this.percentageAboveLimit, - required this.severity, - }); - - /// Percentage above speed limit. - final double percentageAboveLimit; - - /// Severity of the speeding. - final SpeedAlertSeverity severity; - - @override - String toString() => - 'SpeedingUpdatedEvent(' - 'percentageAboveLimit: $percentageAboveLimit, ' - 'severity: $severity' - ')'; -} - -/// RoadSnappedLocationUpdated event message. -/// {@category Navigation} -class RoadSnappedLocationUpdatedEvent { - /// Initialize road snapped location updated event message. - RoadSnappedLocationUpdatedEvent({required this.location}); - - /// Coordinate of the updated location. - final LatLng location; - - @override - String toString() => 'RoadSnappedLocationUpdatedEvent(location: $location)'; -} - -/// RoadSnappedRawLocationUpdated event message (Android only). -/// {@category Navigation} -class RoadSnappedRawLocationUpdatedEvent { - /// Initialize road snapped raw location updated event message. - RoadSnappedRawLocationUpdatedEvent({required this.location}); - - /// Coordinate of the updated location. - final LatLng location; - - @override - String toString() => - 'RoadSnappedRawLocationUpdatedEvent(location: $location)'; -} - -/// GpsAvailabilityUpdated event message (Android only). -/// {@category Navigation} -@Deprecated( - 'Use getNavigationOnGpsAvailabilityChangeEventStream and GpsAvailabilityChangeEvent instead', -) -class GpsAvailabilityUpdatedEvent { - /// Initialize GPS availability updated event message. - GpsAvailabilityUpdatedEvent({required this.available}); - - /// GPS availability. - final bool available; - - @override - String toString() => 'GpsAvailabilityUpdatedEvent(available: $available)'; -} - -/// GpsAvailabilityChange event message (Android only). -/// {@category Navigation} -class GpsAvailabilityChangeEvent { - /// Initialize GPS availability change event message. - GpsAvailabilityChangeEvent({ - required this.isGpsLost, - required this.isGpsValidForNavigation, - }); - - /// Indicates a GPS signal or other sensors good enough for a reasonably certain location have been lost. - /// - /// This state is triggered after a short timeout (10 seconds) and serves as an early warning of potential signal issues. - /// For example, the "Searching for GPS" UI message may be shown when this value is true. - final bool isGpsLost; - - /// Indicates a GPS signal or other sensors are in general good enough for use in navigation. - /// - /// Note that this value takes into account the frequent failure of GPS at the start of nav, - /// and doesn't become true until some time later. - final bool isGpsValidForNavigation; - - @override - String toString() => - 'GpsAvailabilityChangeEvent(' - 'isGpsLost: $isGpsLost, ' - 'isGpsValidForNavigation: $isGpsValidForNavigation' - ')'; -} - /// Navigation simulation options. /// {@category Navigation} class SimulationOptions { @@ -137,39 +24,3 @@ class SimulationOptions { @override String toString() => 'SimulationOptions(speedMultiplier: $speedMultiplier)'; } - -/// Remaining time or distance change event message. -/// {@category Navigation} -class RemainingTimeOrDistanceChangedEvent { - /// Initialize with remaining distance in meters and remaining time in seconds. - RemainingTimeOrDistanceChangedEvent({ - required this.remainingDistance, - required this.remainingTime, - }); - - /// Remaining distance in meters. - final double remainingDistance; - - /// Remaining time in seconds. - final double remainingTime; - - @override - String toString() => - 'RemainingTimeOrDistanceChangedEvent(' - 'remainingDistance: $remainingDistance, ' - 'remainingTime: $remainingTime' - ')'; -} - -/// On arrival event message -/// {@category Navigation} -class OnArrivalEvent { - /// Initialize with arrival waypoint. - OnArrivalEvent({required this.waypoint}); - - /// Arrival waypoint. - final NavigationWaypoint waypoint; - - @override - String toString() => 'OnArrivalEvent(waypoint: $waypoint)'; -} diff --git a/lib/src/types/types.dart b/lib/src/types/types.dart index 8a3fd312..49aa7d77 100644 --- a/lib/src/types/types.dart +++ b/lib/src/types/types.dart @@ -17,6 +17,7 @@ export 'images.dart'; export 'lat_lng.dart'; export 'lat_lng_bounds.dart'; export 'markers.dart'; +export 'navigation.dart'; export 'navigation_destinations.dart'; export 'navigation_initialization_params.dart'; export 'navigation_view_types.dart';