Skip to content

Commit

Permalink
[google_map_flutter] Add style to widget - platform impls (flutter#6205)
Browse files Browse the repository at this point in the history
Platform implementations portion of flutter#6192

Adds handling of `MapConfiguration.style` and implements new `getStyleError` method.

Part of flutter/flutter#66207
  • Loading branch information
stuartmorgan authored and LouiseHsu committed Mar 7, 2024
1 parent aeddce7 commit a05b772
Show file tree
Hide file tree
Showing 30 changed files with 426 additions and 92 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## NEXT
## 2.7.0

* Adds support for `MapConfiguration.style`.
* Adds support for `getStyleError`.
* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1.
* Updates compileSdk version to 34.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,10 @@ static void interpretGoogleMapOptions(Object o, GoogleMapOptionsSink sink) {
if (buildingsEnabled != null) {
sink.setBuildingsEnabled(toBoolean(buildingsEnabled));
}
final Object style = data.get("style");
if (style != null) {
sink.setMapStyle(toString(style));
}
}

/** Returns the dartMarkerId of the interpreted marker. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import android.content.Context;
import android.graphics.Rect;
import androidx.annotation.Nullable;
import com.google.android.gms.maps.GoogleMapOptions;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLngBounds;
Expand All @@ -27,6 +28,7 @@ class GoogleMapBuilder implements GoogleMapOptionsSink {
private Object initialCircles;
private List<Map<String, ?>> initialTileOverlays;
private Rect padding = new Rect(0, 0, 0, 0);
private @Nullable String style;

GoogleMapController build(
int id,
Expand All @@ -48,6 +50,7 @@ GoogleMapController build(
controller.setInitialCircles(initialCircles);
controller.setPadding(padding.top, padding.left, padding.bottom, padding.right);
controller.setInitialTileOverlays(initialTileOverlays);
controller.setMapStyle(style);
return controller;
}

Expand Down Expand Up @@ -178,4 +181,9 @@ public void setInitialCircles(Object initialCircles) {
public void setInitialTileOverlays(List<Map<String, ?>> initialTileOverlays) {
this.initialTileOverlays = initialTileOverlays;
}

@Override
public void setMapStyle(@Nullable String style) {
this.style = style;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/** Controller of a single GoogleMaps MapView instance. */
final class GoogleMapController
Expand Down Expand Up @@ -87,6 +88,9 @@ final class GoogleMapController
private List<Object> initialPolylines;
private List<Object> initialCircles;
private List<Map<String, ?>> initialTileOverlays;
// Null except between initialization and onMapReady.
private @Nullable String initialMapStyle;
private @Nullable String lastStyleError;
@VisibleForTesting List<Float> initialPadding;

GoogleMapController(
Expand Down Expand Up @@ -169,6 +173,10 @@ public void onMapReady(GoogleMap googleMap) {
initialPadding.get(2),
initialPadding.get(3));
}
if (initialMapStyle != null) {
updateMapStyle(initialMapStyle);
initialMapStyle = null;
}
}

