Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

[google_maps_flutter] Take snapshot of map #2607

Merged
merged 15 commits into from
Mar 24, 2020
Merged
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
3 changes: 2 additions & 1 deletion AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ Luigi Agosti <luigi@tengio.com>
Quentin Le Guennec <quentin@tengio.com>
Koushik Ravikumar <koushik@tengio.com>
Nissim Dsilva <nissim@tengio.com>
Giancarlo Rocha <giancarloiff@gmail.com>
Giancarlo Rocha <giancarloiff@gmail.com>
Ryo Miyake <ryo@miyake.id>
4 changes: 4 additions & 0 deletions packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.5.25+1

* Add takeSnapshot that takes a snapshot of the map.

## 0.5.25

* Add an optional param `mipmaps` for `BitmapDescriptor.fromAssetImage`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.os.Bundle;
import android.util.Log;
Expand All @@ -27,6 +28,7 @@
import androidx.lifecycle.LifecycleOwner;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.SnapshotReadyCallback;
import com.google.android.gms.maps.GoogleMapOptions;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.OnMapReadyCallback;
Expand All @@ -44,6 +46,7 @@
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.platform.PlatformView;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
Expand Down Expand Up @@ -276,6 +279,26 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) {
}
break;
}
case "map#takeSnapshot":
{
if (googleMap != null) {
cyanglaz marked this conversation as resolved.
Show resolved Hide resolved
final MethodChannel.Result _result = result;
googleMap.snapshot(
new SnapshotReadyCallback() {
@Override
public void onSnapshotReady(Bitmap bitmap) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
bitmap.recycle();
_result.success(byteArray);
}
});
} else {
result.error("GoogleMap uninitialized", "takeSnapshot", null);
}
break;
}
case "camera#move":
{
final CameraUpdate cameraUpdate =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import 'place_marker.dart';
import 'place_polygon.dart';
import 'place_polyline.dart';
import 'scrolling_map.dart';
import 'snapshot.dart';

final List<Page> _allPages = <Page>[
MapUiPage(),
Expand All @@ -32,6 +33,7 @@ final List<Page> _allPages = <Page>[
PlacePolygonPage(),
PlaceCirclePage(),
PaddingPage(),
SnapshotPage(),
];

class MapsDemo extends StatelessWidget {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2020 The Chromium 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 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

import 'page.dart';

const CameraPosition _kInitialPosition =
CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0);

class SnapshotPage extends Page {
SnapshotPage()
: super(const Icon(Icons.camera_alt), 'Take a snapshot of the map');

@override
Widget build(BuildContext context) {
return _SnapshotBody();
}
}

class _SnapshotBody extends StatefulWidget {
@override
_SnapshotBodyState createState() => _SnapshotBodyState();
}

class _SnapshotBodyState extends State<_SnapshotBody> {
GoogleMapController _mapController;
Uint8List _imageBytes;

@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(
height: 180,
child: GoogleMap(
onMapCreated: onMapCreated,
initialCameraPosition: _kInitialPosition,
),
),
FlatButton(
child: Text('Take a snapshot'),
onPressed: () async {
final imageBytes = await _mapController?.takeSnapshot();
setState(() {
_imageBytes = imageBytes;
});
},
),
Container(
decoration: BoxDecoration(color: Colors.blueGrey[50]),
height: 180,
child: _imageBytes != null ? Image.memory(_imageBytes) : null,
),
],
),
);
}

void onMapCreated(GoogleMapController controller) {
_mapController = controller;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

Expand Down Expand Up @@ -63,4 +64,8 @@ class GoogleMapInspector {
Future<bool> isBuildingsEnabled() async {
return await _channel.invokeMethod<bool>('map#isBuildingsEnabled');
}

Future<Uint8List> takeSnapshot() async {
return await _channel.invokeMethod<Uint8List>('map#takeSnapshot');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

import 'dart:async';
import 'dart:io';
import 'dart:typed_data';

import 'package:e2e/e2e.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:e2e/e2e.dart';

import 'google_map_inspector.dart';

Expand Down Expand Up @@ -838,4 +839,30 @@ void main() {
// ignore: invalid_use_of_visible_for_testing_member
expect(scaled.toJson()[2], 2);
});

testWidgets('testTakeSnapshot', (WidgetTester tester) async {
Completer<GoogleMapInspector> inspectorCompleter =
Completer<GoogleMapInspector>();

await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: GoogleMap(
initialCameraPosition: _kInitialCameraPosition,
onMapCreated: (GoogleMapController controller) {
final GoogleMapInspector inspector =
// ignore: invalid_use_of_visible_for_testing_member
GoogleMapInspector(controller.channel);
inspectorCompleter.complete(inspector);
},
),
),
);

await tester.pumpAndSettle(const Duration(seconds: 3));

final GoogleMapInspector inspector = await inspectorCompleter.future;
final Uint8List bytes = await inspector.takeSnapshot();
expect(bytes?.isNotEmpty, true);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,22 @@ - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
}
} else if ([call.method isEqualToString:@"map#waitForMap"]) {
result(nil);
} else if ([call.method isEqualToString:@"map#takeSnapshot"]) {
if (_mapView != nil) {
UIGraphicsImageRendererFormat* format = [UIGraphicsImageRendererFormat defaultFormat];
format.scale = [[UIScreen mainScreen] scale];
UIGraphicsImageRenderer* renderer =
[[UIGraphicsImageRenderer alloc] initWithSize:_mapView.frame.size format:format];

UIImage* image = [renderer imageWithActions:^(UIGraphicsImageRendererContext* context) {
[_mapView.layer renderInContext:context.CGContext];
}];
result([FlutterStandardTypedData typedDataWithBytes:UIImagePNGRepresentation(image)]);
} else {
result([FlutterError errorWithCode:@"GoogleMap uninitialized"
cyanglaz marked this conversation as resolved.
Show resolved Hide resolved
message:@"takeSnapshot called prior to map initialization"
details:nil]);
}
} else if ([call.method isEqualToString:@"markers#update"]) {
id markersToAdd = call.arguments[@"markersToAdd"];
if ([markersToAdd isKindOfClass:[NSArray class]]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,4 +285,9 @@ class GoogleMapController {
await channel.invokeMethod<double>('map#getZoomLevel');
return zoomLevel;
}

/// Returns the image bytes of the map
Future<Uint8List> takeSnapshot() async {
return await channel.invokeMethod<Uint8List>('map#takeSnapshot');
}
}