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

Я использую google_maps_flutter в своем приложении 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: Как изменить размер значка маркера Google Maps во Flutter?

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

Сохранить ваш PNG в меньшем размере?

MrUpsidown 05.12.2018 17:08

@MrUpsidown, что мне делать с устройством с другим разрешением

Daniel.V 09.12.2018 13:11

На самом деле я сам создал PR (github.com/flutter/plugins/pull/815), чтобы предоставить способ использования байтов в качестве альтернативы, что сделало его гораздо более динамичным (BitmapDescriptor.fromBytes()), но все еще не на ведущем, потому что iOS все еще отсутствует (я мог бы сделать это через несколько дней). На данный момент, я боюсь, у вас не так много вариантов, кроме как уменьшить размер вашего актива.

Miguel Ruivo 15.12.2018 02:46

@MiguelRuivo, можете ли вы опубликовать ответ, используя свой способ?

moonvader 08.06.2019 14:32

@moonvader он уже был объединен, поэтому вы можете прямо сейчас использовать BMP из любого изображения, даже из виджета.

Miguel Ruivo 08.06.2019 15:17

@MiguelRuivo, не могли бы вы опубликовать полный ответ на этот вопрос здесь? Как видите, вопрос довольно популярен, но пока не получил ответа

moonvader 08.06.2019 21:18

@moonvader хорошо, конечно. Если сможете, запомните меня к вечеру понедельника, и я отправлю его, потому что сейчас у меня нет доступа к моей машине.

Miguel Ruivo 09.06.2019 04:09

@MiguelRuivo понедельник

moonvader 10.06.2019 21:03

@moonvader готово.

Miguel Ruivo 11.06.2019 02:17

Я согласен с MrUpsidown. Вам действительно следует использовать изображения меньшего размера и избегать изменения их размера на лету. Пустая трата памяти, обработка ЦП и т. д.

Jason 18.06.2019 15:52

fromAssets больше не рекомендуется

lacas 25.07.2019 09:02
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
53
11
47 096
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

Так что вы можете попробовать Уродливый путь. 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

Flutter делает это автоматически, см. Мой ответ. Если вы сохраните изображения в правильных папках, flutter загрузит правильное изображение с правильным разрешением.

Jason 18.06.2019 15:49

Не называйте переменные начиная с заглавной буквы. Просто не надо ».

egorikem 10.02.2020 13:45

@egorikem Хорошее замечание, пожалуйста, в следующий раз, когда вы заметите что-то подобное, отредактируйте вопрос или ответ.

Saed Nabil 11.02.2020 03:10

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

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

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

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

Например, базовое изображение следует масштабировать до нужного размера за пределами вашего приложения. Разные устройства имеют разное разрешение пикселей, поэтому трепетание подходит. Требуется другая версия вашего изображения, чтобы изображение не выглядело неровным. Увеличивайте изображение для разных разрешений. то есть базовая версия 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 Maps

Этот стандарт требует, чтобы изображения затем загружались с использованием AssetImage ('pathtoimages / image.png'), который не поддерживается плагином Google Maps. Карты 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);
    }

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

Попробуйте 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;
      });
    });
  }

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

MediaQueryData mediaQueryData = MediaQuery.of(context);
ImageConfiguration imageConfig = ImageConfiguration(devicePixelRatio: mediaQueryData.devicePixelRatio);
BitmapDescriptor.fromAssetImage(imageConfig, "assets/images/marker.png");
Ответ принят как подходящий

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. Использование холста

Вы можете нарисовать на холсте все, что захотите, а затем использовать его как маркер. В результате будет получена простая рамка с закругленными углами с текстом 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

спасибо, это потрясающе. В первом примере targetWidth не работает

otto 17.06.2019 23:03

Кто-нибудь может подсказать, как изменить ширину?

otto 23.06.2019 20:15

Вместо установки высоты вы можете установить ширину или даже обе.

Miguel Ruivo 23.06.2019 23:27

Почему выдает ошибку, говоря, что Uint8List is not a subtype of Future<Uint8List> Я пробовал выполнять кастинг, у меня не работает google_maps_flutter: ^0.5.19+2

Harsha Vardhan 17.07.2019 14:02

Просто убедитесь, что вы ожидаете (иначе говоря, используя await), когда вызываете метод.

Miguel Ruivo 18.07.2019 16:05

Да, я использую ожидание,

Harsha Vardhan 19.07.2019 08:01

Было бы лучше разместить ссылку на суть с вашим кодом, чтобы я мог вам помочь.

Miguel Ruivo 19.07.2019 12:20

Это отличное решение, но в Google Maps для Flutter очень плохо реализованы маркеры. Посмотрите на версию React, компоненты или альтернативные плагины Map, они обрабатываются намного лучше (Marker - это просто виджет с lat, long; кого волнует, что это за дочерний элемент).

Oliver Dixon 27.07.2019 09:26

Просто используйте его таким же образом, но с виджетом, который содержит Column, например, с Image и Text.

Miguel Ruivo 26.01.2020 13:20

@MiguelRuivo Можно ли уменьшить размер значка при масштабировании? как убер

BloodLoss 31.01.2020 06:24

Да, но это придется делать самому. Просто динамически изменяйте размер в зависимости от текущего масштабирования.

Miguel Ruivo 31.01.2020 11:04

какой дескриптор правильный? я не знаю, где мне нужно вставить последний Uint8List markerIcon = await getBytesFromAsset ('assets / images / flutter.png', 100); конечный маркер маркера = маркер (значок: BitmapDescriptor.fromBytes (markerIcon));

Pedro Molina 06.02.2020 14:37
BitmapDescriptor.fromBytes()
Miguel Ruivo 07.02.2020 01:38