// Returns the first TextureView found in the view hierarchy.
Expand Down Expand Up @@ -459,26 +467,22 @@ public void onSnapshotReady(Bitmap bitmap) {
}
case "map#setStyle":
{
boolean mapStyleSet;
if (call.arguments instanceof String) {
String mapStyle = (String) call.arguments;
if (mapStyle == null) {
mapStyleSet = googleMap.setMapStyle(null);
} else {
mapStyleSet = googleMap.setMapStyle(new MapStyleOptions(mapStyle));
}
} else {
mapStyleSet = googleMap.setMapStyle(null);
}
Object arg = call.arguments;
final String style = arg instanceof String ? (String) arg : null;
final boolean mapStyleSet = updateMapStyle(style);
ArrayList<Object> mapStyleResult = new ArrayList<>(2);
mapStyleResult.add(mapStyleSet);
if (!mapStyleSet) {
mapStyleResult.add(
"Unable to set the map style. Please check console logs for errors.");
mapStyleResult.add(lastStyleError);
}
result.success(mapStyleResult);
break;
}
case "map#getStyleError":
{
result.success(lastStyleError);
break;
}
case "tileOverlays#update":
{
List<Map<String, ?>> tileOverlaysToAdd = call.argument("tileOverlaysToAdd");
Expand Down Expand Up @@ -926,4 +930,22 @@ public void setTrafficEnabled(boolean trafficEnabled) {
public void setBuildingsEnabled(boolean buildingsEnabled) {
this.buildingsEnabled = buildingsEnabled;
}

public void setMapStyle(@Nullable String style) {
if (googleMap == null) {
initialMapStyle = style;
} else {
updateMapStyle(style);
}
}

private boolean updateMapStyle(String style) {
// Dart passes an empty string to indicate that the style should be cleared.
final MapStyleOptions mapStyleOptions =
style == null || style.isEmpty() ? null : new MapStyleOptions(style);
final boolean set = Objects.requireNonNull(googleMap).setMapStyle(mapStyleOptions);
lastStyleError =
set ? null : "Unable to set the map style. Please check console logs for errors.";
return set;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package io.flutter.plugins.googlemaps;

import androidx.annotation.Nullable;
import com.google.android.gms.maps.model.LatLngBounds;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -55,4 +56,6 @@ interface GoogleMapOptionsSink {
void setInitialCircles(Object initialCircles);

void setInitialTileOverlays(List<Map<String, ?>> initialTileOverlays);

void setMapStyle(@Nullable String style);
}
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,8 @@ void googleMapsTests() {
await controllerCompleter.future;
const String mapStyle =
'[{"elementType":"geometry","stylers":[{"color":"#242f3e"}]}]';
await controller.setMapStyle(mapStyle);
await GoogleMapsFlutterPlatform.instance
.setMapStyle(mapStyle, mapId: controller.mapId);
});

testWidgets('testSetMapStyle invalid Json String',
Expand All @@ -746,10 +747,12 @@ void googleMapsTests() {
await controllerCompleter.future;

try {
await controller.setMapStyle('invalid_value');
await GoogleMapsFlutterPlatform.instance
.setMapStyle('invalid_value', mapId: controller.mapId);
fail('expected MapStyleException');
} on MapStyleException catch (e) {
expect(e.cause, isNotNull);
expect(await controller.getStyleError(), isNotNull);
}
});

Expand All @@ -771,7 +774,8 @@ void googleMapsTests() {

final ExampleGoogleMapController controller =
await controllerCompleter.future;
await controller.setMapStyle(null);
await GoogleMapsFlutterPlatform.instance
.setMapStyle(null, mapId: controller.mapId);
});

testWidgets('testGetLatLng', (WidgetTester tester) async {
Expand Down Expand Up @@ -1211,6 +1215,58 @@ void googleMapsTests() {
await mapIdCompleter.future;
},
);

testWidgets('getStyleError reports last error', (WidgetTester tester) async {
final Key key = GlobalKey();
final Completer<ExampleGoogleMapController> controllerCompleter =
Completer<ExampleGoogleMapController>();

await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: ExampleGoogleMap(
key: key,
initialCameraPosition: _kInitialCameraPosition,
style: '[[[this is an invalid style',
onMapCreated: (ExampleGoogleMapController controller) {
controllerCompleter.complete(controller);
},
),
));

final ExampleGoogleMapController controller =
await controllerCompleter.future;
String? error = await controller.getStyleError();
for (int i = 0; i < 1000 && error == null; i++) {
await Future<void>.delayed(const Duration(milliseconds: 10));
error = await controller.getStyleError();
}
expect(error, isNotNull);
});

testWidgets('getStyleError returns null for a valid style',
(WidgetTester tester) async {
final Key key = GlobalKey();
final Completer<ExampleGoogleMapController> controllerCompleter =
Completer<ExampleGoogleMapController>();

await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: ExampleGoogleMap(
key: key,
initialCameraPosition: _kInitialCameraPosition,
// An empty array is the simplest valid style.
style: '[]',
onMapCreated: (ExampleGoogleMapController controller) {
controllerCompleter.complete(controller);
},
),
));

final ExampleGoogleMapController controller =
await controllerCompleter.future;
final String? error = await controller.getStyleError();
expect(error, isNull);
});
}

class _DebugTileProvider implements TileProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,6 @@ class ExampleGoogleMapController {
.moveCamera(cameraUpdate, mapId: mapId);
}

/// Sets the styling of the base map.
Future<void> setMapStyle(String? mapStyle) {
return GoogleMapsFlutterPlatform.instance
.setMapStyle(mapStyle, mapId: mapId);
}

