Создание пользовательских маркеров на Google Maps в приложениях Flutter

Когда дело доходит до отображения карт в приложении Flutter, есть два основных варианта. Вы можете либо использовать flutter_mapреализацию Leaflet для Flutter, которая будет работать с рядом бесплатных и платных поставщиков карт, либо использовать, google_maps_flutterесли хотите, более популярные Google Карты.

Я буду использовать этот пост в блоге google_maps_flutter, который создан и поддерживается командой Flutter в Google.

Если вы используете Google Карты впервые, я рекомендую проверить кодовую метку Google для карт в Flutter. Мне нужно было нарисовать несколько пользовательских маркеров, таких как те, что на изображении ниже.

Вы можете ожидать, что вы можете передать Widgetдля маркера, так же, как при использовании чего-либо еще во Флаттере. Но это не так просто, как Markerпринять BitmapDescriptorза иконку. Я собираюсь объяснить, как вы можете этого достичь.

Родной путь

Что это BitmapDescriptor? Если вы когда-либо работали с родным SDK Google Maps для iOS или Android, это вас не удивит.

BitmapDescriptorэто объект, который определяет растровое изображение, которое затем может быть использовано Google Maps для рисования на карте. Он используется на обеих платформах, и это единственный способ добавить пользовательские значки для маркеров. Итак, это ограничение исходит от родного SDK Google Maps, который google_maps_flutterиспользует, так как пока нет полной реализации трепетания для сложного виджета, такого как карты.

BitmapDescriptorпримет изображение актива или растровое изображение. В нашем случае текст маркера является динамическим, поэтому статическое изображение ресурса не подходит. Что касается растрового изображения, в нативном Android / iOS есть способ преобразовать ваше Viewв Bitmap(или UIViewв UIImage), и вы можете передать это BitmapDescriptor.

Путь флаттера

Вы можете сделать то же самое во Флаттере, но это немного сложнее. Главным образом потому, что вы не можете надуть / загрузить виджет где-то на стороне и преобразовать его в растровое изображение. Вам нужно нарисовать виджет в иерархии представления экрана и затем извлечь его нарисованное растровое изображение.

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

1. Получение RenderObject

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

final markerKey = GlobalKey();
return RepaintBoundary(
  key: markerKey,
  child: customMarkerWidget,
);

Теперь вы можете GlobalKeyиспользовать это после создания виджета:

markerKey.currentContext.findRenderObject();

2. Преобразование в Uint8List

Uint8Listв основном это список целых чисел без знака, который представляет растровое изображение в этом случае, так как в дротике нет специального растрового класса. BitmapDescriptorтакже будет принимать , Uint8Listтак что нам нужно сделать некоторые преобразования , чтобы получить от RenderObjectдо Uint8List. У меня есть все это в следующем методе:

Future<Uint8List> getUint8List(GlobalKey markerKey) async {
  RenderRepaintBoundary boundary =
  markerKey.currentContext.findRenderObject();
  var image = await boundary.toImage(pixelRatio: 2.0);
  ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
  return byteData.buffer.asUint8List();
}

3. Когда виджет закончил со сборкой?

Я показал вам, как получить RenderObjectи преобразовать его Uint8List, но когда вы вызываете этот метод?

Нам нужно получить наши растровые изображения сразу после их отрисовки. Есть несколько способов сделать это, я использую крошечные AfterLayoutMixin.

@override
void afterFirstLayout(BuildContext context) {
  getUint8List(markerKey).then((markerBitmap) =>
    // Set state and use your markerBitmap
  );
}

4. Создайте маркер

Там у вас есть это. Создайте Markerс Uint8Listи вставьте его в Google Map:

Marker(
    position: position,
    icon: BitmapDescriptor.fromBytes(markerUint8List)
)


Вы должны получить что-то вроде этого:

Я извлек все это в одном простом в использовании классе MarkerGenerator. Это немного более продвинуто, чем руководство, объясненное здесь. Он использует overlayмаркер для создания виджетов и обеспечивает List<Uint8List>обратный вызов. 

Скопируйте его в свой проект. Тогда вы используете MarkerGeneratorэтот способ:

@override  void initState() {    
    super.initState();     
    MarkerGenerator(markerWidgets, (bitmaps) {      
        setState(() {        
            markers = mapBitmapsToMarkers(bitmaps);      
        });    
    }).generate(context);  
}

Время летит, Флаттер спасает

При работе во Flutter некоторые вещи занимают больше времени, чем написание их на родном Android / iOS, как это произошло на этот раз с пользовательскими маркерами карты. Но в целом я считаю, что Flutter определенно экономит время и является отличным выбором для большинства приложений.

Контакты

+38 (093) 647-37-31

pavel.keepwarning@gmail.com

Ришельевская, 33, Одесса, Украина

Блог

Оставьте заявку
и мы Вам перезвоним