Спасибо @MiguelRuivo, это очень информативно. Вы напрямую упомянули рендеринг из виджета; вы можете показать простой пример, как это сделать?

Ryan R 16.02.2020 04:35

Вот моя попытка: git.io/Jv8LU, однако для этого требуется, чтобы виджет уже находился отдельно от дерева виджетов, что неудобно. В идеале нам понадобится такой метод, как: final Uint8List markerIcon = await getBytesFromWidget(widget, devicePixelRatio);

Ryan R 16.02.2020 04:41

Виджет всегда должен быть предварительно обработан, чтобы получить свои байты, чтобы он действительно мог знать, как он выглядит (например, какие ограничения у него могут быть, которые могут повлиять на виджет). Я согласен, что это действительно было бы здорово, но я совершенно уверен, что на данный момент это невозможно. Это потребует фазы рисования / макета только для целей рендеринга.

Miguel Ruivo 18.02.2020 04:04

Отличная работа @MiguelRuivo. Это действительно полезно!

Thiyraash David 24.08.2020 04:46

Я пробовал использовать холст (№2), но, похоже, он не учитывает devicePixelRatio, а маркеры на моем физическом устройстве непропорционально меньше. Кто-нибудь еще сталкивается с этим и находит решение?

manafire 13.01.2021 14:21

Спасибо за решение!

tmsbrndz 07.12.2021 12:02

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

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

    );
});

если вы хотите масштабировать, посмотрите ответ от @xuetongqin

otto 23.06.2019 20:44

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

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

Ошибка loadImage: ссылка на метод загрузки изображения. gist.github.com/netsmertia/9c588f23391c781fa1eb791f0dce0768

Navin Kumar 07.08.2019 10:05

@NavinKumar извини, я понятия не имею о твоей проблеме. Мой код предназначен только для загрузки файлов png, будь это причина.

xuetongqin 08.08.2019 11:37

Что такое метод loadImage ()? Не определено

Ma250 06.09.2019 22:20

Вы должны написать его сами или можете скопировать его на этой странице.

xuetongqin 25.09.2019 11:11

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

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

Это все.

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

Я использовал версию ниже для реализации карты 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;
}

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

Вот пример добавления пользовательского маркера Google Map в мае 2020 года.

Мой пример приложения:

импорт:

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

Создайте экземпляр своей карты маркеров где-нибудь в своем основном классе с отслеживанием состояния:

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

Функция для преобразования ресурса значка в объект Uint8List (совсем не свернутый / с):

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

добавить функцию маркера (вызовите ее, указав координаты широты и долготы в том месте, где вы хотите установить маркеры)

  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 (попробуйте разные значки)

flutter:

  uses-material-design: true

  assets:
    - assets/img/pin1.png
    - assets/img/pin2.png

Спасибо! Я боролся с правильным размером маркеров. В настоящее время, похоже, это работает. Использование screenWidth / 3.2 для размера.

Karel Debedts 20.07.2020 09:48

@Alex Вы отправляли отчет об ошибке разработчикам Flutter Web? Должно.

Ian Smith 20.10.2020 22:32

@IanSmith ... в настоящее время это не реализовано в сети ..., но, возможно, они когда-нибудь добавят его

Alex 20.10.2020 23:20

потрясающе, люблю это)

SardorbekR 17.11.2020 19:30

Поскольку google_map_flutter 0.5.26, fromAsset() устарел и должен быть заменен на fromAssetImage(), как упоминалось в некоторых других ответах. Более элегантный способ применить fromAssetImage() для устройств с разным разрешением - объявить активы изображения с учетом разрешения. Идея состоит в том, что Flutter отображает экраны с использованием логического пикселя, который составляет около 72 пикселей на дюйм, если я правильно помню, в то время как современные мобильные устройства могут содержать более 200 пикселей на дюйм. И решение сделать изображение похожим по размеру на разных мобильных устройствах с разной плотностью пикселей состоит в том, чтобы подготовить несколько копий одного и того же изображения разного размера, где на устройстве с более низкой плотностью пикселей используется меньшее изображение, а на устройстве с более высокой плотностью пикселей. используется изображение большего размера.

Поэтому вам следует подготовить, например, следующие изображения

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

и измените свой код, как показано ниже, где createLocalImageConfiguration() применит правильный масштаб в соответствии с devicePixelRatio

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

Ниже представлена ​​реализация fromAssetImage() последней версии google_map_flutter 1.0.3. Вы можете видеть, что базовая реализация BitmapDescriptor принимает аргумент scale, который является ключом к получению правильного размера изображения.

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

ПРИМЕЧАНИЕ. Вы можете видеть, что свойство size ImageConfiguration работает только для Интернета.

Все приведенные ответы идеальны, но я заметил, что когда вы устанавливаете targetWidth на определенное число, у вас могут возникнуть проблемы с разными телефонами, у которых другой devicePixelRatio. Вот как я это реализовал.

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


  Future<Uint8List> getBytesFromAsset(String path) async {
    double pixelRatio = MediaQuery.of(context).devicePixelRatio;
    ByteData data = await rootBundle.load(path);
    ui.Codec codec = await ui.instantiateImageCodec(
        data.buffer.asUint8List(),
        targetWidth: pixelRatio.round() * 30
    );
    ui.FrameInfo fi = await codec.getNextFrame();
    return (await fi.image.toByteData(format: ui.ImageByteFormat.png)).buffer.asUint8List();
  }

и используйте такой метод

final Uint8List markerIcon = await getBytesFromAsset('assets/images/bike.png');

Marker(icon: BitmapDescriptor.fromBytes(markerIcon),)

Это дает мне динамический размер в зависимости от devicePixelRatio.

Это отлично сработало для меня.

Другие вопросы по теме