/// Return [LatLngBounds] defining the region that is visible in a map.
Future<LatLngBounds> getVisibleRegion() {
return GoogleMapsFlutterPlatform.instance.getVisibleRegion(mapId: mapId);
Expand Down Expand Up @@ -195,6 +189,11 @@ class ExampleGoogleMapController {
return GoogleMapsFlutterPlatform.instance.takeSnapshot(mapId: mapId);
}

/// Returns the last style error, if any.
Future<String?> getStyleError() {
return GoogleMapsFlutterPlatform.instance.getStyleError(mapId: mapId);
}

/// Disposes of the platform resources
void dispose() {
GoogleMapsFlutterPlatform.instance.dispose(mapId: mapId);
Expand Down Expand Up @@ -245,6 +244,7 @@ class ExampleGoogleMap extends StatefulWidget {
this.onTap,
this.onLongPress,
this.cloudMapId,
this.style,
});

/// Callback method for when the map is ready to be used.
Expand Down Expand Up @@ -353,6 +353,9 @@ class ExampleGoogleMap extends StatefulWidget {
/// for more details.
final String? cloudMapId;

/// The locally configured style for the map.
final String? style;

/// Creates a [State] for this [ExampleGoogleMap].
@override
State createState() => _ExampleGoogleMapState();
Expand Down Expand Up @@ -539,5 +542,6 @@ MapConfiguration _configurationFromMapWidget(ExampleGoogleMap map) {
trafficEnabled: map.trafficEnabled,
buildingsEnabled: map.buildingsEnabled,
cloudMapId: map.cloudMapId,
style: map.style,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class MapUiBodyState extends State<MapUiBody> {
bool _myLocationButtonEnabled = true;
late ExampleGoogleMapController _controller;
bool _nightMode = false;
String _mapStyle = '';

@override
void initState() {
Expand Down Expand Up @@ -244,27 +245,16 @@ class MapUiBodyState extends State<MapUiBody> {
return rootBundle.loadString(path);
}

void _setMapStyle(String mapStyle) {
setState(() {
_nightMode = true;
_controller.setMapStyle(mapStyle);
});
}

// Should only be called if _isMapCreated is true.
Widget _nightModeToggler() {
assert(_isMapCreated);
return TextButton(
child: Text('${_nightMode ? 'disable' : 'enable'} night mode'),
onPressed: () {
if (_nightMode) {
setState(() {
_nightMode = false;
_controller.setMapStyle(null);
});
} else {
_getFileData('assets/night_mode.json').then(_setMapStyle);
}
onPressed: () async {
_nightMode = !_nightMode;
final String style =
_nightMode ? await _getFileData('assets/night_mode.json') : '';
setState(() {
_mapStyle = style;
});
},
);
}
Expand All @@ -279,6 +269,7 @@ class MapUiBodyState extends State<MapUiBody> {
cameraTargetBounds: _cameraTargetBounds,
minMaxZoomPreference: _minMaxZoomPreference,
mapType: _mapType,
style: _mapStyle,
rotateGesturesEnabled: _rotateGesturesEnabled,
scrollGesturesEnabled: _scrollGesturesEnabled,
tiltGesturesEnabled: _tiltGesturesEnabled,
Expand Down Expand Up @@ -353,5 +344,11 @@ class MapUiBodyState extends State<MapUiBody> {
_controller = controller;
_isMapCreated = true;
});
// Log any style errors to the console for debugging.
_controller.getStyleError().then((String? error) {
if (error != null) {
debugPrint(error);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ dependencies:
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
google_maps_flutter_platform_interface: ^2.4.0
google_maps_flutter_platform_interface: ^2.5.0

dev_dependencies:
build_runner: ^2.1.10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,11 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform {
return _channel(mapId).invokeMethod<Uint8List>('map#takeSnapshot');
}

@override
Future<String?> getStyleError({required int mapId}) {
return _channel(mapId).invokeMethod<String>('map#getStyleError');
}

/// Set [GoogleMapsFlutterPlatform] to use [AndroidViewSurface] to build the
/// Google Maps widget.
///
Expand Down Expand Up @@ -727,6 +732,7 @@ Map<String, Object> _jsonForMapConfiguration(MapConfiguration config) {
if (config.buildingsEnabled != null)
'buildingsEnabled': config.buildingsEnabled!,
if (config.cloudMapId != null) 'cloudMapId': config.cloudMapId!,
if (config.style != null) 'style': config.style!,
};
}

Expand Down
Loading

0 comments on commit a05b772

Please sign in to comment.