Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
5f861c5
Add my location button
nploi Dec 12, 2022
d9a4fa6
Move to current location when click
nploi Dec 13, 2022
1450b75
Add blue dot
nploi Dec 14, 2022
03ed666
Update my location icon
nploi Dec 14, 2022
72b6553
Add blue dot
nploi Dec 14, 2022
ea3dd73
Add animation
nploi Dec 19, 2022
64668d0
Update my location element
nploi Dec 19, 2022
e2a5a24
Update my location element
nploi Dec 19, 2022
1ae5172
Update my location element
nploi Dec 19, 2022
9abc526
Add blue dot
nploi Dec 19, 2022
b986d65
Add blue dot icon
nploi Dec 20, 2022
c358fe8
Format code
nploi Dec 20, 2022
278b043
Update UI
nploi Dec 20, 2022
a72e0a1
Update types
nploi Dec 20, 2022
7ad54b5
Revert code
nploi Dec 20, 2022
e0c6c3d
Revert code
nploi Dec 20, 2022
ea4cdef
Add integration test
nploi Dec 24, 2022
1cdce7a
Revert code
nploi Dec 24, 2022
58c74ec
Fix test fail
nploi Dec 25, 2022
7bd8aa4
Fix test fail
nploi Dec 25, 2022
1b62e8c
Add more test & fix test fail
nploi Dec 25, 2022
4d6ba54
Revert code
nploi Dec 25, 2022
9d40e2e
Fix spelling
nploi Dec 25, 2022
f3a8fc9
Updated version & changelog
nploi Dec 25, 2022
77414ec
Updated version & changelog
nploi Dec 25, 2022
6668606
Updated version & changelog
nploi Dec 25, 2022
1b4b0bb
Move code to separate file, update logic, add mylocation-sprite-2x to…
nploi Dec 28, 2022
241f69d
Update logic watch position and check permission
nploi Dec 31, 2022
c0fff06
Rename method
nploi Dec 31, 2022
1747092
Revert code
nploi Dec 31, 2022
dd51548
Fix unit-test & fix logic
nploi Jan 1, 2023
c99c506
Update blue dot icon & add todo me
nploi Jan 1, 2023
6c0aecd
Fix lint
nploi Jan 1, 2023
cbc6bc2
Add unit-test for location permission
nploi Jan 1, 2023
766d690
Add author
nploi Jan 1, 2023
d85e6b8
Fix error syntax
nploi Jan 1, 2023
f99bce4
Revert code
nploi Jan 1, 2023
9755380
Fix lint
nploi Jan 1, 2023
65630ef
Merge branch 'main' of https://github.com/nploi/plugins into impl-my-…
nploi Jan 11, 2023
42e307a
Fix linter
nploi Jan 11, 2023
0090120
Merge branch 'main' of https://github.com/nploi/plugins into impl-my-…
nploi Jan 23, 2023
3db8113
Merge branch 'main' into impl-my-location
nploi Jan 25, 2023
9270247
Fix linter
nploi Jan 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 0.4.0+4
Comment thread
nploi marked this conversation as resolved.
Outdated

* Add "My Location" Widget. Issue [#64073](https://github.com/flutter/flutter/issues/64073)
* Updates code for `no_leading_underscores_for_local_identifiers` lint.

## 0.4.0+3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,74 @@ void main() {
expect(controller.trafficLayer, isNotNull);
});
});

group('My Location', () {
testWidgets('by default is disabled', (WidgetTester tester) async {
controller = createController();
controller.init();
expect(controller.myLocationButton, isNull);
});

testWidgets('initializes with my location with my location button',
(WidgetTester tester) async {
const LatLng currentLocation = LatLng(10.8231, 106.6297);
controller = createController(
mapConfiguration: const MapConfiguration(
myLocationEnabled: true,
myLocationButtonEnabled: true,
));
controller.debugSetOverrides(
createMap: (_, __) => map,
markers: markers,
getCurrentLocation: () => Future<LatLng>.value(currentLocation),
);
controller.init();

await Future<void>.delayed(const Duration(milliseconds: 50));

final Set<Marker> capturedMarkers =
verify(markers.addMarkers(captureAny)).captured[1] as Set<Marker>;

final gmaps.LatLng gmCenter = map.center!;

expect(controller.myLocationButton, isNotNull);
expect(capturedMarkers.length, 1);
expect(capturedMarkers.first.position, currentLocation);
expect(capturedMarkers.first.zIndex, 0.5);
expect(gmCenter.lat, currentLocation.latitude);
expect(gmCenter.lng, currentLocation.longitude);
});

testWidgets('initializes with my location without my location button',
(WidgetTester tester) async {
const LatLng currentLocation = LatLng(10.8231, 106.6297);
controller = createController(
mapConfiguration: const MapConfiguration(
myLocationEnabled: true,
myLocationButtonEnabled: false,
));
controller.debugSetOverrides(
createMap: (_, __) => map,
markers: markers,
getCurrentLocation: () => Future<LatLng>.value(currentLocation),
);
controller.init();

await Future<void>.delayed(const Duration(milliseconds: 50));

final Set<Marker> capturedMarkers =
verify(markers.addMarkers(captureAny)).captured[1] as Set<Marker>;

final gmaps.LatLng gmCenter = map.center!;

expect(controller.myLocationButton, isNull);
expect(capturedMarkers.length, 1);
expect(capturedMarkers.first.position, currentLocation);
expect(capturedMarkers.first.zIndex, 0.5);
expect(gmCenter.lat, currentLocation.latitude);
expect(gmCenter.lng, currentLocation.longitude);
});
});
});

