How to change the icon size of Google Maps marker in Flutter?

前端 未结 14 619
不知归路
不知归路 2020-12-07 12:32

I am using google_maps_flutter in my flutter app to use google map I have custom marker icon and I load this with BitmapDescriptor.fromAsset(\"images/car.

相关标签:
14条回答
  • 2020-12-07 13:17

    I have updated the function above, now you can scale the image as you like.

      Future<Uint8List> getBytesFromCanvas(int width, int height, urlAsset) async {
        final ui.PictureRecorder pictureRecorder = ui.PictureRecorder();
        final Canvas canvas = Canvas(pictureRecorder);
    
        final ByteData datai = await rootBundle.load(urlAsset);
        var imaged = await loadImage(new Uint8List.view(datai.buffer));
        canvas.drawImageRect(
          imaged,
          Rect.fromLTRB(
              0.0, 0.0, imaged.width.toDouble(), imaged.height.toDouble()),
          Rect.fromLTRB(0.0, 0.0, width.toDouble(), height.toDouble()),
          new Paint(),
        );
    
        final img = await pictureRecorder.endRecording().toImage(width, height);
        final data = await img.toByteData(format: ui.ImageByteFormat.png);
        return data.buffer.asUint8List();
      }
    
    0 讨论(0)
  • 2020-12-07 13:20

    Here's a May 2020 example of adding a custom Google Map marker.

    My example App:

    imports:

    import 'dart:typed_data';
    import 'dart:ui' as ui;
    import 'package:flutter/services.dart';
    import 'package:flutter/material.dart';
    

    Instantiate your map of markers somewhere in your main stateful class:

    Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
    

    Function to convert the icon asset into a Uint8List object (not convoluted at all /s):

    Future<Uint8List> getBytesFromAsset(String path, int width) async {
        ByteData data = await rootBundle.load(path);
        ui.Codec codec =
            await ui.instantiateImageCodec(data.buffer.asUint8List(), targetWidth: width);
        ui.FrameInfo fi = await codec.getNextFrame();
        return (await fi.image.toByteData(format: ui.ImageByteFormat.png)).buffer.asUint8List();
       }
    

    add marker function (call this with your latitude and longitude coordinates on where you want the markers)

      Future<void> _addMarker(tmp_lat, tmp_lng) async {
        var markerIdVal = _locationIndex.toString();
        final MarkerId markerId = MarkerId(markerIdVal);
        final Uint8List markerIcon = await getBytesFromAsset('assets/img/pin2.png', 100);
    
        // creating a new MARKER
        final Marker marker = Marker(
          icon: BitmapDescriptor.fromBytes(markerIcon),
          markerId: markerId,
          position: LatLng(tmp_lat, tmp_lng),
          infoWindow: InfoWindow(title: markerIdVal, snippet: 'boop'),
        );
    
        setState(() {
          // adding a new marker to map
          markers[markerId] = marker;
        });
      }
    

    pubspec.yaml (feel free to try out different icons)

    flutter:
    
      uses-material-design: true
    
      assets:
        - assets/img/pin1.png
        - assets/img/pin2.png
    
    0 讨论(0)
  • 2020-12-07 13:21

    So you can try the Ugly way . MediaQuery will return the ratio and check for conditions manually something Like so

     double mq = MediaQuery.of(context).devicePixelRatio;
     String icon = "images/car.png";
     if (mq>1.5 && mq<2.5) {icon = "images/car2.png";}
     else if(mq >= 2.5){icon = "images/car3.png";}
      mapController.addMarker(
        MarkerOptions(
           icon: BitmapDescriptor.fromAsset(icon),
           position: LatLng(37.4219999, -122.0862462),
         ),
       );
    

    you need to add your different assets images in your images folder like

    -images/car.png
    -images/car2.png
    -images/car3.png
    
    0 讨论(0)
  • 2020-12-07 13:22

    TL;DR: As long as are able to encode any image into raw bytes such as Uint8List, you should be fine using it as a marker.


    As of now, you can use Uint8List data to create your markers with Google Maps. That means that you can use raw data to paint whatever you want as a map marker, as long as you keep the right encode format (which in this particular scenario, is a png).

    I will go through two examples where you can either:

    1. Pick a local asset and dynamically change its size to whatever you want and render it on the map (a Flutter logo image);
    2. Draw some stuff in canvas and render it as marker as well, but this can be any render widget.

    Besides this, you can even transform a render widget in an static image and thus, use it as marker too.


    1. Using an asset

    First, create a method that handles the asset path and receives a size (this can be either the width, height, or both, but using only one will preserve ratio).

    import 'dart:ui' as ui;
    
    Future<Uint8List> getBytesFromAsset(String path, int width) async {
      ByteData data = await rootBundle.load(path);
      ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), targetWidth: width);
      ui.FrameInfo fi = await codec.getNextFrame();
      return (await fi.image.toByteData(format: ui.ImageByteFormat.png)).buffer.asUint8List();
    }
    

    Then, just add it to your map using the right descriptor:

    final Uint8List markerIcon = await getBytesFromAsset('assets/images/flutter.png', 100);
    final Marker marker = Marker(icon: BitmapDescriptor.fromBytes(markerIcon));
    

    This will produce the following for 50, 100 and 200 width respectively.


    2. Using canvas

    You can draw anything you want with canvas and then use it as a marker. The following will produce some simple rounded box with a Hello world! text in it.

    So, first just draw some stuff using the canvas:

    Future<Uint8List> getBytesFromCanvas(int width, int height) async {
      final ui.PictureRecorder pictureRecorder = ui.PictureRecorder();
      final Canvas canvas = Canvas(pictureRecorder);
      final Paint paint = Paint()..color = Colors.blue;
      final Radius radius = Radius.circular(20.0);
      canvas.drawRRect(
          RRect.fromRectAndCorners(
            Rect.fromLTWH(0.0, 0.0, width.toDouble(), height.toDouble()),
            topLeft: radius,
            topRight: radius,
            bottomLeft: radius,
            bottomRight: radius,
          ),
          paint);
      TextPainter painter = TextPainter(textDirection: TextDirection.ltr);
      painter.text = TextSpan(
        text: 'Hello world',
        style: TextStyle(fontSize: 25.0, color: Colors.white),
      );
      painter.layout();
      painter.paint(canvas, Offset((width * 0.5) - painter.width * 0.5, (height * 0.5) - painter.height * 0.5));
      final img = await pictureRecorder.endRecording().toImage(width, height);
      final data = await img.toByteData(format: ui.ImageByteFormat.png);
      return data.buffer.asUint8List();
    }
    

    and then use it the same way, but this time providing any data you want (eg. width and height) instead of the asset path.

    final Uint8List markerIcon = await getBytesFromCanvas(200, 100);
    final Marker marker = Marker(icon: BitmapDescriptor.fromBytes(markerIcon));
    

    and here you have it.

    0 讨论(0)
  • 2020-12-07 13:24

    I have the same problem and i solve this way.

    Future < Uint8List > getBytesFromCanvas(int width, int height, urlAsset) async 
    {
        final ui.PictureRecorder pictureRecorder = ui.PictureRecorder();
        final Canvas canvas = Canvas(pictureRecorder);
        final Paint paint = Paint()..color = Colors.transparent;
        final Radius radius = Radius.circular(20.0);
        canvas.drawRRect(
            RRect.fromRectAndCorners(
                Rect.fromLTWH(0.0, 0.0, width.toDouble(), height.toDouble()),
                topLeft: radius,
                topRight: radius,
                bottomLeft: radius,
                bottomRight: radius,
            ),
            paint);
    
        final ByteData datai = await rootBundle.load(urlAsset);
    
        var imaged = await loadImage(new Uint8List.view(datai.buffer));
    
        canvas.drawImage(imaged, new Offset(0, 0), new Paint());
    
        final img = await pictureRecorder.endRecording().toImage(width, height);
        final data = await img.toByteData(format: ui.ImageByteFormat.png);
        return data.buffer.asUint8List();
    }
    
    Future < ui.Image > loadImage(List < int > img) async {
        final Completer < ui.Image > completer = new Completer();
        ui.decodeImageFromList(img, (ui.Image img) {
    
            return completer.complete(img);
        });
        return completer.future;
    }
    

    And you can use like this.

    final Uint8List markerIcond = await getBytesFromCanvas(80, 98, urlAsset);
    
    setState(() {
    
        markersMap[markerId] = Marker(
            markerId: MarkerId("marker_${id}"),
            position: LatLng(double.parse(place.lat), double.parse(place.lng)),
    
            icon: BitmapDescriptor.fromBytes(markerIcond),
            onTap: () {
                _onMarkerTapped(placeRemote);
            },
    
        );
    });
    
    0 讨论(0)
  • 2020-12-07 13:24

    What worked for me to select the right image for different densities:

    MediaQueryData mediaQueryData = MediaQuery.of(context);
    ImageConfiguration imageConfig = ImageConfiguration(devicePixelRatio: mediaQueryData.devicePixelRatio);
    BitmapDescriptor.fromAssetImage(imageConfig, "assets/images/marker.png");
    
    0 讨论(0)
提交回复
热议问题