diff --git a/example/lib/main.dart b/example/lib/main.dart index b4802a9..0cb1399 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -16,6 +16,7 @@ import 'package:apple_maps_flutter_example/place_circle.dart'; import 'package:apple_maps_flutter_example/place_polygon.dart'; import 'package:apple_maps_flutter_example/place_polyline.dart'; import 'package:apple_maps_flutter_example/scrolling_map.dart'; +import 'package:apple_maps_flutter_example/snapshot.dart'; import 'package:flutter/material.dart'; final List _allPages = [ @@ -32,6 +33,7 @@ final List _allPages = [ PlaceCirclePage(), ScrollingMapPage(), MapUpdatePage(), + SnapshotPage(), ]; class MapsDemo extends StatelessWidget { diff --git a/example/lib/snapshot.dart b/example/lib/snapshot.dart new file mode 100644 index 0000000..b16f808 --- /dev/null +++ b/example/lib/snapshot.dart @@ -0,0 +1,71 @@ +// 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 'package:apple_maps_flutter_example/page.dart'; +import 'package:flutter/material.dart'; +import 'package:apple_maps_flutter/apple_maps_flutter.dart'; + +const CameraPosition _kInitialPosition = + CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); + +class SnapshotPage extends ExamplePage { + 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> { + AppleMapController? _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: AppleMap( + onMapCreated: onMapCreated, + initialCameraPosition: _kInitialPosition, + ), + ), + TextButton( + 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(AppleMapController controller) { + _mapController = controller; + } +} diff --git a/ios/Classes/MapView/AppleMapController.swift b/ios/Classes/MapView/AppleMapController.swift index b5ba9d7..0d87955 100644 --- a/ios/Classes/MapView/AppleMapController.swift +++ b/ios/Classes/MapView/AppleMapController.swift @@ -16,6 +16,8 @@ public class AppleMapController: NSObject, FlutterPlatformView { var options: [String: Any] var onCalloutTapGestureRecognizer: UITapGestureRecognizer? var currentlySelectedAnnotation: String? + var snapShotOptions: MKMapSnapshotter.Options = MKMapSnapshotter.Options() + var snapShot: MKMapSnapshotter? let availableCaps: Dictionary = [ "buttCap": CGLineCap.butt, @@ -78,7 +80,7 @@ public class AppleMapController: NSObject, FlutterPlatformView { } private func setMethodCallHandlers() { - channel.setMethodCallHandler({ [unowned self] (call: FlutterMethodCall, result: FlutterResult) -> Void in + channel.setMethodCallHandler({ [unowned self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in if let args :Dictionary = call.arguments as? Dictionary { switch(call.method) { case "annotations#update": @@ -151,6 +153,10 @@ public class AppleMapController: NSObject, FlutterPlatformView { case "map#isMyLocationButtonEnabled": result(self.mapView.isMyLocationButtonShowing ?? false) break + case "map#takeSnapshot": + self.takeSnapshot(onCompletion: { (snapshot: FlutterStandardTypedData?, error: Error?) -> Void in + result(snapshot ?? error) + }) case "map#getMinMaxZoomLevels": result([self.mapView.minZoomLevel, self.mapView.maxZoomLevel]) break @@ -341,3 +347,25 @@ extension AppleMapController: MKMapViewDelegate { return MKOverlayRenderer() } } + +extension AppleMapController { + private func takeSnapshot(onCompletion: @escaping (FlutterStandardTypedData?, Error?) -> Void) { + // MKMapSnapShotOptions setting. + snapShotOptions.region = self.mapView.region + snapShotOptions.size = self.mapView.frame.size + snapShotOptions.scale = UIScreen.main.scale + + // Set MKMapSnapShotOptions to MKMapSnapShotter. + snapShot = MKMapSnapshotter(options: snapShotOptions) + + snapShot?.cancel() + + snapShot?.start(completionHandler: {(snapshot: MKMapSnapshotter.Snapshot?, error: Error?) -> Void in + if let imageData = snapshot?.image.pngData() { + onCompletion(FlutterStandardTypedData.init(bytes: imageData), nil) + } else { + onCompletion(nil, error) + } + }) + } +} diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 79459b2..b98166f 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -233,4 +233,9 @@ class AppleMapController { final doubles = List.from(point?['point']); return Offset(doubles.first, doubles.last); } + + /// Returns the image bytes of the map + Future takeSnapshot() { + return channel.invokeMethod('map#takeSnapshot'); + } }