// These are the methods that are delegated to the gmaps.GMap object, that we can mock...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class MockGoogleMapController extends _i1.Mock
_i4.CirclesController? circles,
_i4.PolygonsController? polygons,
_i4.PolylinesController? polylines,
_i4.DebugGetCurrentLocation? getCurrentLocation,
}) =>
super.noSuchMethod(
Invocation.method(
Expand All @@ -112,6 +113,7 @@ class MockGoogleMapController extends _i1.Mock
#circles: circles,
#polygons: polygons,
#polylines: polylines,
#getCurrentLocation: getCurrentLocation,
},
),
returnValueForMissingStub: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
<head>
<title>Browser Tests</title>
<!-- This API key comes from: go/flutter-maps-web-tests-api-key (GCP project: flutter-infra) -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAa9cRBkhuxGq3Xw3HPz8SPwaVOhRmm7kk&libraries=geometry"></script>
</head>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAa9cRBkhuxGq3Xw3HPz8SPwaVOhRmm7kk&libraries=geometry"></script> </head>
Comment thread
nploi marked this conversation as resolved.
Outdated
<body>
<script src="main.dart.js"></script>
</body>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ double _getCssOpacity(Color color) {
// myLocationEnabled needs to be built through dart:html navigator.geolocation
// See: https://api.dart.dev/stable/2.8.4/dart-html/Geolocation-class.html
// trafficEnabled is handled when creating the GMap object, since it needs to be added as a layer.
// trackCameraPosition is just a boolan value that indicates if the map has an onCameraMove handler.
// trackCameraPosition is just a boolean value that indicates if the map has an onCameraMove handler.
// indoorViewEnabled seems to not have an equivalent in web
// buildingsEnabled seems to not have an equivalent in web
// padding seems to behave differently in web than mobile. You can't move UI elements in web.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ part of google_maps_flutter_web;
typedef DebugCreateMapFunction = gmaps.GMap Function(
HtmlElement div, gmaps.MapOptions options);

/// Type used when passing an override to the _getCurrentLocation function.
@visibleForTesting
typedef DebugGetCurrentLocation = Future<LatLng> Function();

/// Encapsulates a [gmaps.GMap], its events, and where in the DOM it's rendered.
class GoogleMapController {
/// Initializes the GMap, and the sub-controllers related to it. Wires events.
Expand Down Expand Up @@ -64,6 +68,7 @@ class GoogleMapController {
// The Flutter widget that contains the rendered Map.
HtmlElementView? _widget;
late HtmlElement _div;
HtmlElement? _myLocationButton;

/// The Flutter widget that will contain the rendered Map. Used for caching.
Widget? get widget {
Expand All @@ -75,6 +80,10 @@ class GoogleMapController {
return _widget;
}

/// A getter for the my location button
@visibleForTesting
HtmlElement? get myLocationButton => _myLocationButton;

// The currently-enabled traffic layer.
gmaps.TrafficLayer? _trafficLayer;

Expand Down Expand Up @@ -114,14 +123,19 @@ class GoogleMapController {
CirclesController? circles,
PolygonsController? polygons,
PolylinesController? polylines,
DebugGetCurrentLocation? getCurrentLocation,
}) {
_overrideCreateMap = createMap;
_markersController = markers ?? _markersController;
_circlesController = circles ?? _circlesController;
_polygonsController = polygons ?? _polygonsController;
_polylinesController = polylines ?? _polylinesController;
_overrideGetCurrentLocation = getCurrentLocation;
}

// Get current location
DebugGetCurrentLocation? _overrideGetCurrentLocation;

DebugCreateMapFunction? _overrideCreateMap;

gmaps.GMap _createMap(HtmlElement div, gmaps.MapOptions options) {
Expand Down Expand Up @@ -163,6 +177,7 @@ class GoogleMapController {

// Create the map...
final gmaps.GMap map = _createMap(_div, options);

_googleMap = map;

_attachMapEvents(map);
Expand All @@ -177,6 +192,14 @@ class GoogleMapController {
);

_setTrafficLayer(map, _lastMapConfiguration.trafficEnabled ?? false);

if ((_lastMapConfiguration.myLocationEnabled ?? false) &&
(_lastMapConfiguration.myLocationButtonEnabled ?? false)) {
_addMyLocationButton(map);
}
if (_lastMapConfiguration.myLocationEnabled ?? false) {
_moveToCurrentLocation();
}
Comment thread
nploi marked this conversation as resolved.
Outdated
}

// Funnels map gmap events into the plugin's stream controller.
Expand Down Expand Up @@ -413,6 +436,113 @@ class GoogleMapController {
return _markersController?.isInfoWindowShown(markerId) ?? false;
}

// Add My Location widget to right bottom
HtmlElement _createMyLocationButton() {
Comment thread
nploi marked this conversation as resolved.
Outdated
final HtmlElement controlDiv = DivElement();
controlDiv.style.marginRight = '10px';

final HtmlElement firstChild = ButtonElement();
firstChild.className = 'gm-control-active';
firstChild.style.backgroundColor = '#fff';
firstChild.style.border = 'none';
firstChild.style.outline = 'none';
firstChild.style.width = '40px';
firstChild.style.height = '40px';
firstChild.style.borderRadius = '2px';
firstChild.style.boxShadow = '0 1px 4px rgba(0,0,0,0.3)';
firstChild.style.cursor = 'pointer';
firstChild.style.padding = '8px';
controlDiv.append(firstChild);

final HtmlElement secondChild = DivElement();
secondChild.style.width = '24px';
secondChild.style.height = '24px';
secondChild.style.backgroundImage =
'url(https://maps.gstatic.com/tactile/mylocation/mylocation-sprite-2x.png)';
Comment thread
nploi marked this conversation as resolved.
Outdated
secondChild.style.backgroundSize = '240px 24px';
secondChild.style.backgroundPosition = '0px 0px';
secondChild.style.backgroundRepeat = 'no-repeat';
secondChild.id = 'you_location_img';
firstChild.append(secondChild);

firstChild.addEventListener('click', (_) {
String imgX = '0';
// Add animation when find current location
final Timer timer =
Timer.periodic(const Duration(milliseconds: 500), (_) {
imgX = (imgX == '-24') ? '0' : '-24';
document.getElementById('you_location_img')?.style.backgroundPosition =
'${imgX}px 0px';
Comment thread
nploi marked this conversation as resolved.
Outdated
Comment thread
nploi marked this conversation as resolved.
Outdated
});
// Find and move to current location
_moveToCurrentLocation().then((_) {
timer.cancel();
document.getElementById('you_location_img')?.style.backgroundPosition =
'-192px 0px';
Comment thread
nploi marked this conversation as resolved.
Outdated
});
});

return controlDiv;
}

// Get current location
Future<LatLng> _getCurrentLocation() async {
final Geoposition location =
await window.navigator.geolocation.getCurrentPosition();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method will not update the user's location if they move around, unless something else on the map changes. I think this should use the watchPosition API, that returns a Stream<Geoposition> and then respond to the events from that stream. Some changes to this PR:

  • If the user wants to render the MyLocation dot: subscribe to the watchPosition Stream, and on each Geolocation event update the Marker.
    • The first event of the watchPosition Stream can be used to remove the "waiting" animation class from the button, for example, and to get ready the marker that needs to be rendered.
  • If the user wants to stop rendering their location, it's easy to remove whatever subscription to the Stream that we do, and whatever cleanup is needed.

Some documentation:

return LatLng(
location.coords!.latitude!.toDouble(),
location.coords!.longitude!.toDouble(),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things here:

  1. What happens when the user denies the geolocation request from the browser? Does the await never return, or does this throw an error? There's a timeout property in the getCurrentPosition method that can be used to get this "unstuck" (say: wait for 30 seconds then fail).
  2. 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).

accuracy-and-heading

I think this should at least handle "1" gracefully. 2 can be left as a "todo" or a continuation to this PR.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm fixed "1", but i think i should do "2" in next pr :D

}

// Find and move to current location
Future<void> _moveToCurrentLocation() async {
Comment thread
nploi marked this conversation as resolved.
Outdated
LatLng location;
if (_overrideGetCurrentLocation != null) {
location = await _overrideGetCurrentLocation!.call();
} else {
location = await _getCurrentLocation();
}

await moveCamera(
CameraUpdate.newLatLng(location),
);

_addBlueDotMarker(location);
Comment thread
nploi marked this conversation as resolved.
Outdated
}

// Add my location to map
void _addMyLocationButton(gmaps.GMap map) {
_myLocationButton = _createMyLocationButton();

map.addListener('dragend', () {
document.getElementById('you_location_img')?.style.backgroundPosition =
'0px 0px';
});

map.controls![gmaps.ControlPosition.RIGHT_BOTTOM as int]
?.push(_myLocationButton);
}

// Add blue dot for current location
Future<void> _addBlueDotMarker(LatLng location) async {
assert(
_markersController != null, 'Cannot update markers after dispose().');
final BitmapDescriptor icon = await BitmapDescriptor.fromAssetImage(
const ImageConfiguration(size: Size(18, 18)),
'icons/blue-dot.png',
package: 'google_maps_flutter_web',
);
_markersController?.addMarkers(<Marker>{
Marker(
markerId: const MarkerId('my_location_blue_dot'),
icon: icon,
position: location,
zIndex: 0.5,
)
});
}

// Cleanup

/// Disposes of this controller and its resources.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: google_maps_flutter_web
description: Web platform implementation of google_maps_flutter
repository: https://github.com/flutter/plugins/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.4.0+3
version: 0.4.0+4
Comment thread
nploi marked this conversation as resolved.
Outdated

environment:
sdk: ">=2.12.0 <3.0.0"
Expand All @@ -15,7 +15,8 @@ flutter:
web:
pluginClass: GoogleMapsPlugin
fileName: google_maps_flutter_web.dart

assets:
- icons/
Comment thread
nploi marked this conversation as resolved.
dependencies:
flutter:
sdk: flutter
Expand Down