Skip to content

Commit e28f004

Browse files
[google_maps_flutter_platform_interface] Convert BitmapDescriptor to typesafe subclasses (#7699)
`BitmapDescriptor` is currently just a wrapper around JSON, with the exception of two of its subclasses. This converts all cases to typesafe structures without breaking the current API. Part of flutter/flutter#155122 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] page, which explains my responsibilities. - [x] I read and followed the [relevant style guides] and ran the auto-formatter. (Unlike the flutter/flutter repo, the flutter/packages repo does use `dart format`.) - [ ] I signed the [CLA]. - [x] The title of the PR starts with the name of the package surrounded by square brackets, e.g. `[shared_preferences]` - [x] I [linked to at least one issue that this PR fixes] in the description above. - [x] I updated `pubspec.yaml` with an appropriate new version according to the [pub versioning philosophy], or this PR is [exempt from version changes]. - [x] I updated `CHANGELOG.md` to add a description of the change, [following repository CHANGELOG style], or this PR is [exempt from CHANGELOG changes]. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md [Tree Hygiene]: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md [relevant style guides]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md#style [CLA]: https://cla.developers.google.com/ [Discord]: https://github.com/flutter/flutter/blob/master/docs/contributing/Chat.md [linked to at least one issue that this PR fixes]: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#overview [pub versioning philosophy]: https://dart.dev/tools/pub/versioning [exempt from version changes]: https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#version [following repository CHANGELOG style]: https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changelog-style [exempt from CHANGELOG changes]: https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changelog [test-exempt]: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#tests
1 parent 6ec1b43 commit e28f004

File tree

4 files changed

+246
-51
lines changed

4 files changed

+246
-51
lines changed

packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.9.5
2+
3+
* Converts `BitmapDescriptor` to typesafe structures.
4+
15
## 2.9.4
26

37
* Converts `PatternItem` to typesafe structure.

packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart

Lines changed: 151 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import 'dart:async' show Future;
66
import 'dart:typed_data' show Uint8List;
77

8-
import 'package:flutter/foundation.dart' show kIsWeb;
8+
import 'package:flutter/foundation.dart' show kIsWeb, visibleForTesting;
99
import 'package:flutter/material.dart'
1010
show
1111
AssetBundleImageKey,
@@ -29,6 +29,14 @@ enum MapBitmapScaling {
2929
none,
3030
}
3131

32+
/// Convert a string from provided JSON to a MapBitmapScaling enum.
33+
@visibleForTesting
34+
MapBitmapScaling mapBitmapScalingFromString(String mode) => switch (mode) {
35+
'auto' => MapBitmapScaling.auto,
36+
'none' => MapBitmapScaling.none,
37+
_ => throw ArgumentError('Unrecognized MapBitmapScaling $mode', 'mode'),
38+
};
39+
3240
// The default pixel ratio for custom bitmaps.
3341
const double _naturalPixelRatio = 1.0;
3442

@@ -44,14 +52,14 @@ const double _naturalPixelRatio = 1.0;
4452
/// a default marker icon.
4553
/// Use the [BitmapDescriptor.defaultMarkerWithHue] to create a
4654
/// [BitmapDescriptor] for a default marker icon with a hue value.
47-
class BitmapDescriptor {
48-
const BitmapDescriptor._(this._json);
55+
abstract class BitmapDescriptor {
56+
const BitmapDescriptor._();
4957

5058
/// The inverse of .toJson.
5159
// TODO(stuartmorgan): Remove this in the next breaking change.
5260
@Deprecated('No longer supported')
53-
BitmapDescriptor.fromJson(Object json) : _json = json {
54-
assert(_json is List<dynamic>);
61+
static BitmapDescriptor fromJson(Object json) {
62+
assert(json is List<dynamic>);
5563
final List<dynamic> jsonList = json as List<dynamic>;
5664
assert(_validTypes.contains(jsonList[0]));
5765
switch (jsonList[0]) {
@@ -61,19 +69,25 @@ class BitmapDescriptor {
6169
assert(jsonList[1] is num);
6270
final num secondElement = jsonList[1] as num;
6371
assert(0 <= secondElement && secondElement < 360);
72+
return DefaultMarker(hue: secondElement);
6473
}
74+
return const DefaultMarker();
6575
case _fromBytes:
6676
assert(jsonList.length == 2);
6777
assert(jsonList[1] != null && jsonList[1] is List<int>);
6878
assert((jsonList[1] as List<int>).isNotEmpty);
79+
return BytesBitmap(byteData: jsonList[1] as Uint8List);
6980
case _fromAsset:
7081
assert(jsonList.length <= 3);
7182
assert(jsonList[1] != null && jsonList[1] is String);
7283
assert((jsonList[1] as String).isNotEmpty);
7384
if (jsonList.length == 3) {
7485
assert(jsonList[2] != null && jsonList[2] is String);
7586
assert((jsonList[2] as String).isNotEmpty);
87+
return AssetBitmap(
88+
name: jsonList[1] as String, package: jsonList[2] as String);
7689
}
90+
return AssetBitmap(name: jsonList[1] as String);
7791
case _fromAssetImage:
7892
assert(jsonList.length <= 4);
7993
assert(jsonList[1] != null && jsonList[1] is String);
@@ -82,7 +96,15 @@ class BitmapDescriptor {
8296
if (jsonList.length == 4) {
8397
assert(jsonList[3] != null && jsonList[3] is List<dynamic>);
8498
assert((jsonList[3] as List<dynamic>).length == 2);
99+
final List<dynamic> sizeList = jsonList[3] as List<dynamic>;
100+
return AssetImageBitmap(
101+
name: jsonList[1] as String,
102+
scale: jsonList[2] as double,
103+
size: Size((sizeList[0] as num).toDouble(),
104+
(sizeList[1] as num).toDouble()));
85105
}
106+
return AssetImageBitmap(
107+
name: jsonList[1] as String, scale: jsonList[2] as double);
86108
case AssetMapBitmap.type:
87109
assert(jsonList.length == 2);
88110
assert(jsonList[1] != null && jsonList[1] is Map<String, dynamic>);
@@ -96,6 +118,16 @@ class BitmapDescriptor {
96118
assert(jsonMap['imagePixelRatio'] is double);
97119
assert(!jsonMap.containsKey('width') || jsonMap['width'] is double);
98120
assert(!jsonMap.containsKey('height') || jsonMap['height'] is double);
121+
final double? width =
122+
jsonMap.containsKey('width') ? jsonMap['width'] as double : null;
123+
final double? height =
124+
jsonMap.containsKey('height') ? jsonMap['height'] as double : null;
125+
return AssetMapBitmap(jsonMap['assetName'] as String,
126+
bitmapScaling:
127+
mapBitmapScalingFromString(jsonMap['bitmapScaling'] as String),
128+
imagePixelRatio: jsonMap['imagePixelRatio'] as double,
129+
width: width,
130+
height: height);
99131
case BytesMapBitmap.type:
100132
assert(jsonList.length == 2);
101133
assert(jsonList[1] != null && jsonList[1] is Map<String, dynamic>);
@@ -109,9 +141,20 @@ class BitmapDescriptor {
109141
assert(jsonMap['imagePixelRatio'] is double);
110142
assert(!jsonMap.containsKey('width') || jsonMap['width'] is double);
111143
assert(!jsonMap.containsKey('height') || jsonMap['height'] is double);
144+
final double? width =
145+
jsonMap.containsKey('width') ? jsonMap['width'] as double : null;
146+
final double? height =
147+
jsonMap.containsKey('height') ? jsonMap['height'] as double : null;
148+
return BytesMapBitmap(jsonMap['byteData'] as Uint8List,
149+
bitmapScaling:
150+
mapBitmapScalingFromString(jsonMap['bitmapScaling'] as String),
151+
width: width,
152+
height: height,
153+
imagePixelRatio: jsonMap['imagePixelRatio'] as double);
112154
default:
113155
break;
114156
}
157+
throw ArgumentError('Unrecognized BitmapDescriptor type ${jsonList[0]}');
115158
}
116159

117160
static const String _defaultMarker = 'defaultMarker';
@@ -160,15 +203,14 @@ class BitmapDescriptor {
160203
static const double hueRose = 330.0;
161204

162205
/// Creates a BitmapDescriptor that refers to the default marker image.
163-
static const BitmapDescriptor defaultMarker =
164-
BitmapDescriptor._(<Object>[_defaultMarker]);
206+
static const BitmapDescriptor defaultMarker = DefaultMarker();
165207

166208
/// Creates a BitmapDescriptor that refers to a colorization of the default
167209
/// marker image. For convenience, there is a predefined set of hue values.
168210
/// See e.g. [hueYellow].
169211
static BitmapDescriptor defaultMarkerWithHue(double hue) {
170212
assert(0.0 <= hue && hue < 360.0);
171-
return BitmapDescriptor._(<Object>[_defaultMarker, hue]);
213+
return DefaultMarker(hue: hue);
172214
}
173215

174216
/// Creates a [BitmapDescriptor] from an asset image.
@@ -189,27 +231,17 @@ class BitmapDescriptor {
189231
}) async {
190232
final double? devicePixelRatio = configuration.devicePixelRatio;
191233
if (!mipmaps && devicePixelRatio != null) {
192-
return BitmapDescriptor._(<Object>[
193-
_fromAssetImage,
194-
assetName,
195-
devicePixelRatio,
196-
]);
234+
return AssetImageBitmap(name: assetName, scale: devicePixelRatio);
197235
}
198236
final AssetImage assetImage =
199237
AssetImage(assetName, package: package, bundle: bundle);
200238
final AssetBundleImageKey assetBundleImageKey =
201239
await assetImage.obtainKey(configuration);
202-
final Size? size = configuration.size;
203-
return BitmapDescriptor._(<Object>[
204-
_fromAssetImage,
205-
assetBundleImageKey.name,
206-
assetBundleImageKey.scale,
207-
if (kIsWeb && size != null)
208-
<Object>[
209-
size.width,
210-
size.height,
211-
],
212-
]);
240+
final Size? size = kIsWeb ? configuration.size : null;
241+
return AssetImageBitmap(
242+
name: assetBundleImageKey.name,
243+
scale: assetBundleImageKey.scale,
244+
size: size);
213245
}
214246

215247
/// Creates a BitmapDescriptor using an array of bytes that must be encoded
@@ -222,15 +254,7 @@ class BitmapDescriptor {
222254
static BitmapDescriptor fromBytes(Uint8List byteData, {Size? size}) {
223255
assert(byteData.isNotEmpty,
224256
'Cannot create BitmapDescriptor with empty byteData');
225-
return BitmapDescriptor._(<Object>[
226-
_fromBytes,
227-
byteData,
228-
if (kIsWeb && size != null)
229-
<Object>[
230-
size.width,
231-
size.height,
232-
]
233-
]);
257+
return BytesBitmap(byteData: byteData, size: size);
234258
}
235259

236260
/// Creates a [BitmapDescriptor] from an asset using [AssetMapBitmap].
@@ -306,10 +330,100 @@ class BitmapDescriptor {
306330
);
307331
}
308332

309-
final Object _json;
310-
311333
/// Convert the object to a Json format.
312-
Object toJson() => _json;
334+
Object toJson();
335+
}
336+
337+
/// A BitmapDescriptor using the default marker.
338+
class DefaultMarker extends BitmapDescriptor {
339+
/// Provide an optional [hue] for the default marker.
340+
const DefaultMarker({this.hue}) : super._();
341+
342+
/// Optional hue of the colorization of the default marker.
343+
final num? hue;
344+
345+
@override
346+
Object toJson() => (hue == null)
347+
? const <Object>[BitmapDescriptor._defaultMarker]
348+
: <Object>[BitmapDescriptor._defaultMarker, hue!];
349+
}
350+
351+
/// A BitmapDescriptor using an array of bytes that must be encoded
352+
/// as PNG.
353+
@Deprecated('Use BytesMapBitmap instead')
354+
class BytesBitmap extends BitmapDescriptor {
355+
/// On the web, the [size] parameter represents the *physical size* of the
356+
/// bitmap, regardless of the actual resolution of the encoded PNG.
357+
/// This helps the browser to render High-DPI images at the correct size.
358+
/// `size` is not required (and ignored, if passed) in other platforms.
359+
@Deprecated('Use BytesMapBitmap instead')
360+
const BytesBitmap({required Uint8List byteData, Size? size})
361+
: this._(byteData, kIsWeb ? size : null);
362+
363+
@Deprecated('Use BytesMapBitmap instead')
364+
const BytesBitmap._(this.byteData, this.size) : super._();
365+
366+
/// Array of bytes encoding a PNG.
367+
final Uint8List byteData;
368+
369+
/// On web, the physical size of the bitmap. Null on all other platforms.
370+
final Size? size;
371+
372+
@override
373+
Object toJson() => <Object>[
374+
BitmapDescriptor._fromBytes,
375+
byteData,
376+
if (size != null) <Object>[size!.width, size!.height]
377+
];
378+
}
379+
380+
/// A bitmap specified by a name and optional package.
381+
class AssetBitmap extends BitmapDescriptor {
382+
/// Provides an asset name with [name] and optionally a [package].
383+
const AssetBitmap({required this.name, this.package}) : super._();
384+
385+
/// Name of the asset backing the bitmap.
386+
final String name;
387+
388+
/// Optional package of the asset.
389+
final String? package;
390+
391+
@override
392+
Object toJson() => <Object>[
393+
BitmapDescriptor._fromAsset,
394+
name,
395+
if (package != null) package!
396+
];
397+
}
398+
399+
/// A [BitmapDescriptor] from an asset image.
400+
@Deprecated('Use AssetMapBitmap instead')
401+
class AssetImageBitmap extends BitmapDescriptor {
402+
/// Creates a [BitmapDescriptor] from an asset image with specified [name] and [scale], and an optional [size].
403+
/// Asset images in flutter are stored per:
404+
/// https://flutter.dev/to/resolution-aware-images
405+
/// This method takes into consideration various asset resolutions
406+
/// and scales the images to the right resolution depending on the dpi.
407+
@Deprecated('Use AssetMapBitmap instead')
408+
const AssetImageBitmap({required this.name, required this.scale, this.size})
409+
: super._();
410+
411+
/// Name of the image asset.
412+
final String name;
413+
414+
/// Scaling factor for the asset image.
415+
final double scale;
416+
417+
/// Size of the image if using mipmaps.
418+
final Size? size;
419+
420+
@override
421+
Object toJson() => <Object>[
422+
BitmapDescriptor._fromAssetImage,
423+
name,
424+
scale,
425+
if (size != null) <Object>[size!.width, size!.height]
426+
];
313427
}
314428

315429
/// Represents a [BitmapDescriptor] base class for map bitmaps.
@@ -344,7 +458,7 @@ abstract class MapBitmap extends BitmapDescriptor {
344458
required this.imagePixelRatio,
345459
this.width,
346460
this.height,
347-
}) : super._(const <Object>[]);
461+
}) : super._();
348462

349463
/// The scaling method of the bitmap.
350464
final MapBitmapScaling bitmapScaling;

packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/google_maps_f
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22
55
# NOTE: We strongly prefer non-breaking changes, even at the expense of a
66
# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
7-
version: 2.9.4
7+
version: 2.9.5
88

99
environment:
1010
sdk: ^3.3.0

0 commit comments

Comments
 (0)