Skip to content

[google_map_flutter] Add style to widget - platform impls #6205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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