简体   繁体   中英

How to take screenshot of Google Map in flutter

I am using screenshot plugin for taking screenshot in flutter, but problem is when i am taking screenshot, is showing blank white screen and after that i not getting anything.This happen every time.I am performing this task using below pseudo.

void main() async {
runApp(MyApp());
}

class MyApp extends StatefulWidget {

@override
_MyAppState createState() => _MyAppState();

 }

 class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
    Completer<GoogleMapController> _controller = Completer();
    LocationData locationdata;
    ScreenshotController screenshotController = ScreenshotController();
    var location;
    var lat = 0.0;
    var lng = 0.0;
    CameraController controller;
    final Set<Marker> _markers = {};
    StreamSubscription<LocationData> locationSubcription;
    LatLng _center;
    File _imageFile;
    static GlobalKey screen = new GlobalKey();
    String imagePath;


      @override
   void initState() {
// TODO: implement initState
super.initState();
// Fetch the available cameras before initializing the app.
WidgetsBinding.instance.addObserver(this);

//  _cameraTogglesRowWidget();

onNewCameraSelected(null);

location = new Location();

_getLocation();

locationSubcription = location.onLocationChanged().listen((location) {
  setState(() {
    locationdata = location;
  });
});
}


 @override
 void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}

 @override
 Widget build(BuildContext context) {
  return MaterialApp(
    home: Scaffold(
  appBar: AppBar(
    title: Text('Maps Sample App'),
    backgroundColor: Colors.green[700],
  ),
  body: Container(
    margin: EdgeInsets.all(5),
    child: Column(children: <Widget>[
        Screenshot(
        controller: screenshotController,
      //          RepaintBoundary(
   //            key: screen,
        child:
        Row(
          children: <Widget>[
            Expanded(
                child: Container(
              width: 200,
              height: 200,
              child: GoogleMap(
                  onMapCreated: _onMapCreated,
                  initialCameraPosition: CameraPosition(
                    target: _center,
                    zoom: 15.0,
                  ),
                  markers: _markers),decoration: BoxDecoration(
                  color: Colors.black,
                  border: Border.all(
                    color: controller != null &&
                        controller.value.isRecordingVideo
                        ? Colors.redAccent
                        : Colors.grey,
                    width: 3.0,
                  ),
                )
            ),),
            Expanded(
              child: Container(
                width: 200,
                height: 200,
                child: Padding(
                  padding: const EdgeInsets.all(1.0),
                  child: Center(
                    child: _cameraPreviewWidget(),
                  ),
                ),
                decoration: BoxDecoration(
                  color: Colors.black,
                  border: Border.all(
                    color: controller != null &&
                            controller.value.isRecordingVideo
                        ? Colors.redAccent
                        : Colors.grey,
                    width: 3.0,
                  ),
                ),
              ),
            )
          ],
        )),
     // ),
      _imageFile != null ? Image.file(_imageFile,height: 200,width: 200,) 
   : Container(),
  //          Screenshot(
   //            controller: screenshotController,
  //            child:
 //            imagePath != null ? Image.asset(imagePath,height: 
             100,width: 100,) : Container(),
    //        ),
      RaisedButton(
        onPressed: () {
          screenshotController.capture().then((File image) {
            //Capture Done
            setState(() {
              _imageFile = image;
            });
          }).catchError((onError) {
            print(onError);
          });

        //  onTakePictureButtonPressed();
      //   ScreenShot();
        },
        child: Text('Capture'),
        )
      ]),
      ),
        ));
        }

   ScreenShot() async{
  RenderRepaintBoundary boundary = 
  screen.currentContext.findRenderObject();
  ui.Image image = await boundary.toImage();
   ByteData byteData = await image.toByteData(format: 
  ui.ImageByteFormat.png);

  var filePath = await ImagePickerSaver.saveFile(
    fileData:byteData.buffer.asUint8List() );
  print(filePath);

  }

  void _onMapCreated(GoogleMapController controller) {
   _controller.complete(controller);
 }

      void _getLocation() async {
  var currentLocation;
   try {
    currentLocation = await location.getLocation();
   } catch (e) {
    currentLocation = null;
  }
  setState(() {
  locationdata = currentLocation;
  lat = locationdata.latitude;
  lng = locationdata.longitude;

  _center = LatLng(lat, lng);
  _markers.add(Marker(
    // This marker id can be anything that uniquely identifies each 
   marker.
    markerId: MarkerId(_center.toString()),
    position: _center,
    infoWindow: InfoWindow(
      title: 'Really cool place',
      snippet: '5 Star Rating',
    ),
      icon: BitmapDescriptor.defaultMarker,
  ));
   });
   }

   @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
  if (state == AppLifecycleState.inactive) {
  controller?.dispose();
  } else if (state == AppLifecycleState.resumed) {
  if (controller != null) {
    onNewCameraSelected(controller.description);
    }
  }
 }

   Widget _captureControlRowWidget() {
   return Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  mainAxisSize: MainAxisSize.max,
  children: <Widget>[
    IconButton(
      icon: const Icon(Icons.camera_alt),
      color: Colors.blue,
      onPressed: controller != null &&
          controller.value.isInitialized
          ? onTakePictureButtonPressed
          : null,

     ),
    ],
   );
  }

 String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();

Future<String> takePicture() async {
if (!controller.value.isInitialized) {
 // showInSnackBar('Error: select a camera first.');
  return null;
}
final Directory extDir = await getApplicationDocumentsDirectory();
final String dirPath = '${extDir.path}/Pictures/flutter_test';
await Directory(dirPath).create(recursive: true);
final String filePath = '$dirPath/${timestamp()}.jpg';

if (controller.value.isTakingPicture) {
  // A capture is already pending, do nothing.
  return null;
}

try {
  await controller.takePicture(filePath);
} on CameraException catch (e) {
//  _showCameraException(e);
  return null;
}
return filePath;
 }

