Skip to content

Commit 1fc2e5b

Browse files
committed
Finalize custom marker size functionality and tests
1 parent 0432554 commit 1fc2e5b

File tree

11 files changed

+46
-109
lines changed

11 files changed

+46
-109
lines changed

packages/google_maps_flutter/google_maps_flutter/example/lib/custom_marker_icon.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
// ignore_for_file: public_member_api_docs
6-
75
import 'dart:typed_data';
86
import 'dart:ui' as ui;
97

108
import 'package:flutter/material.dart';
119

10+
/// Returns a generated png image in [ByteData] format with the requested size.
1211
Future<ByteData> createCustomMarkerIconImage({required Size size}) async {
1312
final ui.PictureRecorder recorder = ui.PictureRecorder();
1413
final Canvas canvas = Canvas(recorder);

packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_tests.dart

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -960,23 +960,6 @@ void googleMapsTests() {
960960
expect(iwVisibleStatus, false);
961961
});
962962

963-
testWidgets('createFromAsset', (WidgetTester tester) async {
964-
const double pixelRatio = 2;
965-
const ImageConfiguration imageConfiguration =
966-
ImageConfiguration(devicePixelRatio: pixelRatio);
967-
final BitmapDescriptor mip = await BitmapDescriptor.createFromAsset(
968-
imageConfiguration,
969-
'red_square.png',
970-
);
971-
final BitmapDescriptor scaled = await BitmapDescriptor.createFromAsset(
972-
imageConfiguration,
973-
'red_square.png',
974-
mipmaps: false,
975-
);
976-
expect((mip.toJson() as List<dynamic>)[3], 1.0);
977-
expect((scaled.toJson() as List<dynamic>)[3], 2);
978-
});
979-
980963
testWidgets('testTakeSnapshot', (WidgetTester tester) async {
981964
final Completer<ExampleGoogleMapController> controllerCompleter =
982965
Completer<ExampleGoogleMapController>();

packages/google_maps_flutter/google_maps_flutter_android/example/lib/custom_marker_icon.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
// ignore_for_file: public_member_api_docs
6-
75
import 'dart:typed_data';
86
import 'dart:ui' as ui;
97

108
import 'package:flutter/material.dart';
119

10+
/// Returns a generated png image in [ByteData] format with the requested size.
1211
Future<ByteData> createCustomMarkerIconImage({required Size size}) async {
1312
final ui.PictureRecorder recorder = ui.PictureRecorder();
1413
final Canvas canvas = Canvas(recorder);

packages/google_maps_flutter/google_maps_flutter_ios/example/ios11/integration_test/google_maps_test.dart

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -805,23 +805,6 @@ void main() {
805805
expect(iwVisibleStatus, false);
806806
});
807807

808-
testWidgets('createFromAsset', (WidgetTester tester) async {
809-
const double pixelRatio = 2;
810-
const ImageConfiguration imageConfiguration =
811-
ImageConfiguration(devicePixelRatio: pixelRatio);
812-
final BitmapDescriptor mip = await BitmapDescriptor.createFromAsset(
813-
imageConfiguration,
814-
'red_square.png',
815-
);
816-
final BitmapDescriptor scaled = await BitmapDescriptor.createFromAsset(
817-
imageConfiguration,
818-
'red_square.png',
819-
mipmaps: false,
820-
);
821-
expect((mip.toJson() as List<dynamic>)[3], 1.0);
822-
expect((scaled.toJson() as List<dynamic>)[3], 2);
823-
});
824-
825808
testWidgets('testTakeSnapshot', (WidgetTester tester) async {
826809
final Completer<ExampleGoogleMapController> controllerCompleter =
827810
Completer<ExampleGoogleMapController>();

packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/custom_marker_icon.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
// ignore_for_file: public_member_api_docs
6-
75
import 'dart:typed_data';
86
import 'dart:ui' as ui;
97

108
import 'package:flutter/material.dart';
119

10+
/// Returns a generated png image in [ByteData] format with the requested size.
1211
Future<ByteData> createCustomMarkerIconImage({required Size size}) async {
1312
final ui.PictureRecorder recorder = ui.PictureRecorder();
1413
final Canvas canvas = Canvas(recorder);

packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ - (UIImage *)extractIconFromData:(NSArray *)iconData
198198
image = [self scaleImage:image scale:screenScale];
199199
// Create resized image.
200200
CGSize size = [FLTGoogleMapJSONConversions sizeFromArray:iconData[4] scale:screenScale];
201-
image = [self scaleImage:image size:size];
201+
image = [self scaleImage:image toSize:size];
202202
}
203203
}
204204

@@ -223,7 +223,7 @@ - (UIImage *)extractIconFromData:(NSArray *)iconData
223223
image = [UIImage imageWithData:[byteData data] scale:screenScale];
224224
// Create resized image.
225225
CGSize size = [FLTGoogleMapJSONConversions sizeFromArray:iconData[4] scale:screenScale];
226-
image = [self scaleImage:image size:size];
226+
image = [self scaleImage:image toSize:size];
227227
}
228228
} else {
229229
// No scaling, load image from bytes without scale parameter.
@@ -263,7 +263,16 @@ - (UIImage *)scaleImage:(UIImage *)image scale:(CGFloat)scale {
263263
return image;
264264
}
265265

266-
- (UIImage *)scaleImage:(UIImage *)image size:(CGSize)size {
266+
/**
267+
Scales an input UIImage to a specified size. If the aspect ratio of the input image
268+
is similar to the target size, the image's scale property is updated rather than resizing the
269+
image. If the aspect ratios significantly differ, the method redraws the image at the target size.
270+
271+
@param image The UIImage to scale.
272+
@param size The target CGSize to scale the image to.
273+
@return UIImage Returns the scaled UIImage.
274+
*/
275+
- (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)size {
267276
if (fabs(((int)image.size.width * image.scale) - size.width) > 0 ||
268277
fabs(((int)image.size.height * image.scale) - size.height) > 0) {
269278
if (fabs(image.size.width / image.size.height - size.width / size.height) < 1e-2) {

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ import 'package:flutter/widgets.dart'
1515
Size,
1616
WidgetsBinding;
1717

18-
/// Type of bitmap scaling options to use on BitmapDescriptor creation.
18+
/// Type of bitmap scaling to use on BitmapDescriptor creation.
1919
enum BitmapScaling {
2020
/// Automatically scale image with devices pixel ratio or to given size,
2121
/// to keep marker sizes same between platforms and devices.
2222
auto,
2323

2424
/// Render marker to the map as without scaling, this can be used if the image
25-
/// is already pre-scaled, or to increase performance with large marker amounts.
25+
/// is already pre-scaled, or to increase performance with a large numbers of markers.
2626
noScaling,
2727
}
2828

@@ -237,6 +237,10 @@ class BitmapDescriptor {
237237
/// Set `mipmaps` to false to load the exact dpi version of the image, `mipmap` is true by default.
238238
/// If `mipmaps` is set to false, optional `imagePixelRatio` can be given to
239239
/// override `devicePixelRatio` value from `ImageConfiguration`.
240+
///
241+
/// Note: Image scaling with [ImageConfiguration.size] parameter does not maintain the aspect ratio.
242+
/// If a proportional resize is required, ensure to provide a [ImageConfiguration.size] with the
243+
/// correct aspect ratio.
240244
static Future<BitmapDescriptor> createFromAsset(
241245
ImageConfiguration configuration,
242246
String assetName, {
@@ -246,6 +250,9 @@ class BitmapDescriptor {
246250
double? imagePixelRatio,
247251
BitmapScaling bitmapScaling = BitmapScaling.auto,
248252
}) async {
253+
assert(!mipmaps || imagePixelRatio == null,
254+
'If `mipmaps` is true, then `imagePixelRatio` must be null.');
255+
249256
final double devicePixelRatio =
250257
WidgetsBinding.instance.platformDispatcher.views.first.devicePixelRatio;
251258
final double? targetImagePixelRatio =
@@ -256,7 +263,7 @@ class BitmapDescriptor {
256263
return BitmapDescriptor._(<Object>[
257264
_asset,
258265
assetName,
259-
_getBitMapScalingString(bitmapScaling),
266+
_getBitmapScalingString(bitmapScaling),
260267
targetImagePixelRatio ?? devicePixelRatio,
261268
if (size != null)
262269
<Object>[
@@ -274,7 +281,7 @@ class BitmapDescriptor {
274281
return BitmapDescriptor._(<Object>[
275282
_asset,
276283
assetBundleImageKey.name,
277-
_getBitMapScalingString(bitmapScaling),
284+
_getBitmapScalingString(bitmapScaling),
278285
assetBundleImageKey.scale,
279286
if (size != null)
280287
<Object>[
@@ -286,19 +293,24 @@ class BitmapDescriptor {
286293

287294
/// Creates a BitmapDescriptor using an array of bytes that must be encoded
288295
/// as PNG.
296+
///
289297
/// The optional [size] parameter represents the *logical size* of the
290298
/// bitmap, regardless of the actual resolution of the encoded PNG.
291299
/// This helps the platform to render High-DPI images at the correct size.
292-
/// [ImagePixelRatio] value can be use to scale the image to
300+
/// [imagePixelRatio] value can be use to scale the image to
293301
/// proper size across platforms.
302+
///
303+
/// Note: Image scaling with [size] parameter does not maintain the aspect ratio.
304+
/// If a proportional resize is required, ensure to provide a [size] with the
305+
/// correct aspect ratio.
294306
static BitmapDescriptor createFromBytes(
295307
Uint8List byteData, {
296308
BitmapScaling bitmapScaling = BitmapScaling.auto,
297309
double? imagePixelRatio,
298310
Size? size,
299311
}) {
300312
assert(byteData.isNotEmpty,
301-
'Cannot create BitmapDescriptor with empty byteData');
313+
'Cannot create BitmapDescriptor with empty byteData.');
302314
assert(bitmapScaling != BitmapScaling.noScaling || imagePixelRatio == null,
303315
'If bitmapScaling is set to BitmapScaling.noScaling, scale parameter cannot be used.');
304316
assert(bitmapScaling != BitmapScaling.noScaling || size == null,
@@ -307,7 +319,7 @@ class BitmapDescriptor {
307319
return BitmapDescriptor._(<Object>[
308320
_bytes,
309321
byteData,
310-
_getBitMapScalingString(bitmapScaling),
322+
_getBitmapScalingString(bitmapScaling),
311323
imagePixelRatio ?? 1.0,
312324
if (size != null)
313325
<Object>[
@@ -317,7 +329,7 @@ class BitmapDescriptor {
317329
]);
318330
}
319331

320-
static String _getBitMapScalingString(BitmapScaling bitmapScaling) {
332+
static String _getBitmapScalingString(BitmapScaling bitmapScaling) {
321333
switch (bitmapScaling) {
322334
case BitmapScaling.auto:
323335
return bitmapAutoScaling;

packages/google_maps_flutter/google_maps_flutter_web/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ Only Android supports "[Lite Mode](https://developers.google.com/maps/documentat
7575

7676
Google Maps for web uses `HtmlElementView` to render maps. When a `GoogleMap` is stacked below other widgets, [`package:pointer_interceptor`](https://www.pub.dev/packages/pointer_interceptor) must be used to capture mouse events on the Flutter overlays. See issue [#73830](https://github.com/flutter/flutter/issues/73830).
7777

78-
## Custom marker icons
78+
### Custom marker icons
7979

80-
In order to achieve the best possible performance when using custom marker images on the web platform, the `size` parameter should be used to scale the marker when using `BitmapDescriptor.createFromAsset` or `BitmapDescriptor.createFromBytes` methods.
80+
Images are not automatically scaled according to the imageAspectRatio value of the BitmapDescriptor in web, so the `size` parameter must be used to scale the marker when using the `BitmapDescriptor.createFromAsset` or `BitmapDescriptor.createFromBytes` methods.
8181

8282
<?code-excerpt "readme_excerpts.dart (CreateFromAsset)"?>
8383
```dart

packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ class MyApp extends StatefulWidget {
2020
class _MyAppState extends State<MyApp> {
2121
@override
2222
Widget build(BuildContext context) {
23-
return const Directionality(
24-
textDirection: TextDirection.ltr,
25-
child: Text('Testing... Look at the console output for results!'));
23+
return const Text('Testing... Look at the console output for results!');
2624
}
2725
}

packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart

Lines changed: 8 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -277,41 +277,16 @@ Future<gmaps.Icon?> _gmIconFromBitmapDescriptor(
277277
}
278278
} else if (iconConfig[0] == 'asset') {
279279
assert(iconConfig.length >= 3);
280-
// iconConfig[2] contains the DPIs of the screen, but that information is
281-
// already encoded in the iconConfig[1]
282-
283280
final String assetUrl =
284281
ui.webOnlyAssetManager.getAssetUrl(iconConfig[1]! as String);
285282
icon = gmaps.Icon()..url = assetUrl;
286283

287284
switch (iconConfig[2]! as String) {
288285
case BitmapDescriptor.bitmapAutoScaling:
289-
if (iconConfig.length == 4) {
290-
final double scale = iconConfig[3]! as double;
291-
// Google Maps Web SDK does not support the scaling of the marker with anything other than
292-
// the size parameter, therefore the width and height of the image must be read from the image.
293-
// To avoid this, it is best to provide the image size instead of scale when using the web platform.
294-
295-
final ByteData bytedata =
296-
await ui.webOnlyAssetManager.load(iconConfig[1]! as String);
297-
final Uint8List bytes = bytedata.buffer.asUint8List();
298-
final img.Decoder? decoder = img.findDecoderForData(bytes);
299-
final img.DecodeInfo? decodeInfo = decoder?.startDecode(bytes);
300-
301-
if (decodeInfo != null) {
302-
final gmaps.Size size = gmaps.Size(
303-
decodeInfo.width / scale,
304-
decodeInfo.height / scale,
305-
);
306-
icon.size = size;
307-
icon.scaledSize = size;
308-
}
309-
} else if (iconConfig.length == 5) {
310-
final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 4);
311-
if (size != null) {
312-
icon.size = size;
313-
icon.scaledSize = size;
314-
}
286+
final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 4);
287+
if (size != null) {
288+
icon.size = size;
289+
icon.scaledSize = size;
315290
}
316291
break;
317292
case BitmapDescriptor.bitmapNoScaling:
@@ -325,29 +300,10 @@ Future<gmaps.Icon?> _gmIconFromBitmapDescriptor(
325300
icon = gmaps.Icon()..url = Url.createObjectUrlFromBlob(blob);
326301
switch (iconConfig[2]! as String) {
327302
case BitmapDescriptor.bitmapAutoScaling:
328-
if (iconConfig.length == 4) {
329-
final double scale = iconConfig[3]! as double;
330-
// Google Maps Web SDK does not support the scaling of the marker with anything other than
331-
// the size parameter, therefore the width and height of the image must be read from the image.
332-
// To avoid this, it is best to provide the image size instead of scale when using the web platform.
333-
final img.Decoder? decoder =
334-
img.findDecoderForData(bytes as Uint8List);
335-
final img.DecodeInfo? decodeInfo = decoder?.startDecode(bytes);
336-
337-
if (decodeInfo != null) {
338-
final gmaps.Size size = gmaps.Size(
339-
decodeInfo.width / scale,
340-
decodeInfo.height / scale,
341-
);
342-
icon.size = size;
343-
icon.scaledSize = size;
344-
}
345-
} else if (iconConfig.length == 5) {
346-
final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 4);
347-
if (size != null) {
348-
icon.size = size;
349-
icon.scaledSize = size;
350-
}
303+
final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 4);
304+
if (size != null) {
305+
icon.size = size;
306+
icon.scaledSize = size;
351307
}
352308
break;
353309
case BitmapDescriptor.bitmapNoScaling:

0 commit comments

Comments
 (0)