Как изменить размер значка маркера Google Maps во флаттере?

Я использую google_maps_flutter в своем приложении флаттера, чтобы использовать карту Google. У меня есть пользовательский значок маркера, и я загружаю его с помощью BitmapDescriptor.fromAsset("images/car.png") однако размер моего значка на карте слишком велик, я хочу сделать его меньше, но Я не мог найти никакой опции для этого есть какая-либо опция, чтобы изменить пользовательский значок маркера. вот мой код трепетания:

mapController.addMarker(
        MarkerOptions(
          icon: BitmapDescriptor.fromAsset("images/car.png"),

          position: LatLng(
            deviceLocations[i]['latitude'],
            deviceLocations[i]['longitude'],
          ),
        ),
      );

И вот скриншот моего эмулятора Android:

Как вы можете видеть на картинке, мой размер иконки слишком велик

Ответ 1

TL; DR: Пока вы можете кодировать любое изображение в необработанные байты, такие как Uint8List, вы можете использовать его в качестве маркера.


На данный момент вы можете использовать данные Uint8List для создания маркеров с помощью Google Maps. Это означает, что вы можете использовать необработанные данные для рисования того, что вы хотите, в качестве маркера карты, если вы сохраняете правильный формат кодирования (который в данном конкретном сценарии является png).

Я рассмотрю два примера, где вы можете:

  1. Выберите локальный ресурс и динамически измените его размер на любой, какой вы пожелаете, и отобразите его на карте (изображение логотипа Flutter);
  2. Нарисуйте некоторые вещи на холсте и визуализируйте их как маркер, но это может быть любой виджет визуализации.

Кроме того, вы даже можете преобразовать виджет рендеринга в статическое изображение и, таким образом, использовать его как маркер.


1. Использование актива

Сначала создайте метод, который обрабатывает путь к ресурсу и получает размер (это может быть либо ширина, высота, либо и то и другое, но использование только одного из них сохранит соотношение).

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();
}

Затем просто добавьте его на карту, используя правильный дескриптор:

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

Это даст следующие значения для ширины 50, 100 и 200 соответственно.

asset_example


2. Использование холста

Вы можете нарисовать все что угодно с помощью canvas, а затем использовать это как маркер. Далее будет получено простое округленное поле с текстом Hello world!.

Итак, сначала нарисуйте некоторые вещи, используя холст:

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();
}

а затем используйте его тем же способом, но на этот раз предоставьте любые нужные вам данные (например, ширину и высоту) вместо пути ресурса.

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

и вот оно у вас.

canvas_example

Ответ 2

Я обновил функцию выше, теперь вы можете масштабировать изображение, как вам нравится.

  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();
  }

Ответ 3

У меня та же проблема, и я решаю этот путь.

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;
}

И вы можете использовать как это.

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);
        },

    );
});

Ответ 4

Что сработало для меня, чтобы выбрать правильное изображение для разных плотностей:

MediaQueryData mediaQueryData = MediaQuery.of(context);
ImageConfiguration imageConfig = ImageConfiguration(devicePixelRatio: mediaQueryData.devicePixelRatio);
BitmapDescriptor.fromAssetImage(imageConfig, "assets/images/marker.png");

Ответ 5

Таким образом, вы можете попробовать Гадкий путь. MediaQuery вернет соотношение и проверит условия вручную что-то вроде

 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),
     ),
   );

вам нужно добавить свои различные активы изображения в папке изображений, как

-images/car.png
-images/car2.png
-images/car3.png

Ответ 6

Попробуйте BitmapDescriptor.fromAssetImage. Он также будет игнорировать размер изображения.

BitmapDescriptor.fromAssetImage(
            ImageConfiguration(size: Size(32, 32)), 'assets/car.png')
        .then((onValue) {
      setState(() {
        markerIcon = onValue;
      });
    });

Также использование конфигурации по умолчанию не удается.

loadMarkerImage(BuildContext context) {
    var config = createLocalImageConfiguration(context, size: Size(30, 30));
    BitmapDescriptor.fromAssetImage(config, 'assets/car.png')
        .then((onValue) {
      setState(() {
        markerIcon = onValue;
      });
    });
  }

Ответ 7

Я добавлю решение, сочетающее несколько идей и кодов из любого места, чтобы решить эту проблему, сначала функцию для управления размером изображения:

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;
}

Затем примените эту функцию в зависимости от устройства IOS или Android. Функция getBytesFromCanvas() принимает два параметра: реальный размер изображения и 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);
});

Это все.

Ответ 8

Я нашел самый простой способ решить эту проблему.

Я использовал ниже версию для реализации карты Google. В нижней версии карты Google BitmapDescriptor.fromBytes не работает.

 google_maps_flutter: ^0.5.19

