Я использую 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:
Как вы можете видеть на картинке, мой нестандартный размер значка слишком велик
@MrUpsidown, что мне делать с устройством с другим разрешением
На самом деле я сам создал PR (github.com/flutter/plugins/pull/815), чтобы предоставить способ использования байтов в качестве альтернативы, что сделало его гораздо более динамичным (BitmapDescriptor.fromBytes())
, но все еще не на ведущем, потому что iOS все еще отсутствует (я мог бы сделать это через несколько дней). На данный момент, я боюсь, у вас не так много вариантов, кроме как уменьшить размер вашего актива.
@MiguelRuivo, можете ли вы опубликовать ответ, используя свой способ?
@moonvader он уже был объединен, поэтому вы можете прямо сейчас использовать BMP из любого изображения, даже из виджета.
@MiguelRuivo, не могли бы вы опубликовать полный ответ на этот вопрос здесь? Как видите, вопрос довольно популярен, но пока не получил ответа
@moonvader хорошо, конечно. Если сможете, запомните меня к вечеру понедельника, и я отправлю его, потому что сейчас у меня нет доступа к моей машине.
@MiguelRuivo понедельник
@moonvader готово.
Я согласен с MrUpsidown. Вам действительно следует использовать изображения меньшего размера и избегать изменения их размера на лету. Пустая трата памяти, обработка ЦП и т. д.
fromAssets больше не рекомендуется
Так что вы можете попробовать Уродливый путь. 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 загрузит правильное изображение с правильным разрешением.
Не называйте переменные начиная с заглавной буквы. Просто не надо ».
@egorikem Хорошее замечание, пожалуйста, в следующий раз, когда вы заметите что-то подобное, отредактируйте вопрос или ответ.
Я нашел простой способ решить эту проблему:
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
).
Я рассмотрю два примера, в которых вы можете:
Помимо этого, вы даже можете преобразовать виджет рендеринга в статическое изображение и, таким образом, использовать его в качестве маркера.
Сначала создайте метод, который обрабатывает путь к активу и получает размер (это может быть ширина, высота или и то, и другое, но использование только одного значения сохранит соотношение).
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 соответственно.
Вы можете нарисовать на холсте все, что захотите, а затем использовать его как маркер. В результате будет получена простая рамка с закругленными углами с текстом 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));
и вот оно у вас.
спасибо, это потрясающе. В первом примере targetWidth не работает
Кто-нибудь может подсказать, как изменить ширину?
Вместо установки высоты вы можете установить ширину или даже обе.
Почему выдает ошибку, говоря, что Uint8List is not a subtype of Future<Uint8List>
Я пробовал выполнять кастинг, у меня не работает google_maps_flutter: ^0.5.19+2
Просто убедитесь, что вы ожидаете (иначе говоря, используя await
), когда вызываете метод.
Да, я использую ожидание,
Было бы лучше разместить ссылку на суть с вашим кодом, чтобы я мог вам помочь.
Это отличное решение, но в Google Maps для Flutter очень плохо реализованы маркеры. Посмотрите на версию React, компоненты или альтернативные плагины Map, они обрабатываются намного лучше (Marker - это просто виджет с lat, long; кого волнует, что это за дочерний элемент).
Просто используйте его таким же образом, но с виджетом, который содержит Column
, например, с Image
и Text
.
@MiguelRuivo Можно ли уменьшить размер значка при масштабировании? как убер
Да, но это придется делать самому. Просто динамически изменяйте размер в зависимости от текущего масштабирования.
какой дескриптор правильный? я не знаю, где мне нужно вставить последний Uint8List markerIcon = await getBytesFromAsset ('assets / images / flutter.png', 100); конечный маркер маркера = маркер (значок: BitmapDescriptor.fromBytes (markerIcon));
BitmapDescriptor.fromBytes()
Спасибо @MiguelRuivo, это очень информативно. Вы напрямую упомянули рендеринг из виджета; вы можете показать простой пример, как это сделать?
Вот моя попытка: git.io/Jv8LU, однако для этого требуется, чтобы виджет уже находился отдельно от дерева виджетов, что неудобно. В идеале нам понадобится такой метод, как: final Uint8List markerIcon = await getBytesFromWidget(widget, devicePixelRatio);
Виджет всегда должен быть предварительно обработан, чтобы получить свои байты, чтобы он действительно мог знать, как он выглядит (например, какие ограничения у него могут быть, которые могут повлиять на виджет). Я согласен, что это действительно было бы здорово, но я совершенно уверен, что на данный момент это невозможно. Это потребует фазы рисования / макета только для целей рендеринга.
Отличная работа @MiguelRuivo. Это действительно полезно!
Я пробовал использовать холст (№2), но, похоже, он не учитывает devicePixelRatio, а маркеры на моем физическом устройстве непропорционально меньше. Кто-нибудь еще сталкивается с этим и находит решение?
Спасибо за решение!
У меня такая же проблема, и я решаю так.
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
Я обновил функцию выше, теперь вы можете масштабировать изображение по своему усмотрению.
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
@NavinKumar извини, я понятия не имею о твоей проблеме. Мой код предназначен только для загрузки файлов png, будь это причина.
Что такое метод loadImage ()? Не определено
Вы должны написать его сами или можете скопировать его на этой странице.
Я добавлю решение, сочетающее несколько идей и кодов из любого места, чтобы исправить эту проблему, сначала функцию для управления размером изображения:
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 для размера.
@Alex Вы отправляли отчет об ошибке разработчикам Flutter Web? Должно.
@IanSmith ... в настоящее время это не реализовано в сети ..., но, возможно, они когда-нибудь добавят его
потрясающе, люблю это)
Поскольку 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
.
Это отлично сработало для меня.
Сохранить ваш PNG в меньшем размере?