Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom markers with Flutter Google Maps plugin

Is there a way to display custom markers with the official Flutter Google Maps plugin on the map? Can't find any way to do it based on the documentation.

like image 544
Dmitry Buzolin Avatar asked Oct 01 '18 12:10

Dmitry Buzolin


People also ask

How do I change the icon size on Google Maps marker in flutter?

Using an asset final Uint8List markerIcon = await getBytesFromAsset('assets/images/flutter. png', 100); final Marker marker = Marker(icon: BitmapDescriptor. fromBytes(markerIcon)); This will produce the following for 50, 100 and 200 width respectively.


1 Answers

You can use Flutter widgets for custom markers with this class

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

/// This just adds overlay and builds [_MarkerHelper] on that overlay.
/// [_MarkerHelper] does all the heavy work of creating and getting bitmaps
class MarkerGenerator {
  final Function(List<Uint8List>) callback;
  final List<Widget> markerWidgets;

  MarkerGenerator(this.markerWidgets, this.callback);

  void generate(BuildContext context) {
    WidgetsBinding.instance
        .addPostFrameCallback((_) => afterFirstLayout(context));
  }

  void afterFirstLayout(BuildContext context) {
    addOverlay(context);
  }

  void addOverlay(BuildContext context) {
    OverlayState overlayState = Overlay.of(context);

    OverlayEntry entry;
    entry = OverlayEntry(
        builder: (context) {
          return _MarkerHelper(
            markerWidgets: markerWidgets,
            callback: (List<Uint8List> bitmapList) {
                // Given callback function
                callback.call(bitmapList);
                
                // Remove marker widget stack from Overlay when finished
                entry.remove();
              },
          );
        },
        maintainState: true);

    overlayState.insert(entry);
  }
}

/// Maps are embeding GoogleMap library for Andorid/iOS  into flutter.
///
/// These native libraries accept BitmapDescriptor for marker, which means that for custom markers
/// you need to draw view to bitmap and then send that to BitmapDescriptor.
///
/// Because of that Flutter also cannot accept Widget for marker, but you need draw it to bitmap and
/// that's what this widget does:
///
/// 1) It draws marker widget to tree
/// 2) After painted access the repaint boundary with global key and converts it to uInt8List
/// 3) Returns set of Uint8List (bitmaps) through callback
class _MarkerHelper extends StatefulWidget {
  final List<Widget> markerWidgets;
  final Function(List<Uint8List>) callback;

  const _MarkerHelper({Key key, this.markerWidgets, this.callback})
      : super(key: key);

  @override
  _MarkerHelperState createState() => _MarkerHelperState();
}

class _MarkerHelperState extends State<_MarkerHelper> with AfterLayoutMixin {
  List<GlobalKey> globalKeys = [];

  @override
  void afterFirstLayout(BuildContext context) {
    _getBitmaps(context).then((list) {
      widget.callback(list);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Transform.translate(
          offset: Offset(MediaQuery.of(context).size.width, 0),
          child: Material(
            type: MaterialType.transparency,
            child: Stack(
              children: widget.markerWidgets.map((i) {
                final markerKey = GlobalKey();
                globalKeys.add(markerKey);
                return RepaintBoundary(
                  key: markerKey,
                  child: i,
                );
              }).toList(),
            ),
          ),
    );
  }

  Future<List<Uint8List>> _getBitmaps(BuildContext context) async {
    var futures = globalKeys.map((key) => _getUint8List(key));
    return Future.wait(futures);
  }

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

/// AfterLayoutMixin
mixin AfterLayoutMixin<T extends StatefulWidget> on State<T> {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance
        .addPostFrameCallback((_) => afterFirstLayout(context));
  }

  void afterFirstLayout(BuildContext context);
}

usage example

import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:map_marker_sample/gist/Gist.dart';

class MapScreen extends StatefulWidget {
  @override
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  List<Marker> markers = [];

  @override
  Widget build(BuildContext context) {
    return Container(
        child: Stack(
          children: <Widget>[
            GoogleMap(
              initialCameraPosition:
                  CameraPosition(target: LatLng(45.811328, 15.975862), zoom: 8),
              markers: markers.toSet(),
            ),
          ],
        ),
    );
  }

  @override
  void initState() {
    super.initState();

    MarkerGenerator(markerWidgets(), (bitmaps) {
      setState(() {
        markers = mapBitmapsToMarkers(bitmaps);
      });
    }).generate(context);
  }

  List<Marker> mapBitmapsToMarkers(List<Uint8List> bitmaps) {
    List<Marker> markersList = [];
    bitmaps.asMap().forEach((i, bmp) {
      final city = cities[i];
      markersList.add(Marker(
          markerId: MarkerId(city.name),
          position: city.position,
          icon: BitmapDescriptor.fromBytes(bmp)));
    });
    return markersList;
  }
}

// Example of marker widget
Widget _getMarkerWidget(String name) {
  return Container(
    padding: EdgeInsets.symmetric(horizontal: 0, vertical: 0),
    child: Container(
      padding: EdgeInsets.symmetric(horizontal: 3, vertical: 1.5),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(10),
        border: Border.all(color: Colors.black, width: 1),
        color: Colors.white,
        shape: BoxShape.rectangle,
      ),
      child: Text(
        name,
        style: TextStyle(fontSize: 14, color: Colors.black),
      ),
    ),
  );
}

// Example of backing data
List<City> cities = [
  City("Zagreb", LatLng(45.792565, 15.995832)),
  City("Ljubljana", LatLng(46.037839, 14.513336)),
  City("Novo Mesto", LatLng(45.806132, 15.160768)),
  City("Varaždin", LatLng(46.302111, 16.338036)),
  City("Maribor", LatLng(46.546417, 15.642292)),
  City("Rijeka", LatLng(45.324289, 14.444480)),
  City("Karlovac", LatLng(45.489728, 15.551561)),
  City("Klagenfurt", LatLng(46.624124, 14.307974)),
  City("Graz", LatLng(47.060426, 15.442028)),
  City("Celje", LatLng(46.236738, 15.270346))
];

List<Widget> markerWidgets() {
  return cities.map((c) => _getMarkerWidget(c.name)).toList();
}

class City {
  final String name;
  final LatLng position;

  City(this.name, this.position);
}

source Link: https://infinum.com/the-capsized-eight/creating-custom-markers-on-google-maps-in-flutter-apps

github repository :https://github.com/itsJoKr/markers_generator_sample

like image 112
Bilal Şimşek Avatar answered Oct 02 '22 18:10

Bilal Şimşek