Skip to content

Commit bce83cd

Browse files
committed
[google_maps_flutter] Improve marker sizing
1 parent 84fd510 commit bce83cd

File tree

11 files changed

+509
-66
lines changed

11 files changed

+509
-66
lines changed

packages/google_maps_flutter/google_maps_flutter/AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,4 @@ Anton Borries <[email protected]>
6565
6666
Rahul Raj <[email protected]>
6767
Taha Tesser <[email protected]>
68+
Joonas Kerttula <[email protected]>

packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
## NEXT
1+
## 2.7.0
22

3-
* Updates minimum supported SDK version to Flutter 3.16/Dart 3.2.
3+
* Adds support for BitmapDescriptor classes `AssetMapBitmap` and `BytesMapBitmap`.
4+
* Updates minimum supported SDK version to Flutter 3.22/Dart 3.4.
45

56
## 2.6.1
67

packages/google_maps_flutter/google_maps_flutter/example/integration_test/src/maps_controller.dart

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6+
import 'dart:convert';
67

78
import 'package:flutter/foundation.dart';
89
import 'package:flutter/material.dart';
910
import 'package:flutter_test/flutter_test.dart';
1011
import 'package:google_maps_flutter/google_maps_flutter.dart';
1112
import 'package:integration_test/integration_test.dart';
1213

14+
import 'resources/icon_image_base64.dart';
1315
import 'shared.dart';
1416

1517
/// Integration Tests that only need a standard [GoogleMapController].
@@ -401,6 +403,112 @@ void runTests() {
401403
expect(iwVisibleStatus, false);
402404
});
403405

