Siguiendo con CodeLab, podrás agregar Google Maps a tu aplicación Flutter en menos de una hora. En realidad, es bastante loco lo fácil que es.
Configure sus claves de API, agregue el widget GoogleMap a su árbol de widgets y disfrute de la misma experiencia de exploración de mapas que disfruta normalmente con la aplicación Google Maps.
Después de familiarizarse con el google_maps_flutter paquete sin embargo, inevitablemente querrá personalizar sus marcadores de mapa y darse cuenta de que las soluciones disponibles son bastante limitadas.
Usar activos de imagen.
- Crear un BitmapDescriptor a partir de un activo de imagen.
- Crear un marcador personalizado a partir de un activo de imagen.
Usar imágenes de red.
Convierta los widgets en iconos.
- Creación de marcadores personalizados en Google Maps en aplicaciones de Flutter.
- Flutter Widget to Image (básicamente la documentación oficial en forma de artículo).
Como puede ver, una buena parte de las soluciones propuestas en Internet requieren que tenga una imagen existente en su carpeta de activos. Si es así, tome estas soluciones y ejecútelas . Si no lo hace, puede adquirir algunos o buscar otra forma.
La alternativa que prefiero consiste en transformar los widgets de Flutter en imágenes y luego en BitmapDescriptors
(marcadores de mapa). Los enlaces en la última sección anterior tienen más detalles sobre este proceso.
Índice
Objetivo
Mi objetivo general es enseñarle cómo utilizar cualquier widget personalizado como marcador de mapa.
Mi objetivo específico para este artículo es tomar iconos que existen en el paquete Material y utilícelos como marcadores de mapa.
No busque iconos «gratuitos» en Internet. No intente aprender los entresijos de Inkscape para que pueda crear sus propios iconos (no es una idea terrible). Simplemente tome los íconos que tiene a mano y colóquelos en el mapa.
El proceso
La clase Marcador de Google Maps tiene un parámetro de icono que determina qué icono se utiliza cuando se dibuja en la pantalla. Sin embargo, el parámetro de icono no toma un tipo de datos de icono real.
En cambio, se necesita un BitmapDescriptor que es exclusivo del paquete de Google Maps y define una imagen de mapa de bits.
Los BitmapDescriptors
se pueden crear de dos formas: desde un activo o desde bytes
. En este ejemplo usaremos bytes
.
BitmapDescriptor markerIcon = BitmapDescriptor.fromBytes(imageData);
En esta línea de código, imageData
debe tener un Uint8List
tipo de datos, que es simplemente una «lista de longitud fija de enteros de 8 bits sin signo» que describe la imagen / mapa de bits.
Con esto en mente, necesitamos pasar de un widget de Flutter a la clase Uint8List
.
En resumen, el proceso se verá así:
El primer paso en el proceso es crear el widget que eventualmente mostrará en el mapa. Puede hacer esto de la misma manera que crearía cualquier otro widget.
Como estoy tratando de agregar un ícono de material al mapa, mi widget es este:
IconButton( icon: Icon(Icons.star), onPressed: () // Do something )
El problema de todo este proceso es que el widget en realidad necesita ser dibujado en la pantalla para que exista RenderObject
.
En otras palabras, si intenta crear un widget en su función WidgetToImage ()
y usa su RenderObject
, obtendrá una excepción de puntero nulo.
Es poco probable que desee mostrar el widget de marcador en otro lugar de la pantalla además del mapa, así que … tendrá que ocultarlo.
Opción 1: el widget de pila
Literalmente, tome sus widgets de marcadores y colóquelos detrás de todo lo demás.
return Scaffold( key: searchScaffoldKey, extendBody: true, floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, bottomNavigationBar: BottomAppBar(...). body: Stack( children: [ MarkerIcons(), // Whatever comes first is drawn first MainBody() ], ), ); }
Opción 2 – Traducir el widget fuera de la pantalla
Puedes usar el widget Transform
para mover el widget antes de pintar. En el siguiente ejemplo, agregué el marcador a una barra de navegación inferior y lo moví hacia abajo 200 píxeles.
Transform.translate( offset: Offset(0,200), child: RepaintBoundary( key: iconKey, child: IconButton(icon: Icon(Icons.star), onPressed: () // Do something ), ), ),
Opción 3: crear un widget sin necesidad de mostrarlo
Puede que esto no sea posible, pero si sabe cómo hacerlo, responda esto Publicación de desbordamiento de pila.
Cosas que no funcionan
Intentando ocultar el widget usando el Opacity, Visibility o Offstage-class, los widgets no funcionarán ya que RenderBox
no existirá. Créeme. Lo intenté.
El segundo paso es obtener una RenderObject
desde nuestro Widget. RenderObjects
son creados indirectamente por Widgets, por lo que este paso no es tan complicado.
Lo que tenemos que hacer es guardar una referencia a este RenderObject
(la imagen que se muestra en la pantalla) para que podamos reutilizarlo en el siguiente paso.
Podemos hacer esto envolviendo nuestro widget en un widget RepaintBoundary
. De los documentos, Repaint «Crea un widget que aísla los repintados» , por lo que, en última instancia, solo tenemos un widget y su objeto RenderObject
correspondiente.
Entonces, donde sea que decida ocultar su widget, envuélvalo en un Límite de repintado y etiquételo con una GlobalKey:
GlobalKey iconKey = GlobalKey(); ...RepaintBoundary( key: iconKey, child: IconButton(icon: Icon(Icons.star), onPressed: () // Do something ), ),
En el siguiente paso, conectaremos el RenderObject
en un objeto RenderRepaintBoundary
(no un Widget) para obtener un límite de repintado real.
Esto guardará efectivamente la salida visual del widget secundario como una capa independiente en la interfaz de usuario que no necesita ser redibujada cada vez que se pinta la pantalla.
Puede encontrar más información sobre el widget RepaintBoundary
en este vídeo.
Crea un límite RenderRepaintBoundary
de repintado alrededor de RenderBox / RenderObject
.
Esta clase también es importante para nosotros porque tiene un método to image()
que puede transformar el widget en pantalla en una imagen Flutter.
RenderRepaintBoundary boundary = iconKey.currentContext.findRenderObject();
Este paso es rápido. Simplemente llame al método toImage()
.
ui.Image image = await boundary.toImage(pixelRatio: 3.0);
Otro paso rápido:
ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
Y finalmente:
var pngBytes = byteData.buffer.asUint8List();
El resultado
Al final, deberá agregar estos tres componentes a su aplicación.
1. Una GlobalKey ()
para cada marcador diferente que desee utilizar.
// Map markers GlobalKey iconKey = GlobalKey();
2. El widget de marcador (oculto).
Widget MarkerIcons() return Stack( children: [ RepaintBoundary( key: iconKey, child: IconButton(icon: Icon(Icons.star), onPressed: () // Do something ), ), ], );
3. La función Future para convertir su widget en un Uint8List
.
Future<BitmapDescriptor> getCustomIcon(GlobalKey iconKey) async { Future<Uint8List> _capturePng(GlobalKey iconKey) async { try { print('inside'); RenderRepaintBoundary boundary = iconKey.currentContext.findRenderObject(); ui.Image image = await boundary.toImage(pixelRatio: 3.0); ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png); var pngBytes = byteData.buffer.asUint8List(); print(pngBytes); return pngBytes; } catch (e) { print(e); } } Uint8List imageData = await _capturePng(iconKey); log("testIcon set"); return BitmapDescriptor.fromBytes(imageData); }
Puede llamar a esta función desde dentro de la función que busca sus marcadores.
BitmapDescriptor testIcon = await getCustomIcon(iconKey);
El icono se puede asignar al marcador mediante el parámetro de icono:
final marker = Marker( markerId: MarkerId(sighting.id), icon: testIcon, position: LatLng(thisSighting.latitude, thisSighting.longitude), infoWindow: InfoWindow( title: thisSighting.primaryType, snippet: thisSighting.description, onTap: () // Do something , ), );
Alternativa
Si todo esto parece demasiado complicado o si está buscando una solución alternativa, encontré geoapify.com
.
Usando su sitio web, puede crear marcadores de mapa personalizados usando los conjuntos de iconos Material
y FontAwesome
y luego descargar o acceder a los resultados de la imagen a través de su API. Es una idea genial y es gratis.
Espero que te haya sido útil. Gracias por leer este artículo.
Añadir comentario