Skip to content

Commit ff56a9b

Browse files
JSUYAseungsoo47
andauthored
[google_maps_flutter] Update google_maps_flutter to 2.16.0 (#1019)
Co-authored-by: Seungsoo Lee <seungsoo47.lee@samsung.com>
1 parent 8e2f00d commit ff56a9b

13 files changed

Lines changed: 677 additions & 9 deletions

packages/google_maps_flutter/CHANGELOG.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
## NEXT
2-
3-
* Update code format.
1+
## 0.1.14
2+
3+
* Update google_maps_flutter to 2.16.0.
4+
* Update google_maps_flutter_platform_interface to 2.15.0.
5+
* Add support for ground overlays (bounds-based) using
6+
`google.maps.GroundOverlay`.
7+
* Forward the `colorScheme` map option to `google.maps.ColorScheme`.
8+
* * Update code format.
49

510
## 0.1.13
611

packages/google_maps_flutter/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ This package is not an _endorsed_ implementation of `google_maps_flutter`. There
2020

2121
```yaml
2222
dependencies:
23-
google_maps_flutter: ^2.10.0
24-
google_maps_flutter_tizen: ^0.1.13
23+
google_maps_flutter: ^2.16.0
24+
google_maps_flutter_tizen: ^0.1.14
2525
```
2626
2727
For detailed usage, see https://pub.dev/packages/google_maps_flutter#sample-usage.
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// ignore_for_file: public_member_api_docs
6+
7+
import 'package:flutter/material.dart';
8+
import 'package:google_maps_flutter/google_maps_flutter.dart';
9+
10+
import 'page.dart';
11+
12+
class GroundOverlayPage extends GoogleMapExampleAppPage {
13+
const GroundOverlayPage({Key? key})
14+
: super(const Icon(Icons.map), 'Ground overlay', key: key);
15+
16+
@override
17+
Widget build(BuildContext context) {
18+
return const GroundOverlayBody();
19+
}
20+
}
21+
22+
class GroundOverlayBody extends StatefulWidget {
23+
const GroundOverlayBody({super.key});
24+
25+
@override
26+
State<StatefulWidget> createState() => GroundOverlayBodyState();
27+
}
28+
29+
class GroundOverlayBodyState extends State<GroundOverlayBody> {
30+
GoogleMapController? controller;
31+
GroundOverlay? _groundOverlay;
32+
int _groundOverlayIndex = 0;
33+
34+
static const LatLng _mapCenter = LatLng(37.422026, -122.085329);
35+
36+
final LatLngBounds _bounds1 = LatLngBounds(
37+
southwest: const LatLng(37.42, -122.09),
38+
northeast: const LatLng(37.423, -122.084),
39+
);
40+
final LatLngBounds _bounds2 = LatLngBounds(
41+
southwest: const LatLng(37.421, -122.091),
42+
northeast: const LatLng(37.424, -122.08),
43+
);
44+
45+
late LatLngBounds _currentBounds = _bounds1;
46+
47+
// ignore: use_setters_to_change_properties
48+
void _onMapCreated(GoogleMapController controller) {
49+
this.controller = controller;
50+
}
51+
52+
Future<void> _addGroundOverlay() async {
53+
final AssetMapBitmap image = await AssetMapBitmap.create(
54+
createLocalImageConfiguration(context),
55+
'assets/red_square.png',
56+
bitmapScaling: MapBitmapScaling.none,
57+
);
58+
59+
_groundOverlayIndex += 1;
60+
61+
final GroundOverlay overlay = GroundOverlay.fromBounds(
62+
groundOverlayId: GroundOverlayId('ground_overlay_$_groundOverlayIndex'),
63+
image: image,
64+
bounds: _currentBounds,
65+
onTap: _toggleBounds,
66+
);
67+
68+
setState(() {
69+
_groundOverlay = overlay;
70+
});
71+
}
72+
73+
void _removeGroundOverlay() {
74+
setState(() {
75+
_groundOverlay = null;
76+
});
77+
}
78+
79+
void _toggleTransparency() {
80+
if (_groundOverlay == null) {
81+
return;
82+
}
83+
setState(() {
84+
final double transparency =
85+
_groundOverlay!.transparency == 0.0 ? 0.5 : 0.0;
86+
_groundOverlay =
87+
_groundOverlay!.copyWith(transparencyParam: transparency);
88+
});
89+
}
90+
91+
void _toggleVisible() {
92+
if (_groundOverlay == null) {
93+
return;
94+
}
95+
setState(() {
96+
_groundOverlay =
97+
_groundOverlay!.copyWith(visibleParam: !_groundOverlay!.visible);
98+
});
99+
}
100+
101+
Future<void> _toggleBounds() async {
102+
setState(() {
103+
_currentBounds = _currentBounds == _bounds1 ? _bounds2 : _bounds1;
104+
});
105+
await _addGroundOverlay();
106+
}
107+
108+
@override
109+
Widget build(BuildContext context) {
110+
final Set<GroundOverlay> overlays = <GroundOverlay>{
111+
if (_groundOverlay != null) _groundOverlay!,
112+
};
113+
return Column(
114+
mainAxisSize: MainAxisSize.min,
115+
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
116+
crossAxisAlignment: CrossAxisAlignment.stretch,
117+
children: <Widget>[
118+
Expanded(
119+
child: GoogleMap(
120+
initialCameraPosition: const CameraPosition(
121+
target: _mapCenter,
122+
zoom: 14.0,
123+
),
124+
groundOverlays: overlays,
125+
onMapCreated: _onMapCreated,
126+
),
127+
),
128+
Row(
129+
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
130+
children: <Widget>[
131+
TextButton(
132+
onPressed: _groundOverlay == null ? _addGroundOverlay : null,
133+
child: const Text('Add'),
134+
),
135+
TextButton(
136+
onPressed: _groundOverlay != null ? _removeGroundOverlay : null,
137+
child: const Text('Remove'),
138+
),
139+
TextButton(
140+
onPressed: _groundOverlay == null ? null : _toggleTransparency,
141+
child: const Text('Toggle transparency'),
142+
),
143+
TextButton(
144+
onPressed: _groundOverlay == null ? null : _toggleVisible,
145+
child: const Text('Toggle visible'),
146+
),
147+
TextButton(
148+
onPressed: _groundOverlay == null ? null : _toggleBounds,
149+
child: const Text('Change bounds'),
150+
),
151+
],
152+
),
153+
],
154+
);
155+
}
156+
}

packages/google_maps_flutter/example/lib/main.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
66

77
import 'animate_camera.dart';
88
import 'clustering.dart';
9+
import 'ground_overlay.dart';
910
import 'heatmap.dart';
1011
import 'lite_mode.dart';
1112
import 'map_click.dart';
@@ -40,6 +41,7 @@ final List<GoogleMapExampleAppPage> _allPages = <GoogleMapExampleAppPage>[
4041
const SnapshotPage(),
4142
const LiteModePage(),
4243
const TileOverlayPage(),
44+
const GroundOverlayPage(),
4345
const ClusteringPage(),
4446
const MapIdPage(),
4547
const HeatmapPage(),

packages/google_maps_flutter/example/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ environment:
99
dependencies:
1010
flutter:
1111
sdk: flutter
12-
google_maps_flutter: ^2.10.0
13-
google_maps_flutter_platform_interface: ^2.10.0
12+
google_maps_flutter: ^2.16.0
13+
google_maps_flutter_platform_interface: ^2.15.0
1414
google_maps_flutter_tizen:
1515
path: ../
1616

packages/google_maps_flutter/lib/google_maps_flutter_tizen.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ part 'src/circles.dart';
2626
part 'src/convert.dart';
2727
part 'src/google_maps_controller.dart';
2828
part 'src/google_maps_flutter_tizen.dart';
29+
part 'src/ground_overlay.dart';
30+
part 'src/ground_overlays.dart';
2931
part 'src/marker.dart';
3032
part 'src/markers.dart';
3133
part 'src/marker_clustering.dart';

packages/google_maps_flutter/lib/src/convert.dart

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,62 @@ Map<int, String> _mapTypeToMapTypeId = <int, String>{
1616
4: 'hybrid',
1717
};
1818

19+
// Builds the raw map options map from a [MapConfiguration].
20+
//
21+
// This mirrors the platform interface's `jsonForMapConfiguration` helper
22+
// (which is not exported), plus [MapConfiguration.colorScheme].
23+
//
24+
// Intentionally omitted:
25+
// * `cloudMapId` — deprecated alias for `mapId`, which is already serialized.
26+
// * `markerType` — only relevant to advanced markers, which are not supported
27+
// on Tizen.
28+
Map<String, dynamic> _mapOptionsFromConfiguration(MapConfiguration config) {
29+
final EdgeInsets? padding = config.padding;
30+
return <String, dynamic>{
31+
if (config.compassEnabled != null) 'compassEnabled': config.compassEnabled,
32+
if (config.mapToolbarEnabled != null)
33+
'mapToolbarEnabled': config.mapToolbarEnabled,
34+
if (config.cameraTargetBounds != null)
35+
'cameraTargetBounds': config.cameraTargetBounds!.toJson(),
36+
if (config.mapType != null) 'mapType': config.mapType!.index,
37+
if (config.minMaxZoomPreference != null)
38+
'minMaxZoomPreference': config.minMaxZoomPreference!.toJson(),
39+
if (config.rotateGesturesEnabled != null)
40+
'rotateGesturesEnabled': config.rotateGesturesEnabled,
41+
if (config.scrollGesturesEnabled != null)
42+
'scrollGesturesEnabled': config.scrollGesturesEnabled,
43+
if (config.tiltGesturesEnabled != null)
44+
'tiltGesturesEnabled': config.tiltGesturesEnabled,
45+
if (config.zoomControlsEnabled != null)
46+
'zoomControlsEnabled': config.zoomControlsEnabled,
47+
if (config.zoomGesturesEnabled != null)
48+
'zoomGesturesEnabled': config.zoomGesturesEnabled,
49+
if (config.liteModeEnabled != null)
50+
'liteModeEnabled': config.liteModeEnabled,
51+
if (config.trackCameraPosition != null)
52+
'trackCameraPosition': config.trackCameraPosition,
53+
if (config.myLocationEnabled != null)
54+
'myLocationEnabled': config.myLocationEnabled,
55+
if (config.myLocationButtonEnabled != null)
56+
'myLocationButtonEnabled': config.myLocationButtonEnabled,
57+
if (padding != null)
58+
'padding': <double>[
59+
padding.top,
60+
padding.left,
61+
padding.bottom,
62+
padding.right,
63+
],
64+
if (config.indoorViewEnabled != null)
65+
'indoorEnabled': config.indoorViewEnabled,
66+
if (config.trafficEnabled != null) 'trafficEnabled': config.trafficEnabled,
67+
if (config.buildingsEnabled != null)
68+
'buildingsEnabled': config.buildingsEnabled,
69+
if (config.mapId != null) 'mapId': config.mapId,
70+
if (config.style != null) 'style': config.style,
71+
if (config.colorScheme != null) 'colorScheme': config.colorScheme!.index,
72+
};
73+
}
74+
1975
String? _getCameraBounds(dynamic option) {
2076
if (option is! List<Object?> || option.first == null) {
2177
return null;
@@ -90,9 +146,33 @@ String _rawOptionsToString(Map<String, dynamic> rawOptions) {
90146
options += ", gestureHandling: 'auto'";
91147
}
92148

149+
final String? colorScheme = _colorSchemeToJs(rawOptions['colorScheme']);
150+
if (colorScheme != null) {
151+
options += ', colorScheme: $colorScheme';
152+
}
153+
93154
return options;
94155
}
95156

157+
// Maps a serialized MapColorScheme value from the platform interface to the
158+
// JavaScript google.maps.ColorScheme enum.
159+
String? _colorSchemeToJs(Object? value) {
160+
if (value is! int) {
161+
return null;
162+
}
163+
// The platform interface serializes MapColorScheme as its enum index:
164+
// 0 = light, 1 = dark, 2 = followSystem.
165+
switch (value) {
166+
case 0:
167+
return 'google.maps.ColorScheme.LIGHT';
168+
case 1:
169+
return 'google.maps.ColorScheme.DARK';
170+
case 2:
171+
return 'google.maps.ColorScheme.FOLLOW_SYSTEM';
172+
}
173+
return null;
174+
}
175+
96176
String _applyInitialPosition(CameraPosition initialPosition, String options) {
97177
options += ', zoom: ${initialPosition.zoom}';
98178
options +=
@@ -400,3 +480,56 @@ util.GCircleOptions _circleOptionsFromCircle(Circle circle) {
400480
..visible = circle.visible
401481
..zIndex = circle.zIndex;
402482
}
483+
484+
util.GGroundOverlayOptions? _groundOverlayOptionsFromGroundOverlay(
485+
GroundOverlay groundOverlay,
486+
) {
487+
// The JS Maps GroundOverlay only supports bounds-based positioning. Skip
488+
// position-only overlays — the platform interface allows the field but the
489+
// JS API has no equivalent.
490+
final LatLngBounds? bounds = groundOverlay.bounds;
491+
if (bounds == null) {
492+
debugPrint(
493+
'GroundOverlay ${groundOverlay.groundOverlayId.value} skipped: '
494+
'the Google Maps JavaScript API only supports bounds-based '
495+
'ground overlays.',
496+
);
497+
return null;
498+
}
499+
final String? imageUrl = _imageUrlFromMapBitmap(groundOverlay.image);
500+
if (imageUrl == null) {
501+
debugPrint(
502+
'GroundOverlay ${groundOverlay.groundOverlayId.value} skipped: '
503+
'unsupported image source.',
504+
);
505+
return null;
506+
}
507+
return util.GGroundOverlayOptions()
508+
..url = "'$imageUrl'"
509+
..bounds = '{south:${bounds.southwest.latitude},'
510+
' west:${bounds.southwest.longitude},'
511+
' north:${bounds.northeast.latitude},'
512+
' east:${bounds.northeast.longitude}}'
513+
..clickable = groundOverlay.clickable
514+
..opacity = 1.0 - groundOverlay.transparency
515+
..visible = groundOverlay.visible;
516+
}
517+
518+
String? _imageUrlFromMapBitmap(MapBitmap bitmap) {
519+
final List<Object?> iconConfig = bitmap.toJson() as List<Object?>;
520+
if (iconConfig.isEmpty) {
521+
return null;
522+
}
523+
if (iconConfig[0] == 'asset' && iconConfig.length >= 2) {
524+
final Map<String, Object?> assetConfig =
525+
iconConfig[1]! as Map<String, Object?>;
526+
return '../${assetConfig['assetName']}';
527+
}
528+
if (iconConfig[0] == 'bytes' && iconConfig.length >= 2) {
529+
final Map<String, Object?> assetConfig =
530+
iconConfig[1]! as Map<String, Object?>;
531+
final List<int> bytes = assetConfig['byteData']! as List<int>;
532+
return 'data:image/png;base64,${base64Encode(bytes)}';
533+
}
534+
return null;
535+
}

0 commit comments

Comments
 (0)