406+
testWidgets('markerWithAssetMapBitmap', (WidgetTester tester) async {
407+
final Set<Marker> markers = <Marker>{
408+
Marker(
409+
markerId: const MarkerId('1'),
410+
icon: AssetMapBitmap(
411+
'assets/red_square.png',
412+
imagePixelRatio: 1.0,
413+
)),
414+
};
415+
await pumpMap(
416+
tester,
417+
GoogleMap(
418+
initialCameraPosition: const CameraPosition(target: LatLng(10.0, 15.0)),
419+
markers: markers,
420+
),
421+
);
422+
});
423+
424+
testWidgets('markerWithAssetMapBitmapCreate', (WidgetTester tester) async {
425+
final ImageConfiguration imageConfiguration = ImageConfiguration(
426+
devicePixelRatio: tester.view.devicePixelRatio,
427+
);
428+
final Set<Marker> markers = <Marker>{
429+
Marker(
430+
markerId: const MarkerId('1'),
431+
icon: await AssetMapBitmap.create(
432+
imageConfiguration,
433+
'assets/red_square.png',
434+
)),
435+
};
436+
await pumpMap(
437+
tester,
438+
GoogleMap(
439+
initialCameraPosition: const CameraPosition(target: LatLng(10.0, 15.0)),
440+
markers: markers,
441+
),
442+
);
443+
});
444+
445+
testWidgets('markerWithBytesMapBitmap', (WidgetTester tester) async {
446+
final Uint8List bytes = const Base64Decoder().convert(iconImageBase64);
447+
final Set<Marker> markers = <Marker>{
448+
Marker(
449+
markerId: const MarkerId('1'),
450+
icon: BytesMapBitmap(
451+
bytes,
452+
imagePixelRatio: tester.view.devicePixelRatio,
453+
),
454+
),
455+
};
456+
await pumpMap(
457+
tester,
458+
GoogleMap(
459+
initialCameraPosition: const CameraPosition(target: LatLng(10.0, 15.0)),
460+
markers: markers,
461+
),
462+
);
463+
});
464+
465+
testWidgets('markerWithLegacyAsset', (WidgetTester tester) async {
466+
tester.view.devicePixelRatio = 2.0;
467+
final ImageConfiguration imageConfiguration = ImageConfiguration(
468+
devicePixelRatio: tester.view.devicePixelRatio,
469+
size: const Size(100, 100),
470+
);
471+
final Set<Marker> markers = <Marker>{
472+
Marker(
473+
markerId: const MarkerId('1'),
474+
icon: await BitmapDescriptor.fromAssetImage(
475+
imageConfiguration,
476+
'assets/red_square.png',
477+
)),
478+
};
479+
await pumpMap(
480+
tester,
481+
GoogleMap(
482+
initialCameraPosition: const CameraPosition(target: LatLng(10.0, 15.0)),
483+
markers: markers,
484+
),
485+
);
486+
487+
await tester.pumpAndSettle();
488+
});
489+
490+
testWidgets('markerWithLegacyBytes', (WidgetTester tester) async {
491+
tester.view.devicePixelRatio = 2.0;
492+
final Uint8List bytes = const Base64Decoder().convert(iconImageBase64);
493+
final Set<Marker> markers = <Marker>{
494+
Marker(
495+
markerId: const MarkerId('1'),
496+
icon: BitmapDescriptor.fromBytes(
497+
bytes,
498+
size: const Size(100, 100),
499+
)),
500+
};
501+
await pumpMap(
502+
tester,
503+
GoogleMap(
504+
initialCameraPosition: const CameraPosition(target: LatLng(10.0, 15.0)),
505+
markers: markers,
506+
),
507+
);
508+
509+
await tester.pumpAndSettle();
510+
});
511+
404512
testWidgets('testTakeSnapshot', (WidgetTester tester) async {
405513
final Completer<GoogleMapController> controllerCompleter =
406514
Completer<GoogleMapController>();
1.23 KB
Loading
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
/// This constant holds the base64-encoded data of a 16x16 PNG image of the
6+
/// Flutter logo.
7+
///
8+
/// See `icon_image.png` source in the same directory.
9+
///
10+
/// To create or update this image, follow these steps:
11+
/// 1. Create or update a 16x16 PNG image.
12+
/// 2. Convert the image to a base64 string using a script below.
13+
/// 3. Replace the existing base64 string below with the new one.
14+
///
15+
/// Example of converting an image to base64 in Dart:
16+
/// ```dart
17+
/// import 'dart:convert';
18+
/// import 'dart:io';
19+
///
20+
/// void main() async {
21+
/// final bytes = await File('icon_image.png').readAsBytes();
22+
/// final base64String = base64Encode(bytes);
23+
/// print(base64String);
24+
/// }
25+
/// ```
26+
const String iconImageBase64 =
27+
'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAIRlWElmTU'
28+
'0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIA'
29+
'AIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQ'
30+
'AAABCgAwAEAAAAAQAAABAAAAAAx28c8QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAVlpVFh0WE1M'
31+
'OmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIH'
32+
'g6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8v'
33+
'd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcm'
34+
'lwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFk'
35+
'b2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk'
36+
'9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6'
37+
'eG1wbWV0YT4KTMInWQAAAplJREFUOBF1k01ME1EQx2fe7tIPoGgTE6AJgQQSPaiH9oAtkFbsgX'
38+
'jygFcT0XjSkxcTDxtPJh6MR28ePMHBBA8cNLSIony0oBhEMVETP058tE132+7uG3cW24DAXN57'
39+
'2fn9/zPz3iIcEdEl0nIxtNLr1IlVeoMadkubKmoL+u2SzAV8IjV5Ekt4GN+A8+VOUPwLarOI2G'
40+
'Vpqq0i4JQorwQxPtWHVZ1IKP8LNGDXGaSyqARFxDGo7MJBy4XVf3AyQ+qTHnTEXoF9cFUy3OkY'
41+
'0oWxmWFtD5xNoc1sQ6AOn1+hCNTkkhKow8KFZV77tVs2O9dhFvBm0IA/U0RhZ7/ocEx23oUDlh'
42+
'h8HkNjZIN8Lb3gOU8gOp7AKJHCB2/aNZkTftHumNzzbtl2CBPZHqxw8mHhVZBeoz6w5DvhE2FZ'
43+
'lQYPjKdd2/qRyKZ6KsPv7TEk7EYEk0A0EUmJduHRy1i4oLKqgmC59ZggAdwrC9pFuWy1iUT2rA'
44+
'uv0h2UdNtNqxCBBkgqorjOMOgksN7CxQ90vEb00U3c3LIwyo9o8FXxQVNr8Coqyk+S5EPBXnjt'
45+
'xRmc4TegI7qWbvBkeeUbGMnTCd4nZnYeDOWIEtlC6cKK/JJepY3hZSvN33jovO6L0XFqPKqBTO'
46+
'FuapUoPr1lxDM7cmC2TAOz25cYSGa++feBew/cjpc0V+mNT29/HZp3KDFTNLvuTRPEHy5065lj'
47+
'Xn4y41XM+wP/AlcycRmdc3MUhvLm/J/ceu/3qUVT62oP2EZpjSylHybHSpDUVcjq9gEBVo0+Xt'
48+
'JyN2IWRO+3QUforRoKnZLVsglaMECW+YmMSj9M3SrC6Lg71CMiqWfUrJ6ywzefhnZ+G69BaKdB'
49+
'WhXQAn6wzDUpfUPw7MrmX/WhbfmKblw+AAAAAElFTkSuQmCC';
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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+
import 'dart:typed_data';
6+
import 'dart:ui' as ui;
7+
8+
import 'package:flutter/material.dart';
9+
10+
/// Returns a generated png image in [ByteData] format with the requested size.
11+
Future<ByteData> createCustomMarkerIconImage({required Size size}) async {
12+
final ui.PictureRecorder recorder = ui.PictureRecorder();
13+
final Canvas canvas = Canvas(recorder);
14+
final _MarkerPainter painter = _MarkerPainter();
15+
16+
painter.paint(canvas, size);
17+
18+
final ui.Image image = await recorder
19+
.endRecording()
20+
.toImage(size.width.floor(), size.height.floor());
21+
22+
final ByteData? bytes =
23+
await image.toByteData(format: ui.ImageByteFormat.png);
24+
return bytes!;
25+
}
26+
27+
class _MarkerPainter extends CustomPainter {
28+
@override
29+
void paint(Canvas canvas, Size size) {
30+
final Rect rect = Offset.zero & size;
31+
const RadialGradient gradient = RadialGradient(
32+
colors: <Color>[Colors.yellow, Colors.red],
33+
stops: <double>[0.4, 1.0],
34+
);
35+
36+
// Draw radial gradient
37+
canvas.drawRect(
38+
rect,
39+
Paint()..shader = gradient.createShader(rect),
40+
);
41+
42+
// Draw diagonal black line
43+
canvas.drawLine(
44+
Offset.zero,
45+
Offset(size.width, size.height),
46+
Paint()
47+
..color = Colors.black
48+
..strokeWidth = 1,
49+
);
50+
}
51+
52+
@override
53+
bool shouldRepaint(_MarkerPainter oldDelegate) => false;
54+
@override
55+
bool shouldRebuildSemantics(_MarkerPainter oldDelegate) => false;
56+
}

0 commit comments

Comments
 (0)