Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Closed
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
1 change: 1 addition & 0 deletions packages/google_maps_flutter/google_maps_flutter/AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,4 @@ Anton Borries <[email protected]>
Alex Li <[email protected]>
Rahul Raj <[email protected]>
Taha Tesser <[email protected]>
Joonas Kerttula <[email protected]>
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## NEXT
## 2.3.0

* Adds better support for marker size and scaling behaviour with `BitmapDescriptor.createFromAsset` and `BitmapDescriptor.createFromBytes`.
* Deprecates `BitmapDescriptor.fromAssetImage` in favor of `BitmapDescriptor.createFromAsset`
* Deprecates `BitmapDescriptor.fromBytes` in favor of `BitmapDescriptor.createFromBytes`
* Updates minimum Flutter version to 3.0.

## 2.2.3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -940,17 +940,21 @@ void main() {
expect(iwVisibleStatus, false);
});

testWidgets('fromAssetImage', (WidgetTester tester) async {
testWidgets('createFromAsset', (WidgetTester tester) async {
const double pixelRatio = 2;
const ImageConfiguration imageConfiguration =
ImageConfiguration(devicePixelRatio: pixelRatio);
final BitmapDescriptor mip = await BitmapDescriptor.fromAssetImage(
imageConfiguration, 'red_square.png');
final BitmapDescriptor scaled = await BitmapDescriptor.fromAssetImage(
imageConfiguration, 'red_square.png',
mipmaps: false);
expect((mip.toJson() as List<dynamic>)[2], 1);
expect((scaled.toJson() as List<dynamic>)[2], 2);
final BitmapDescriptor mip = await BitmapDescriptor.createFromAsset(
imageConfiguration,
'red_square.png',
);
final BitmapDescriptor scaled = await BitmapDescriptor.createFromAsset(
imageConfiguration,
'red_square.png',
mipmaps: false,
);
expect((mip.toJson() as List<dynamic>)[3], 1.0);
expect((scaled.toJson() as List<dynamic>)[3], 2);
});

testWidgets('testTakeSnapshot', (WidgetTester tester) async {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: public_member_api_docs

import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';

Future<ByteData> createCustomMarkerIconImage({required Size size}) async {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final Canvas canvas = Canvas(recorder);
final _MarkerPainter painter = _MarkerPainter();

painter.paint(canvas, size);

final ui.Image image = await recorder
.endRecording()
.toImage(size.width.floor(), size.height.floor());

final ByteData? bytes =
await image.toByteData(format: ui.ImageByteFormat.png);
return bytes!;
}

class _MarkerPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final Rect rect = Offset.zero & size;
const RadialGradient gradient = RadialGradient(
colors: <Color>[Colors.yellow, Colors.red],
stops: <double>[0.4, 1.0],
);
canvas.drawRect(
rect,
Paint()..shader = gradient.createShader(rect),
);
}

@override
bool shouldRepaint(_MarkerPainter oldDelegate) => false;
@override
bool shouldRebuildSemantics(_MarkerPainter oldDelegate) => false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@
// ignore_for_file: public_member_api_docs
// ignore_for_file: unawaited_futures

import 'dart:async';
import 'dart:math';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

import 'custom_marker_icon.dart';
import 'page.dart';

class MarkerIconsPage extends GoogleMapExampleAppPage {
Expand All @@ -30,65 +35,235 @@ class MarkerIconsBody extends StatefulWidget {
const LatLng _kMapCenter = LatLng(52.4478, -3.5402);

class MarkerIconsBodyState extends State<MarkerIconsBody> {
final double _markerAssetImageSize = 48;
final Size _customSize = const Size(55, 30);
Set<Marker> _markers = <Marker>{};
double _markerScale = 1.0;
bool _scalingEnabled = true;
bool _customSizeEnabled = false;
bool _mipMapsEnabled = true;
GoogleMapController? controller;
BitmapDescriptor? _markerIcon;
BitmapDescriptor? _markerIconAsset;
BitmapDescriptor? _markerIconBytes;
final int _markersAmountPerType = 15;

@override
Widget build(BuildContext context) {
_createMarkerImageFromAsset(context);
_createCustomMarkerIconImages(context);
final Size size = getCurrentMarkerSize();
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Center(
child: SizedBox(
width: 350.0,
height: 300.0,
child: GoogleMap(
initialCameraPosition: const CameraPosition(
target: _kMapCenter,
zoom: 7.0,
Column(children: <Widget>[
Center(
child: SizedBox(
width: 350.0,
height: 300.0,
child: GoogleMap(
initialCameraPosition: const CameraPosition(
target: _kMapCenter,
zoom: 7.0,
),
markers: _markers,
onMapCreated: _onMapCreated,
),
markers: <Marker>{_createMarker()},
onMapCreated: _onMapCreated,
),
),
)
TextButton(
onPressed: () => _toggleScaling(context),
child: Text(_scalingEnabled
? 'Disable auto scaling'
: 'Enable auto scaling'),
),
if (_scalingEnabled) ...<Widget>[
Container(
width: size.width,
height: size.height,
color: Colors.red,
),
Text(
'Reference box with size of ${size.width} x ${size.height} in logical pixels.'),
const SizedBox(height: 10),
TextButton(
onPressed: () => _toggleCustomSize(context),
child: Text(_customSizeEnabled
? 'Disable custom size'
: 'Enable custom size'),
),
if (_customSizeEnabled)
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
TextButton(
onPressed: _markerScale <= 0.5
? null
: () => _decreaseScale(context),
child: const Text(
'-',
textScaleFactor: 2,
),
),
Text('scale ${_markerScale}x'),
TextButton(
onPressed: _markerScale >= 2.5
? null
: () => _increaseScale(context),
child: const Text(
'+',
textScaleFactor: 2,
),
),
],
),
],
TextButton(
onPressed: () => _toggleMipMaps(context),
child: Text(_mipMapsEnabled ? 'Disable mipmaps' : 'Enable mipmaps'),
),
])
],
);
}

Marker _createMarker() {
if (_markerIcon != null) {
return Marker(
markerId: const MarkerId('marker_1'),
position: _kMapCenter,
icon: _markerIcon!,
);
} else {
return const Marker(
markerId: MarkerId('marker_1'),
position: _kMapCenter,
);
}
Size getCurrentMarkerSize() {
return _scalingEnabled && _customSizeEnabled
? _customSize * _markerScale
: Size(_markerAssetImageSize, _markerAssetImageSize);
}

Future<void> _createMarkerImageFromAsset(BuildContext context) async {
if (_markerIcon == null) {
final ImageConfiguration imageConfiguration =
createLocalImageConfiguration(context, size: const Size.square(48));
BitmapDescriptor.fromAssetImage(
imageConfiguration, 'assets/red_square.png')
.then(_updateBitmap);
}
void _toggleMipMaps(BuildContext context) {
_mipMapsEnabled = !_mipMapsEnabled;
_updateMarkerImages(context);
}

void _toggleScaling(BuildContext context) {
_scalingEnabled = !_scalingEnabled;
_updateMarkerImages(context);
}

void _toggleCustomSize(BuildContext context) {
_customSizeEnabled = !_customSizeEnabled;
_updateMarkerImages(context);
}

void _decreaseScale(BuildContext context) {
_markerScale = max(_markerScale - 0.5, 0.0);
_updateMarkerImages(context);
}

void _increaseScale(BuildContext context) {
_markerScale = min(_markerScale + 0.5, 2.5);
_updateMarkerImages(context);
}

void _updateMarkerImages(BuildContext context) {
_updateMarkerAssetImage(context);
_updateMarkerBytesImage(context);
_updateMarkers();
}

Marker _createAssetMarker(int index) {
final LatLng position =
LatLng(_kMapCenter.latitude - (index * 0.5), _kMapCenter.longitude - 1);

return Marker(
markerId: MarkerId('marker_asset_$index'),
position: position,
icon: _markerIconAsset!,
);
}

Marker _createBytesMarker(int index) {
final LatLng position =
LatLng(_kMapCenter.latitude - (index * 0.5), _kMapCenter.longitude + 1);

return Marker(
markerId: MarkerId('marker_bytes_$index'),
position: position,
icon: _markerIconBytes!,
);
}

void _updateBitmap(BitmapDescriptor bitmap) {
void _updateMarkers() {
final Set<Marker> markers = <Marker>{};
for (int i = 0; i < _markersAmountPerType; i++) {
if (_markerIconAsset != null) {
markers.add(_createAssetMarker(i));
}
if (_markerIconBytes != null) {
markers.add(_createBytesMarker(i));
}
}
setState(() {
_markerIcon = bitmap;
_markers = markers;
});
}

Future<void> _updateMarkerAssetImage(BuildContext context) async {
final Size? size =
_scalingEnabled && _customSizeEnabled ? getCurrentMarkerSize() : null;
final ImageConfiguration imageConfiguration = createLocalImageConfiguration(
context,
size: size,
);
BitmapDescriptor.createFromAsset(
imageConfiguration, 'assets/red_square.png',
mipmaps: _mipMapsEnabled,
imagePixelRatio: _mipMapsEnabled ? null : 1.0,
bitmapScaling:
_scalingEnabled ? BitmapScaling.auto : BitmapScaling.noScaling)
.then(_updateAssetBitmap);
}

Future<void> _updateMarkerBytesImage(BuildContext context) async {
final double devicePixelRatio =
WidgetsBinding.instance.window.devicePixelRatio;

final Size markerSize = getCurrentMarkerSize();

final double? imagePixelRatio = _scalingEnabled ? devicePixelRatio : null;

// Create canvasSize with physical marker size
final Size canvasSize = Size(markerSize.width * (imagePixelRatio ?? 1.0),
markerSize.height * (imagePixelRatio ?? 1.0));

final ByteData bytes = await createCustomMarkerIconImage(size: canvasSize);

// Size is used only for custom size
final Size? size =
_scalingEnabled && _customSizeEnabled ? getCurrentMarkerSize() : null;

final BitmapDescriptor bitmap = BitmapDescriptor.createFromBytes(
bytes.buffer.asUint8List(),
imagePixelRatio: _customSizeEnabled ? null : imagePixelRatio,
size: size,
bitmapScaling:
_scalingEnabled ? BitmapScaling.auto : BitmapScaling.noScaling);

_updateBytesBitmap(bitmap);
}

void _updateAssetBitmap(BitmapDescriptor bitmap) {
_markerIconAsset = bitmap;
_updateMarkers();
}

void _updateBytesBitmap(BitmapDescriptor bitmap) {
_markerIconBytes = bitmap;
_updateMarkers();
}

void _createCustomMarkerIconImages(BuildContext context) {
if (_markerIconAsset == null) {
_updateMarkerAssetImage(context);
}

if (_markerIconBytes == null) {
_updateMarkerBytesImage(context);
}
}

void _onMapCreated(GoogleMapController controllerParam) {
setState(() {
controller = controllerParam;
Expand Down
Loading