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

前端 未结 14 618
不知归路
不知归路 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:09

    A simple way I found to solve this is simply

    BitmapDescriptor get deliveryIcon {
      bool isIOS = Theme.of(context).platform == TargetPlatform.iOS;
      if (isIOS)
        return BitmapDescriptor.fromAsset('assets/icons/orange_pin.png');
      else
        return BitmapDescriptor.fromAsset(
            'assets/icons/3.0x/orange_pin.png');
    } 
    

    Simply put, supply the android the larger asset.

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

    I will add a solution mixing severals ideas and codes from anywhere to fix this problem, first a function to manage image size:

    Future<Uint8List> getBytesFromCanvas(double escala, 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));
    
      double width = ((imaged.width.toDouble() * escala).toInt()).toDouble();
      double height = ((imaged.height.toDouble() * escala).toInt()).toDouble();
    
      canvas.drawImageRect(imaged, Rect.fromLTRB(0.0, 0.0, imaged.width.toDouble(), imaged.height.toDouble()),
                                  Rect.fromLTRB(0.0, 0.0, width, height),
                                  new Paint(),
      );
    
      final img = await pictureRecorder.endRecording().toImage(width.toInt(), height.toInt());
      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;
    }
    

    Then apply this function depending on the device IOS or Android. The getBytesFromCanvas() function take two parameters, scale of image real size and asset url.

    var iconTour;
    
    bool isIOS = Theme.of(context).platform == TargetPlatform.iOS;
    if (isIOS){
    
      final markerIcon = await getBytesFromCanvas(0.7, 'images/Icon.png');
      iconTour = BitmapDescriptor.fromBytes(markerIcon);
    
    }
    else{
    
      final markerIcon = await getBytesFromCanvas(1, 'images/Icon.png');
      iconTour = BitmapDescriptor.fromBytes(markerIcon);
    
    }
    
    setState(() {
      final Marker marker = Marker(icon: iconTour);
    });
    

    Thats all.

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

    Since google_map_flutter 0.5.26, fromAsset() is deprecated and should be replaced with fromAssetImage() as some other answers mentioned. A more elegant way to apply fromAssetImage() for different resolution devices is to declare resolution-aware image assets. The idea is that Flutter renders screens using logical pixel, which is around 72px per inch if I remember correctly, while modern mobile devices could contain more than 200px per inch. And the solution to make a image looks similar in size on different mobile devices with different pixel density is to prepare multiple copy of the same image in different size, where on lower pixel density device the smaller image is used, and on higher pixel density device the bigger image is used.

    So you should prepare for example the following images

    images/car.png           <-- if this base image is 100x100px
    images/2.0x/car.png      <-- 2.0x one should be 200x200px
    images/3.0x/car.png      <-- and 3.0x one should be 300x300px
    

    and modify your code as below, where createLocalImageConfiguration() will apply the correct scale according to devicePixelRatio

    mapController.addMarker(
            MarkerOptions(
              icon: BitmapDescriptor.fromAssetImage(
                      createLocalImageConfiguration(context),
                      "images/car.png"),
              position: LatLng(
                deviceLocations[i]['latitude'],
                deviceLocations[i]['longitude'],
              ),
            ),
          );
    

    Below is the implementation of fromAssetImage() of the latest google_map_flutter 1.0.3. You can see that the underlying implementation of BitmapDescriptor takes an argument scale, which is the key to getting the right size of image.

      static Future<BitmapDescriptor> fromAssetImage(
        ImageConfiguration configuration,
        String assetName, {
        AssetBundle bundle,
        String package,
        bool mipmaps = true,
      }) async {
        if (!mipmaps && configuration.devicePixelRatio != null) {
          return BitmapDescriptor._(<dynamic>[
            'fromAssetImage',
            assetName,
            configuration.devicePixelRatio,
          ]);
        }
        final AssetImage assetImage =
            AssetImage(assetName, package: package, bundle: bundle);
        final AssetBundleImageKey assetBundleImageKey =
            await assetImage.obtainKey(configuration);
        return BitmapDescriptor._(<dynamic>[
          'fromAssetImage',
          assetBundleImageKey.name,
          assetBundleImageKey.scale,
          if (kIsWeb && configuration?.size != null)
            [
              configuration.size.width,
              configuration.size.height,
            ],
        ]);
      }
    

    NOTE: You can see that the size property of the ImageConfiguration only works for web.

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

    BitmapDescriptor.fromAsset() is the correct way to add markers, with one open bug that affects your code. As Saed answered, you need to provide different sizes of the image for different device screen densities. From the image you provided, I would guess the base size for the image you want would be about 48 pixels. So you would need to make copies of sizes, 48, 96 (2.0x), and 144 (3.0x).

    The runtime should select the correct one depending on screen density. See https://flutter.dev/docs/development/ui/assets-and-images#declaring-resolution-aware-image-assets.

    This is not done automatically on Android or Fuschia at the moment. If you are releasing now and want to work around this, you can check the platform using the following logic:

        MediaQueryData data = MediaQuery.of(context);
        double ratio = data.devicePixelRatio;
    
        bool isIOS = Theme.of(context).platform == TargetPlatform.iOS;
    

    If the platform is not iOS, you would implement the buckets in your code. Combining the logic into one method:

    String imageDir(String prefix, String fileName, double pixelRatio, bool isIOS) {
        String directory = '/';
        if (!isIOS) {
            if (pixelRatio >= 1.5) {
                directory = '/2.0x/';
            }
            else if (pixelRatio >= 2.5) {
                directory = '/3.0x/';
            }
            else if (pixelRatio >= 3.5) {
                directory = '/4.0x/';
            }
        }
        return '$prefix$directory$fileName';
    }
    

    You could then create a marker for an icon named person_icon in the assets directory **assets/map_icons/**with this code, using the method:

                myLocationMarker = Marker(
                markerId: MarkerId('myLocation'),
                position: showingLocation, flat: true,
                icon: BitmapDescriptor.fromAsset(imageDir('assets/map_icons','person_icon.png', ratio, isIos)));
    
    0 讨论(0)
  • 2020-12-07 13:15

    Large images should be avoided, as they consume unnecessary space. Images should be scaled for your map, with variations of pixel resolution to cater for the device.

    For example the base image should be scaled to the correct size outside of your application. Different devices have different pixel resolutions, which flutter caters for. Different version of your image are required so that the image does not appear jagged. Scale up the image for different resolutions. i.e base version 32x32 pixels, version 2.0 will be 64x64 pixels, version 3.0 will be 128x128 etc. See the standard flutter way described below, which caters for different pixel resolutions, dependent on the device manufacturer.

    BitmapDescriptor.fromAsset does not support the automatic decoding of pixel resolution, and will load the file specified in the path. To correct this call AssetImage to decode the correct filename.

    There is a bug with the rendering of images, images in iOS look bigger than Android, see defect 24865. There is a workaround for this too, by hardcoding the file name of the resolution you would prefer.

    The following sections outline the standard flutter way, the AssetImage workaround, and the 24865 workaround.

    Standard Flutter image naming conventions

    Create an asset folder with the naming convention:

    pathtoimages/image.png
    pathtoimages/Mx/image.png
    pathtoimages/Nx/image.png
    pathtoimages/etc.
    

    Where M and N are resolutions (2.0x) or themes (dark). Then add the image or all the images to the pubspec.file as either

    flutter:
      assets:
        - pathtoimages/image.png
    

    or

    flutter:
      assets:
        - pathtoimages/
    

    Workaround for Google Maps

    This standard requires that images are then loaded using AssetImage('pathtoimages/image.png') which is not supported by the google maps plugin. Google maps requires that you use BitmapDescriptor.fromAsset('pathtoimages/image.png'), which at this time does not resolve to the correct image. To fix this use you can get the correct image from AssetImage by first createLocalImageConfiguration using the BuildContext as defined here. Then use this configuration to resolve the correct image as follows:

    ImageConfiguration config = createLocalImageConfiguration(context);
    AssetImage('pathtoimages/image.png')
       .obtainKey(config)
       .then((resolvedImage) {
           print('Name: ' + resolvedImage.onValue.name);
        });
    

    Defect 24865 workaround

     BitmapDescriptor get deliveryIcon {
          bool isIOS = Theme.of(context).platform == TargetPlatform.iOS;
              If (isIOS)
                  return BitmapDescriptor.fromAsset('pathtoimages/image.png');
             else
                  return BitmapDescriptor.fromAsset(
                  resolvedImageName);
        }
    
    0 讨论(0)
  • 2020-12-07 13:16

    I found simplest way to solve this issue.

    I used below version for google map implementation. In lower version of google map BitmapDescriptor.fromBytes not working.

     google_maps_flutter: ^0.5.19
    

    And set marker points like

    Future setMarkersPoint() async {
      var icon = 'your url';
      Uint8List dataBytes;
      var request = await http.get(icon);
      var bytes = await request.bodyBytes;
    
      setState(() {
        dataBytes = bytes;
      });
    
      final Uint8List markerIcoenter code heren =
          await getBytesFromCanvas(150, 150, dataBytes);
    
      var myLatLong = LatLng(double.parse(-6.9024812),
          double.parse(107.61881));
    
      _markers.add(Marker(
        markerId: MarkerId(myLatLong.toString()),
        icon: BitmapDescriptor.fromBytes(markerIcon),
        position: myLatLong,
       infoWindow: InfoWindow(
         title: 'Name of location',
        snippet: 'Marker Description',
       ),
      ));
    

    }

    And If you want to change icon size then use below code.

    Future<Uint8List> getBytesFromCanvas(
      int width, int height, Uint8List dataBytes) 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);
    
    var imaged = await loadImage(dataBytes.buffer.asUint8List());
    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();
     }
    
        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;
    }
    

    Hope It will work for you..!!

    0 讨论(0)
提交回复
热议问题