void onTakePictureButtonPressed() {
  takePicture().then((String filePath) {
  if (mounted) {
    setState(() {
      imagePath = filePath;

    });
   // if (filePath != null)
      //showInSnackBar('Picture saved to $filePath');
  }
  });
}

 void onNewCameraSelected(CameraDescription cameraDescription) async {
  if (controller != null) {
  await controller.dispose();
   }
  controller = CameraController(
  CameraDescription(
      name: "1",
      lensDirection: CameraLensDirection.front,
      sensorOrientation: 270),
  ResolutionPreset.high,
  enableAudio: true,
   );

  // If the controller is updated then update the UI.
  controller.addListener(() {
  if (mounted) setState(() {});
  if (controller.value.hasError) {
    //  showInSnackBar('Camera error 
  ${controller.value.errorDescription}');
    print(controller.value.errorDescription);
   }
   });

   try {
   await controller.initialize();
  } on CameraException catch (e) {
  print(e);
  //  _showCameraException(e);
  }

  if (mounted) {
  setState(() {});
  }
  }

  Widget _cameraTogglesRowWidget() {
   final List<Widget> toggles = <Widget>[];

  if (cameras.isEmpty) {
    return const Text('No camera found');
  } else {
    for (CameraDescription cameraDescription in cameras) {
     toggles.add(
      SizedBox(
        width: 90.0,
        child: RadioListTile<CameraDescription>(
          title: Icon(getCameraLensIcon(cameraDescription.lensDirection)),
          groupValue: controller?.description,
          value: cameraDescription,
          onChanged: controller != null ? null : onNewCameraSelected,
        ),
      ),
       );
        }
      }

          return Row(children: toggles);
        }

       IconData getCameraLensIcon(CameraLensDirection direction) {
     switch (direction) {
      case CameraLensDirection.back:
       return Icons.camera_rear;
         case CameraLensDirection.front:
     return Icons.camera_front;
        case CameraLensDirection.external:
          return Icons.camera;
         }
        throw ArgumentError('Unknown lens direction');
          }

             Widget _cameraPreviewWidget() {
       if (controller == null || !controller.value.isInitialized) {
       return const Text(
      'Tap a camera',
      style: TextStyle(
        color: Colors.white,
      fontSize: 24.0,
      fontWeight: FontWeight.w900,
      ),
      );
      } else {
   return AspectRatio(
    aspectRatio: controller.value.aspectRatio,
     child: CameraPreview(controller),
    );
    }
   }
        }

''''''

That is code Output: https://i.stack.imgur.com/PMLKu.jpg

But I need that Output: https://i.stack.imgur.com/rAAs8.jpg

I sent a pull request to Flutter that implements this. Until they merge it, you could use my fork of the plugin: https://github.com/duzenko/plugins/tree/maps-snapshot

Checked on the package: google_maps_flutter 2.0.6

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GoogleMap(
        initialCameraPosition: CameraPosition(
          target: LatLng(52.45594441365693, 30.96944870261119),
          zoom: 14,
        ),
        onMapCreated: (controller) async {
          final uin8list = await controller.takeSnapshot(); // its our screenshot

          // For examle, we can convert this uin8list to base64 and send 
          // to photohosting imgbb.com and get url on this image
          final base64image = base64Encode(uin8list);

          //...api post request
        },
      ),

The issue here is that the snapshot method and the Screenshot plugin both take the image before the map is rendered. There are two callbacks in the flutter_maps plugin which are available:

  1. onMapCreated fires too soon and not on each redraw.
  2. onCameraIdle is good on Android, but doesn't fire on the first render on iOS.

You can use onCameraIdle if you put an animateCamera in the onMapCreated callback and capture the image in onCameraIdle . I found you need to move the camera each time by adding a small random zoom offset.

class GoogleMapWidget extends StatefulWidget {
  const GoogleMapWidget(
      {Key? key, required this.latLng, required this.markers})
      : super(key: key);

  final LatLng latLng;

  final Set<Marker> markers;

  @override
  State<GoogleMapWidget> createState() => GoogleMapWidgetState();
}

class GoogleMapWidgetState extends State<GoogleMapWidget> {
  final Completer<GoogleMapController> _mapController =
      Completer<GoogleMapController>();

  Uint8List? imageBytes;

  @override
  Widget build(BuildContext context) {
    return imageBytes != null
        ? Image.memory(imageBytes!, fit: BoxFit.cover)
        : GoogleMap(
            onMapCreated: (GoogleMapController controller) {
              _mapController.complete(controller);
              takeSnapShot();
            },
            initialCameraPosition: CameraPosition(
              target: widget.latLng,
              zoom: 13.0,
            ),
            markers: widget.markers,
            compassEnabled: false,
            indoorViewEnabled: false,
            myLocationEnabled: false,
            myLocationButtonEnabled: false,
            zoomControlsEnabled: false,
            zoomGesturesEnabled: false,
            tiltGesturesEnabled: false,
            scrollGesturesEnabled: false,
            rotateGesturesEnabled: false,
          );
  }

  void takeSnapShot() async {
    GoogleMapController controller = await _mapController.future;
    Future<void>.delayed(const Duration(milliseconds: 1000), () async {
      imageBytes = await controller.takeSnapshot();
      setState(() {});
    });
  }

for getting the screenshot of the only map content you can use GoogleMapController.takeSnapshot() function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM