From 67274bd73d48e6ac4ab48bccc4301c7f96ef2757 Mon Sep 17 00:00:00 2001 From: Zubii Date: Sun, 12 Jul 2026 15:51:28 +0300 Subject: [PATCH 1/5] feat(google_maps_flutter_web): add support for my location --- .../assets/blue-dot.png | Bin 0 -> 1754 bytes .../assets/my_location-sprite-2x.png | Bin 0 -> 1582 bytes .../lib/google_maps_flutter_web.dart | 2 + .../lib/src/geolocation_api.dart | 71 +++++ .../lib/src/google_maps_controller.dart | 27 ++ .../lib/src/google_maps_inspector_web.dart | 2 +- .../lib/src/my_location.dart | 260 ++++++++++++++++++ .../google_maps_flutter_web/pubspec.yaml | 2 + 8 files changed, 363 insertions(+), 1 deletion(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/assets/blue-dot.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/assets/my_location-sprite-2x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/lib/src/geolocation_api.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/lib/src/my_location.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_web/assets/blue-dot.png b/packages/google_maps_flutter/google_maps_flutter_web/assets/blue-dot.png new file mode 100644 index 0000000000000000000000000000000000000000..77780f8ab4b78bdfec273c504a5104fbf134b054 GIT binary patch literal 1754 zcmZ`&2{_bg82=oR5nXJQm2#HK7$oNyGn1KQ9_Mxl12BHWEsU5N%Jf zqH>gOA*oH$wM!`p!}!nf|Lr!3f*GYq*i&YY=D$1qZg zfRceZrc7;OV?>U?ry3(>P7}F8y9}XYDgxhsD`ADg6U|6=!;Dw%^uKP~pNl_?qfpP~ zZ}&^FuMQ07uBvW}gd%Kc3>}$3#ecnm8QE?9Bk9Kp^wbT6Svc*?5s*0o*rUE@wzlSu zNFv2b+YoxD0QxX9DMVQn!U-WNtA!)P670a4>A6|Otw>4h-(2WC!L=u|@L&h1LyM-N zKnIe7VBnx>1Pv(_<~JjMe3rt|P(%O6Uy+eC8rBy+-0?M-Jm0D4jm|S04Ao< zsB)n!2{FrH!?2-C$}E_M8v2m?9DP_gR|}W<^*$lCZjc)W5QFyyU28&t(p@Ln{apeYLFImfA z51r3jy>G{{B_pF?YgLJDxG1~ua#)u+pU=;euSDa((9?%v%{zp*lgpkwsV0`cF-W-! zbhbC{IjloYi_uky?93~F0FJ-!qr|e%2~CxUs*CD&X$Z!@C5$gK1|OJ}I7T3=$+$UHLf#p z(;e>zVwcj7%nvwYu$LV5y3Af+IC<90i@bloT9T>$e1EHek5>C?Mcn4PgvRYgL?d?N z3O51Qv^QPJy7NNM2H$r-wJmof+!}3>x3qXw+^jUYI9}Z5@ORu zHcmcgFj5}3M_mezP*#;D%w$+btjIA@R?^L(QnVHrAKBuA_L?E@#)yIz|x|DHH# zo9lheF_F*Dk{u;kb-2qu-h?&rgnZ2ts15z*{r=Gl$g5Jeh>s?me7PU~FMuJ7x}6mg z$O^R$jtqqc5by+x^>}kA-sX5)i%qt8D^on)7LQjsKnVCpLHMDNy{$S4JjIZ9P5Mko=jLPq?eAoYtct6U&s6iPFw z0VGOvj7NuR1*Jig5m1tffQhG=AXO8$q*Y8M&fj6=Y54hewHN&`(t1_B1lP5R(i8dx|%`4MPOMUr(@7s;q8Dv zTOCtn6-PF&Fer6F?lL#G`rSr1M*4y{in)GjIs8R(ZOK!BP6ucaKm!M7t87C^gv)w8 zM{VsNy-9i3bfY6zsoS=oFUNfMe|CJbm+?y4{)b3?)`E8u?roq@NRR)-hD)bb8JlEd z)F-T@4&EGCn~_d$HMV5@$F-#m&yC4-#N=~Jdym2Kf64tyI)c%-lr|L@K^M46zQq zge5Iyqm+e38LS8_WJ}b613@x@^k~m;Q34)?6#)o9C0eZZMLDd?t|Qil(vM`A;DYA9 zCa`_GV+%QdV-*jjr(Qoc1`pb@0`?r8zWPfWgX^qR#$33oeKDGITG*Gej&RCp(j_W? zq}PGyLw`eNo@@bidmYp*1Jtbm)Gc+74vl)C5M2A2Po=-H?`5@#%-<5$`So=xcOy!? zPm)L^k}vy^`T%4Y|L#Vi*0m_B$G(bz+;9CgU~l}Uj}Ai`()i+l?1qW74R!lc*Fay+ z{BtLa>%O`;sV5s83`gMl$Ac#Cdsc=WxBggaywY2|h!=SM%>IzJxUzJpj#l1}z-o~^;2ZOYPyLdHQBA(K4y#zlPIUu{t#I0G}$%4MgIIpG~~I1ro0% zR`MuTgu?mi>hV%&%`T6rC8(eJ@&gf=}JNN5_Z;SNmlkGFzR<&)P-wA@Tf` zXquml@bF$S$MgU?es;epD?JTF^cX}$;(>@LAfnGFYU~kjk8vz76Cbnj)Q*@(w@3YJ zMUj9rw^%x)5+NG)-rwq8w9iR8h=zeunN#FlA- _geolocation.clearWatch(watchId); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index 13e9054d9c7c..e901f95b1334 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -37,6 +37,7 @@ class GoogleMapController { _heatmapsController = HeatmapsController(); _polygonsController = PolygonsController(stream: _streamController); _polylinesController = PolylinesController(stream: _streamController); + _myLocationController = MyLocationController(geolocationApi: WebGeolocationApi()); // Check if all markers are of the same type. Mixing marker types is not // allowed. @@ -170,6 +171,7 @@ class GoogleMapController { ClusterManagersController? _clusterManagersController; TileOverlaysController? _tileOverlaysController; GroundOverlaysController? _groundOverlaysController; + MyLocationController? _myLocationController; StreamSubscription? _onClickSubscription; StreamSubscription? _onRightClickSubscription; @@ -203,6 +205,7 @@ class GoogleMapController { ClusterManagersController? clusterManagers, TileOverlaysController? tileOverlays, GroundOverlaysController? groundOverlays, + MyLocationController? myLocation, }) { _overrideCreateMap = createMap; _overrideSetOptions = setOptions; @@ -214,6 +217,7 @@ class GoogleMapController { _clusterManagersController = clusterManagers ?? _clusterManagersController; _tileOverlaysController = tileOverlays ?? _tileOverlaysController; _groundOverlaysController = groundOverlays ?? _groundOverlaysController; + _myLocationController = myLocation ?? _myLocationController; } DebugCreateMapFunction? _overrideCreateMap; @@ -276,6 +280,7 @@ class GoogleMapController { // Now attach the geometry, traffic and any other layers... _renderInitialGeometry(); _setTrafficLayer(map, _lastMapConfiguration.trafficEnabled ?? false); + _renderMyLocation(map, _lastMapConfiguration); } // Funnels map gmap events into the plugin's stream controller. @@ -430,6 +435,7 @@ class GoogleMapController { _setOptions(newOptions); _setTrafficLayer(_googleMap!, newConfiguration.trafficEnabled ?? false); + _renderMyLocation(_googleMap!, newConfiguration); } /// Updates the map options with a new list of [styles]. @@ -610,6 +616,25 @@ class GoogleMapController { _markersController?.hideMarkerInfoWindow(markerId); } + /// Render my location + Future _renderMyLocation(gmaps.Map map, MapConfiguration mapConfiguration) async { + if (mapConfiguration.myLocationEnabled != true) { + _myLocationController?.removeMyLocationButton(map); + _myLocationController?.removeBlueDot(_markersController!); + return; + } + + await _myLocationController?.displayAndWatchMyLocation(_markersController!); + await _myLocationController?.centerMyCurrentLocation(this); + + if (mapConfiguration.myLocationButtonEnabled != true) { + _myLocationController?.removeMyLocationButton(map); + return; + } + + _myLocationController?.addMyLocationButton(map, this); + } + /// Returns true if the [InfoWindow] of the marker identified by [MarkerId] is shown. bool isInfoWindowShown(MarkerId markerId) { return _markersController?.isInfoWindowShown(markerId) ?? false; @@ -648,6 +673,8 @@ class GoogleMapController { _onIdleSubscription?.cancel(); _onIdleSubscription = null; _streamController.close(); + _myLocationController?.dispose(); + _myLocationController = null; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart index 863dcfad514c..c665e9278d1a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart @@ -124,7 +124,7 @@ class GoogleMapsInspectorWeb extends GoogleMapsInspectorPlatform { @override Future isMyLocationButtonEnabled({required int mapId}) async { - return false; // My Location widget not supported on the web + return _configurationProvider(mapId).myLocationButtonEnabled ?? false; } @override diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/my_location.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/my_location.dart new file mode 100644 index 000000000000..ad3b00755ba9 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/my_location.dart @@ -0,0 +1,260 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of '../google_maps_flutter_web.dart'; + +const _kMyLocationButtonId = 'my_location_button'; +const _kMyLocationBlueDot = 'my_location_blue_dot'; + +/// This class manages the current location and the my location button. +class MyLocationController { + /// Creates a [MyLocationController] with the given [GeolocationApi]. + MyLocationController({required GeolocationApi geolocationApi}) : _geolocationApi = geolocationApi; + + final GeolocationApi _geolocationApi; + + /// The my location button + MyLocationButton? myLocationButton; + LatLng? _lastKnownLocation; + int? _watchId; + + /// Watch current location and update blue dot + Future displayAndWatchMyLocation( + MarkersController markersController, + ) async { + final Marker marker = await _createBlueDotMarker(); + + if (_lastKnownLocation != null) { + _setBlueDotMarker(markersController, marker.copyWith(positionParam: _lastKnownLocation)); + } + + _watchId = _geolocationApi.watchPosition((double latitude, double longitude) { + _lastKnownLocation = LatLng(latitude, longitude); + + // TODO(Zubii12): https://github.com/flutter/plugins/pull/6868#discussion_r1057898052 + // We're discarding a lot of information from coords, like its accuracy, heading and speed. Those can be used to: + // - Render a bigger "blue halo" around the current position marker when the accuracy is low. + // - Render the direction in which we're looking at with a small "cone" using the heading information. + // - Render the current position marker as an arrow when the current position is "moving" (speed > certain threshold), and the direction in which the arrow should point (again, with the heading information). + + final Marker markerUpdate = marker.copyWith(positionParam: _lastKnownLocation); + + _setBlueDotMarker(markersController, markerUpdate); + }, (dynamic error) => myLocationButton?.doneAnimation()); + } + + void _setBlueDotMarker(MarkersController markersController, Marker marker) { + if (markersController.markers.containsKey(marker.markerId)) { + markersController.changeMarkers({marker}); + } else { + markersController.addMarkers({marker}); + } + } + + /// Get current location + Future _getCurrentLocation() async { + if (_lastKnownLocation != null) { + return _lastKnownLocation; + } + + final completer = Completer(); + + _geolocationApi.getCurrentPosition( + (double latitude, double longitude) { + if (completer.isCompleted) { + return; + } + final latLng = LatLng(latitude, longitude); + _lastKnownLocation = latLng; + completer.complete(latLng); + }, + (dynamic error) { + if (completer.isCompleted) { + return; + } + completer.complete(null); + }, + ); + + try { + return await completer.future.timeout(const Duration(seconds: 31)); + } on TimeoutException { + return null; + } + } + + /// Find and move to current location + Future centerMyCurrentLocation(GoogleMapController controller) async { + try { + final LatLng? location = await _getCurrentLocation(); + + if (location != null) { + await controller.moveCamera(CameraUpdate.newLatLng(location)); + } + myLocationButton?.doneAnimation(); + } catch (e) { + myLocationButton?.disableBtn(); + } + } + + /// Add my location to map + void addMyLocationButton(gmaps.Map map, GoogleMapController controller) { + myLocationButton = MyLocationButton(); + myLocationButton?.addClickListener((_) => centerMyCurrentLocation(controller)); + map.addListener( + 'dragend', + () { + myLocationButton?.resetAnimation(); + }.toJS, + ); + map.addListener( + 'center_changed', + () { + myLocationButton?.resetAnimation(); + }.toJS, + ); + if (myLocationButton != null) { + map.controls[gmaps.ControlPosition.RIGHT_BOTTOM as int].push(myLocationButton!.getButton); + } + } + + /// Remove my location button from map + void removeMyLocationButton(gmaps.Map map) { + if (myLocationButton != null) { + map.controls[gmaps.ControlPosition.RIGHT_BOTTOM as int].pop(); + myLocationButton = null; + } + } + + /// Remove blue dot from map + void removeBlueDot(MarkersController markersController) { + const markerId = MarkerId(_kMyLocationBlueDot); + + if (markersController.markers.containsKey(markerId)) { + markersController.removeMarkers({markerId}); + } + } + + /// Create blue dot marker + Future _createBlueDotMarker() async { + final BitmapDescriptor icon = await BitmapDescriptor.fromAssetImage( + const ImageConfiguration(size: Size(18, 18)), + 'assets/blue-dot.png', + package: 'google_maps_flutter_web', + ); + + return Marker(markerId: const MarkerId(_kMyLocationBlueDot), icon: icon, zIndex: .5); + } + + /// Dispose the controller and stop watching the position + void dispose() { + if (_watchId == null) { + return; + } + + _geolocationApi.clearWatch(_watchId!); + _watchId = null; + } +} + +/// This class support create my location button & handle animation +class MyLocationButton { + /// Add css and create my location button + MyLocationButton() { + _addCss(); + _createButton(); + } + + late web.HTMLButtonElement _btnChild; + late web.HTMLDivElement _imageChild; + late web.HTMLDivElement _controlDiv; + + /// Add animation css + void _addCss() { + final styleElement = web.HTMLStyleElement(); + web.document.head?.append(styleElement); + final web.CSSStyleSheet? sheet = styleElement.sheet; + var rule = '.waiting { animation: 1000ms infinite step-end blink-position-icon;}'; + sheet?.insertRule(rule); + rule = + '@keyframes blink-position-icon {0% {background-position: -24px 0px;} ' + '50% {background-position: 0px 0px;}}'; + sheet?.insertRule(rule); + } + + /// Add My Location widget to right bottom + void _createButton() { + _controlDiv = web.HTMLDivElement(); + + _controlDiv.style.marginRight = '10px'; + + _btnChild = web.HTMLButtonElement(); + _btnChild.className = 'gm-control-active'; + _btnChild.style.backgroundColor = '#fff'; + _btnChild.style.border = 'none'; + _btnChild.style.outline = 'none'; + _btnChild.style.width = '40px'; + _btnChild.style.height = '40px'; + _btnChild.style.borderRadius = '2px'; + _btnChild.style.boxShadow = '0 1px 4px rgba(0,0,0,0.3)'; + _btnChild.style.cursor = 'pointer'; + _btnChild.style.padding = '8px'; + _controlDiv.append(_btnChild); + + _imageChild = web.HTMLDivElement(); + _imageChild.style.width = '24px'; + _imageChild.style.height = '24px'; + _imageChild.style.backgroundImage = + 'url(${web.window.location.href.replaceAll('/#', '')}/assets/packages/google_maps_flutter_web/assets/my_location-sprite-2x.png)'; + _imageChild.style.backgroundSize = '240px 24px'; + _imageChild.style.backgroundPosition = '0px 0px'; + _imageChild.style.backgroundRepeat = 'no-repeat'; + _imageChild.id = _kMyLocationButtonId; + _btnChild.append(_imageChild); + } + + /// Get button element + web.HTMLElement get getButton => _controlDiv; + + /// Add click listener + void addClickListener(void Function(web.Event)? listener) { + _btnChild.addEventListener('click', listener?.toJS); + } + + /// Reset animation + void resetAnimation() { + if (_btnChild.disabled) { + _imageChild.style.backgroundPosition = '-24px 0px'; + } else { + _imageChild.style.backgroundPosition = '0px 0px'; + } + } + + /// Start animation + void startAnimation() { + if (_btnChild.disabled) { + return; + } + _imageChild.classList.add('waiting'); + } + + /// Done animation + void doneAnimation() { + if (_btnChild.disabled) { + return; + } + _imageChild.classList.remove('waiting'); + _imageChild.style.backgroundPosition = '-192px 0px'; + } + + /// Disable button + void disableBtn() { + _btnChild.disabled = true; + _imageChild.classList.remove('waiting'); + _imageChild.style.backgroundPosition = '-24px 0px'; + } + + /// Check button disabled or enabled + bool isDisabled() => _btnChild.disabled; +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 9a19bffc4e88..be5d4d4c2bdf 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -15,6 +15,8 @@ flutter: web: pluginClass: GoogleMapsPlugin fileName: google_maps_flutter_web.dart + assets: + - assets/ dependencies: collection: ^1.16.0 From 98d800e46299b9988653a7d2a8f43130a87e0ee1 Mon Sep 17 00:00:00 2001 From: Zubii Date: Sun, 12 Jul 2026 16:05:22 +0300 Subject: [PATCH 2/5] test(google_maps_flutter_web): add integration tests --- .../google_maps_controller_test.dart | 118 ++++++++++++++++++ .../google_maps_controller_test.mocks.dart | 84 +++++++++++++ .../google_maps_plugin_test.dart | 5 +- .../google_maps_plugin_test.mocks.dart | 56 +++++++++ 4 files changed, 262 insertions(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_controller_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_controller_test.dart index bf1a77889f2b..a4183aaf9146 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_controller_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_controller_test.dart @@ -32,6 +32,8 @@ gmaps.Map mapShim() => throw UnimplementedError(); ), MockSpec(fallbackGenerators: {#googleMap: mapShim}), MockSpec(fallbackGenerators: {#googleMap: mapShim}), + MockSpec(fallbackGenerators: {#googleMap: mapShim}), + MockSpec(fallbackGenerators: {#googleMap: mapShim}), ]) /// Test Google Map Controller void main() { @@ -1025,5 +1027,121 @@ void main() { verify(mock.isInfoWindowShown(markerId)); }); }); + + group('My Location', () { + late MockMyLocationController mockMyLocationController; + late MockGeolocationApi mockGeolocationApi; + + setUp(() { + mockMyLocationController = MockMyLocationController(); + mockGeolocationApi = MockGeolocationApi(); + }); + + testWidgets('by default is disabled', (WidgetTester tester) async { + controller = createController(); + controller.init(); + expect(mockMyLocationController.myLocationButton, isNull); + }); + + testWidgets('initializes with my location & display my location button', ( + WidgetTester tester, + ) async { + const currentLocation = LatLng(10.8231, 106.6297); + final map = gmaps.Map(createDivElement()); + final markers = MockMarkersController(); + + controller = createController( + mapConfiguration: const MapConfiguration( + myLocationEnabled: true, + myLocationButtonEnabled: true, + ), + ); + + final myLocationController = MyLocationController(geolocationApi: mockGeolocationApi); + + controller.debugSetOverrides( + createMap: (_, _) => map, + markers: markers, + myLocation: myLocationController, + ); + + when(mockGeolocationApi.watchPosition(any, any)).thenAnswer((inv) { + final onSuccess = inv.positionalArguments[0] as void Function(double, double); + onSuccess(currentLocation.latitude, currentLocation.longitude); + return 0; + }); + when( + mockGeolocationApi.getCurrentPosition(any, any, timeoutMs: anyNamed('timeoutMs')), + ).thenAnswer((inv) { + final onSuccess = inv.positionalArguments[0] as void Function(double, double); + onSuccess(currentLocation.latitude, currentLocation.longitude); + }); + + controller.init(); + await tester.pumpAndSettle(); + + final List> allAddMarkersCalls = verify( + markers.addMarkers(captureAny), + ).captured.cast>(); + final Set blueDotCall = allAddMarkersCalls.firstWhere( + (set) => set.any((m) => m.markerId == const MarkerId('my_location_blue_dot')), + orElse: () => {}, + ); + + expect(blueDotCall.length, 1); + expect(blueDotCall.first.position, currentLocation); + expect(blueDotCall.first.zIndex, .5); + expect(map.controls[gmaps.ControlPosition.RIGHT_BOTTOM as int].length, equals(1)); + }); + + testWidgets('initializes with my location only', (WidgetTester tester) async { + final map = gmaps.Map(createDivElement()); + final markers = MockMarkersController(); + const currentLocation = LatLng(10.8231, 106.6297); + + controller = createController( + mapConfiguration: const MapConfiguration( + myLocationEnabled: true, + myLocationButtonEnabled: false, + ), + ); + + final myLocationController = MyLocationController(geolocationApi: mockGeolocationApi); + + controller.debugSetOverrides( + createMap: (_, _) => map, + markers: markers, + myLocation: myLocationController, + ); + + when(mockGeolocationApi.watchPosition(any, any)).thenAnswer((inv) { + final onSuccess = inv.positionalArguments[0] as void Function(double, double); + onSuccess(currentLocation.latitude, currentLocation.longitude); + return 0; + }); + when( + mockGeolocationApi.getCurrentPosition(any, any, timeoutMs: anyNamed('timeoutMs')), + ).thenAnswer((inv) { + final onSuccess = inv.positionalArguments[0] as void Function(double, double); + onSuccess(currentLocation.latitude, currentLocation.longitude); + }); + + controller.init(); + await tester.pumpAndSettle(); + + final List> allAddMarkersCalls = verify( + markers.addMarkers(captureAny), + ).captured.cast>(); + final Set blueDotCall = allAddMarkersCalls.firstWhere( + (set) => set.any((m) => m.markerId == const MarkerId('my_location_blue_dot')), + orElse: () => {}, + ); + + expect(blueDotCall.length, 1); + expect(blueDotCall.first.position, currentLocation); + expect(blueDotCall.first.zIndex, 0.5); + expect(map.controls[gmaps.ControlPosition.RIGHT_BOTTOM as int].length, equals(0)); + }); + }); }); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_controller_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_controller_test.mocks.dart index 6da195b8f167..249bf6f37bfb 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_controller_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_controller_test.mocks.dart @@ -498,3 +498,87 @@ class MockGroundOverlaysController extends _i1.Mock implements _i2.GroundOverlay returnValueForMissingStub: null, ); } + +/// A class which mocks [MyLocationController]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockMyLocationController extends _i1.Mock implements _i2.MyLocationController { + @override + set myLocationButton(_i2.MyLocationButton? value) => super.noSuchMethod( + Invocation.setter(#myLocationButton, value), + returnValueForMissingStub: null, + ); + + @override + _i6.Future displayAndWatchMyLocation( + _i2.MarkersController? markersController, + ) => + (super.noSuchMethod( + Invocation.method(#displayAndWatchMyLocation, [markersController]), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) + as _i6.Future); + + @override + _i6.Future centerMyCurrentLocation(_i2.GoogleMapController? controller) => + (super.noSuchMethod( + Invocation.method(#centerMyCurrentLocation, [controller]), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) + as _i6.Future); + + @override + void addMyLocationButton(_i4.Map? map, _i2.GoogleMapController? controller) => super.noSuchMethod( + Invocation.method(#addMyLocationButton, [map, controller]), + returnValueForMissingStub: null, + ); + + @override + void removeMyLocationButton(_i4.Map? map) => super.noSuchMethod( + Invocation.method(#removeMyLocationButton, [map]), + returnValueForMissingStub: null, + ); + + @override + void removeBlueDot(_i2.MarkersController? markersController) => + super.noSuchMethod( + Invocation.method(#removeBlueDot, [markersController]), + returnValueForMissingStub: null, + ); + + @override + void dispose() => + super.noSuchMethod(Invocation.method(#dispose, []), returnValueForMissingStub: null); +} + +/// A class which mocks [GeolocationApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockGeolocationApi extends _i1.Mock implements _i2.GeolocationApi { + @override + int watchPosition(void Function(double, double)? onSuccess, void Function(dynamic)? onError) => + (super.noSuchMethod( + Invocation.method(#watchPosition, [onSuccess, onError]), + returnValue: 0, + returnValueForMissingStub: 0, + ) + as int); + + @override + void getCurrentPosition( + void Function(double, double)? onSuccess, + void Function(dynamic)? onError, { + int? timeoutMs = 30000, + }) => super.noSuchMethod( + Invocation.method(#getCurrentPosition, [onSuccess, onError], {#timeoutMs: timeoutMs}), + returnValueForMissingStub: null, + ); + + @override + void clearWatch(int? watchId) => super.noSuchMethod( + Invocation.method(#clearWatch, [watchId]), + returnValueForMissingStub: null, + ); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_plugin_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_plugin_test.dart index ca1cde5334d9..e9b7cb492d2e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_plugin_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_plugin_test.dart @@ -15,7 +15,10 @@ import 'package:integration_test/integration_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -@GenerateNiceMocks(>[MockSpec()]) +@GenerateNiceMocks(>[ + MockSpec(), + MockSpec(), +]) import 'google_maps_plugin_test.mocks.dart'; /// Test GoogleMapsPlugin diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_plugin_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_plugin_test.mocks.dart index 94cfdf241871..5fc689e3ca39 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_plugin_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_plugin_test.mocks.dart @@ -120,6 +120,7 @@ class MockGoogleMapController extends _i1.Mock implements _i4.GoogleMapControlle _i6.ClusterManagersController? clusterManagers, _i4.TileOverlaysController? tileOverlays, _i4.GroundOverlaysController? groundOverlays, + _i4.MyLocationController? myLocation, }) => super.noSuchMethod( Invocation.method(#debugSetOverrides, [], { #createMap: createMap, @@ -132,6 +133,7 @@ class MockGoogleMapController extends _i1.Mock implements _i4.GoogleMapControlle #clusterManagers: clusterManagers, #tileOverlays: tileOverlays, #groundOverlays: groundOverlays, + #myLocation: myLocation, }), returnValueForMissingStub: null, ); @@ -297,3 +299,57 @@ class MockGoogleMapController extends _i1.Mock implements _i4.GoogleMapControlle void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), returnValueForMissingStub: null); } + +/// A class which mocks [MyLocationController]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockMyLocationController extends _i1.Mock implements _i4.MyLocationController { + @override + set myLocationButton(_i4.MyLocationButton? value) => super.noSuchMethod( + Invocation.setter(#myLocationButton, value), + returnValueForMissingStub: null, + ); + + @override + _i3.Future displayAndWatchMyLocation( + _i4.MarkersController? markersController, + ) => + (super.noSuchMethod( + Invocation.method(#displayAndWatchMyLocation, [markersController]), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) + as _i3.Future); + + @override + _i3.Future centerMyCurrentLocation(_i4.GoogleMapController? controller) => + (super.noSuchMethod( + Invocation.method(#centerMyCurrentLocation, [controller]), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) + as _i3.Future); + + @override + void addMyLocationButton(_i5.Map? map, _i4.GoogleMapController? controller) => super.noSuchMethod( + Invocation.method(#addMyLocationButton, [map, controller]), + returnValueForMissingStub: null, + ); + + @override + void removeMyLocationButton(_i5.Map? map) => super.noSuchMethod( + Invocation.method(#removeMyLocationButton, [map]), + returnValueForMissingStub: null, + ); + + @override + void removeBlueDot(_i4.MarkersController? markersController) => + super.noSuchMethod( + Invocation.method(#removeBlueDot, [markersController]), + returnValueForMissingStub: null, + ); + + @override + void dispose() => + super.noSuchMethod(Invocation.method(#dispose, []), returnValueForMissingStub: null); +} From 80f7b5ad28735293f41890b2f2c1bd636e835780 Mon Sep 17 00:00:00 2001 From: Zubii Date: Sun, 12 Jul 2026 16:13:11 +0300 Subject: [PATCH 3/5] chore(google_maps_flutter): bump package versions, update changelogs & md files --- packages/google_maps_flutter/google_maps_flutter_web/AUTHORS | 1 + .../google_maps_flutter/google_maps_flutter_web/CHANGELOG.md | 4 ++++ .../google_maps_flutter/google_maps_flutter_web/README.md | 5 ----- .../google_maps_flutter/google_maps_flutter_web/pubspec.yaml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/AUTHORS b/packages/google_maps_flutter/google_maps_flutter_web/AUTHORS index 906ab219beee..2d5b11ddc211 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/AUTHORS +++ b/packages/google_maps_flutter/google_maps_flutter_web/AUTHORS @@ -66,3 +66,4 @@ Alex Li Rahul Raj <64.rahulraj@gmail.com> Justin Baumann Joonas Kerttula +Zubsacu Ionut diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md index c8657c54f8d8..583f1bd2b9d3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.3 + +* Add support for my location button. + ## 0.6.2+3 * Updates README to include setup information. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/README.md b/packages/google_maps_flutter/google_maps_flutter_web/README.md index ada93c74d823..e69ee0b28f65 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/README.md @@ -100,11 +100,6 @@ The following map options are not available in web, because the map doesn't rota There's no "Map Toolbar" in web, so the `mapToolbarEnabled` option is unused. -There's no "My Location" widget in web ([tracking issue](https://github.com/flutter/flutter/issues/64073)), so the following options are ignored, for now: - -* `myLocationButtonEnabled` -* `myLocationEnabled` - There's no `defaultMarkerWithHue` in web. If you need colored pins/markers, you may need to use your own asset images. Indoor and building layers are still not available on the web. Traffic is. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index be5d4d4c2bdf..95d582bc4c17 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 0.6.2+3 +version: 0.6.3 environment: sdk: ^3.10.0 From 97aa32c501fa63126039c3d5cf855d2d9f0ea874 Mon Sep 17 00:00:00 2001 From: Zubii Date: Sun, 12 Jul 2026 16:13:47 +0300 Subject: [PATCH 4/5] chore(google_maps_flutter): add temporary dependency_overrides for federated plugin testing --- .../google_maps_flutter/example/pubspec.yaml | 5 +++++ .../google_maps_flutter/google_maps_flutter/pubspec.yaml | 6 ++++++ .../google_maps_flutter_web/example/latest/pubspec.yaml | 4 ++++ .../google_maps_flutter_web/pubspec.yaml | 8 ++++++++ 4 files changed, 23 insertions(+) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml index 6bd36da0ee9a..273c0004e323 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml @@ -32,3 +32,8 @@ flutter: uses-material-design: true assets: - assets/ +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins +dependency_overrides: + google_maps_flutter_platform_interface: {path: /Users/zubii/Projects/packages/packages/google_maps_flutter/google_maps_flutter_platform_interface} + google_maps_flutter_web: {path: ../../../../packages/google_maps_flutter/google_maps_flutter_web} diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 0342292634b8..ce30cd7979bb 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -41,3 +41,9 @@ topics: # The example deliberately includes limited-use secrets. false_secrets: - /example/web/index.html + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins +dependency_overrides: + google_maps_flutter_platform_interface: {path: /Users/zubii/Projects/packages/packages/google_maps_flutter/google_maps_flutter_platform_interface} + google_maps_flutter_web: {path: ../../../packages/google_maps_flutter/google_maps_flutter_web} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/pubspec.yaml index b5e0c77bbe65..7424ecea40dc 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/pubspec.yaml @@ -28,7 +28,11 @@ flutter: assets: - assets/ +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins dependency_overrides: + google_maps_flutter: + { path: ../../../../../packages/google_maps_flutter/google_maps_flutter } # Override the google_maps_flutter dependency on google_maps_flutter_web. # TODO(ditman): Unwind the circular dependency. This will create problems # if we need to make a breaking change to google_maps_flutter_web. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 95d582bc4c17..c11ba1fac963 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -42,3 +42,11 @@ topics: # The example deliberately includes limited-use secrets. false_secrets: - /example/**/web/index.html + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins +dependency_overrides: + google_maps_flutter_platform_interface: + { + path: /Users/zubii/Projects/packages/packages/google_maps_flutter/google_maps_flutter_platform_interface, + } From 771d7702100dd9e0723679094ea54c00b22a0b35 Mon Sep 17 00:00:00 2001 From: Zubii Date: Wed, 15 Jul 2026 00:10:17 +0300 Subject: [PATCH 5/5] chore(google_maps_flutter_web): fix changelog --- .../google_maps_flutter/google_maps_flutter_web/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md index 583f1bd2b9d3..fa2d54c310a2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.6.3 -* Add support for my location button. +* Adds support for my location button. ## 0.6.2+3