И установите маркеры как

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',
   ),
  ));

}

И если вы хотите изменить размер значка, используйте приведенный ниже код.

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;
}

enter image description here

Надеюсь, это будет работать для вас.. !!

Ответ 9

Простой способ, который я нашел, чтобы решить это просто

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');
} 

Проще говоря, поставьте андроид больший актив.

Ответ 10

BitmapDescriptor.fromAsset() - это правильный способ добавления маркеров с одной открытой ошибкой, которая влияет на ваш код. Как ответил Саед, вам нужно предоставить разные размеры изображения для разных плотностей экрана устройства. Исходя из предоставленного вами изображения, я бы предположил, что базовый размер требуемого изображения будет около 48 пикселей. Таким образом, вам нужно будет делать копии размером 48, 96 (2.0x) и 144 (3.0x).

Время выполнения должно выбрать правильное в зависимости от плотности экрана. См. Https://flutter.dev/docs/development/ui/assets-and-images#declaring-resolution-aware-image-assets.

На данный момент это не выполняется автоматически на Android или Fuschia. Если вы выпускаете сейчас и хотите обойти это, вы можете проверить платформу, используя следующую логику:

    MediaQueryData data = MediaQuery.of(context);
    double ratio = data.devicePixelRatio;

    bool isIOS = Theme.of(context).platform == TargetPlatform.iOS;

Если платформа не iOS, вы бы внедрили в свой код сегменты. Объединяя логику в один метод:

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';
}

Затем вы можете создать маркер для значка с именем person_icon в каталоге ресурсов ** assets/map_icons/** с помощью этого кода, используя метод:

            myLocationMarker = Marker(
            markerId: MarkerId('myLocation'),
            position: showingLocation, flat: true,
            icon: BitmapDescriptor.fromAsset(imageDir('assets/map_icons','person_icon.png', ratio, isIos)));

Ответ 11

вам нужно сделать разные размеры для ваших иконок и сделать папку с изображениями, например,

-images/car.png
-images/2.0x/car.png
-images/3.0x/car.png

Flutter автоматически выберет "правильный" актив в зависимости от соотношения пикселей устройства

проверьте здесь

Ответ 12

Следует избегать больших изображений, так как они занимают ненужное пространство. Изображения должны быть масштабированы для вашей карты, с вариациями разрешения в пикселях, чтобы соответствовать устройству.

Например, базовое изображение должно быть масштабировано до правильного размера за пределами вашего приложения. Разные устройства имеют разное разрешение пикселей, которое развевается. Требуется другая версия вашего изображения, чтобы изображение не казалось неровным. Увеличьте изображение для разных разрешений. то есть базовая версия 32x32 пикселей, версия 2.0 будет 64x64 пикселей, версия 3.0 будет 128x128 и т.д. См. описанный ниже стандартный способ флаттера, который учитывает различные разрешения пикселей в зависимости от производителя устройства.

BitmapDescriptor.fromAsset не поддерживает автоматическое декодирование разрешения в пикселях и будет загружать файл, указанный в пути. Чтобы исправить это, вызовите AssetImage для декодирования правильного имени файла.

Есть ошибка с рендерингом изображений, изображения в iOS выглядят больше, чем в Android, см. Дефект 24865. Для этого тоже есть обходной путь, путем жесткого кодирования имени файла разрешения, которое вы бы предпочли.

В следующих разделах описывается стандартный способ флаттера, обходной путь AssetImage и обходной путь 24865.

Стандартные соглашения об именах изображений Flutter

Создайте папку ресурсов с соглашением об именах:

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

Где M и N - разрешения (2.0x) или темы (темные). Затем добавьте изображение или все изображения в файл pubspec.file либо

flutter:
  assets:
    - pathtoimages/image.png

или же

flutter:
  assets:
    - pathtoimages/

Обходной путь для Карт Google

Этот стандарт требует, чтобы изображения затем загружались с использованием AssetImage ('pathtoimages/image.png'), которое не поддерживается плагином карт Google. Карты Google требуют, чтобы вы использовали BitmapDescriptor.fromAsset('pathtoimages/image.png'), который в настоящее время не разрешает правильное изображение. Чтобы исправить это использование, вы можете получить правильное изображение из AssetImage, сначала создавая createLocalImageConfiguration, используя BuildContext, как определено здесь. Затем используйте эту конфигурацию для разрешения правильного изображения следующим образом:

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

Дефект 24865 Обходной путь

 BitmapDescriptor get deliveryIcon {
      bool isIOS = Theme.of(context).platform == TargetPlatform.iOS;
          If (isIOS)
              return BitmapDescriptor.fromAsset('pathtoimages/image.png');
         else
              return BitmapDescriptor.fromAsset(
              resolvedImageName);
    }