How to customize google maps marker icon in Flutter

16,891

Solution 1

enter image description here

To achieve the above styling fallow steps bellow:

  1. Import packages
import 'dart:async';
import 'dart:ui' as ui;
import 'dart:typed_data';
import 'dart:io';
import 'package:flutter/material.dart';
  1. (Optional) Add a helper function to get image by the path
Future<ui.Image> getImageFromPath(String imagePath) async {
    File imageFile = File(imagePath);

    Uint8List imageBytes = imageFile.readAsBytesSync();

    final Completer<ui.Image> completer = new Completer();

    ui.decodeImageFromList(imageBytes, (ui.Image img) {
      return completer.complete(img);
    });

    return completer.future;
}
  1. Add function getMarkerIcon() returning BitmapDescriptor. This is a replacement for your function getBytesFromCanvas()
Future<BitmapDescriptor> getMarkerIcon(String imagePath, Size size) async {
    final ui.PictureRecorder pictureRecorder = ui.PictureRecorder();
    final Canvas canvas = Canvas(pictureRecorder);

    final Radius radius = Radius.circular(size.width / 2);

    final Paint tagPaint = Paint()..color = Colors.blue;
    final double tagWidth = 40.0;

    final Paint shadowPaint = Paint()..color = Colors.blue.withAlpha(100);
    final double shadowWidth = 15.0;

    final Paint borderPaint = Paint()..color = Colors.white;
    final double borderWidth = 3.0;

    final double imageOffset = shadowWidth + borderWidth;

    // Add shadow circle
    canvas.drawRRect(
        RRect.fromRectAndCorners(
          Rect.fromLTWH(
              0.0,
              0.0,
              size.width,
              size.height
          ),
          topLeft: radius,
          topRight: radius,
          bottomLeft: radius,
          bottomRight: radius,
        ),
        shadowPaint);

    // Add border circle
    canvas.drawRRect(
        RRect.fromRectAndCorners(
          Rect.fromLTWH(
              shadowWidth,
              shadowWidth,
              size.width - (shadowWidth * 2),
              size.height - (shadowWidth * 2)
          ),
          topLeft: radius,
          topRight: radius,
          bottomLeft: radius,
          bottomRight: radius,
        ),
        borderPaint);

    // Add tag circle
    canvas.drawRRect(
        RRect.fromRectAndCorners(
          Rect.fromLTWH(
              size.width - tagWidth,
              0.0,
              tagWidth,
              tagWidth
          ),
          topLeft: radius,
          topRight: radius,
          bottomLeft: radius,
          bottomRight: radius,
        ),
        tagPaint);

    // Add tag text
    TextPainter textPainter = TextPainter(textDirection: TextDirection.ltr);
    textPainter.text = TextSpan(
      text: '1',
      style: TextStyle(fontSize: 20.0, color: Colors.white),
    );

    textPainter.layout();
    textPainter.paint(
        canvas,
        Offset(
            size.width - tagWidth / 2 - textPainter.width / 2,
            tagWidth / 2 - textPainter.height / 2
        )
    );

    // Oval for the image
    Rect oval = Rect.fromLTWH(
        imageOffset,
        imageOffset,
        size.width - (imageOffset * 2),
        size.height - (imageOffset * 2)
    );

    // Add path for oval image
    canvas.clipPath(Path()
      ..addOval(oval));

    // Add image
    ui.Image image = await getImageFromPath(imagePath); // Alternatively use your own method to get the image
    paintImage(canvas: canvas, image: image, rect: oval, fit: BoxFit.fitWidth);

    // Convert canvas to image
    final ui.Image markerAsImage = await pictureRecorder.endRecording().toImage(
        size.width.toInt(),
        size.height.toInt()
    );

    // Convert image to bytes
    final ByteData byteData = await markerAsImage.toByteData(format: ui.ImageByteFormat.png);
    final Uint8List uint8List = byteData.buffer.asUint8List();

    return BitmapDescriptor.fromBytes(uint8List);
}
  1. Use it like so
final Marker marker = Marker(
      icon: await getMarkerIcon("path/to/your/image.png", Size(150.0, 150.0))
);

Note: The tag with a number is positioned 'loosely' for the demo purpose - you might need to style it differently so it would expand with the content.

Solution 2

  1. uses image package, https://pub.dev/packages/image, as im
  2. download image File f = await _downloadFile(url, "border"); im.Image img = im.decodeImage(f.readAsBytesSync());
  3. use drawString() to write number on it
  4. BitmapDescriptor.fromBytes(im.encodePng(img))

static var httpClient = new HttpClient();

Future<File> _downloadFile(String url, String filename) async {
    var request = await httpClient.getUrl(Uri.parse(url));
    var response = await request.close();
    var bytes = await consolidateHttpClientResponseBytes(response);
    String dir = (await getApplicationDocumentsDirectory()).path;
    File file = new File('$dir/$filename');
    await file.writeAsBytes(bytes);
    return file;
  }
Share:
16,891
Admin
Author by

Admin

Updated on June 05, 2022

Comments

  • Admin
    Admin almost 2 years

    I am using google_maps_flutter in my flutter app to use google map I have custom marker icon and I load this with BitmapDescriptor.fromBytes(markerIcon) But I want to show icon from Url with some text. here is my flutter code:

    Map<MarkerId, Marker> markers =
      <MarkerId, Marker>{}; // CLASS MEMBER, MAP OF MARKS
    
    void _add([center]) async {
    
    
    for (int i = 0; i < sallersList.length; i++) {
      if (sallersList[i]["uID"] != currentUser.uid) {
        /*var request = await http.get(sallersList[i]["user_image"]);
        var bytes = await request.bodyBytes;*/
    
         final Uint8List markerIcon = await getBytesFromCanvas(150,         150);
    
        var markerIdVal = sallersList[i]["uID"];
        final MarkerId markerId = MarkerId(markerIdVal);
    
        // creating a new MARKER
        final Marker marker = Marker(
          markerId: markerId,
          position: LatLng(
              sallersList[i]["latitude"], //+ sin(1 * pi / 6.0) / 20.0,
              sallersList[i]["longitude"] //+ cos(1 * pi / 6.0) / 20.0,
              ),
          // icon: BitmapDescriptor.fromBytes(bytes.buffer.asUint8List(),),
          icon: BitmapDescriptor.fromBytes(markerIcon),
          infoWindow: InfoWindow(
              title: sallersList[i]["user_name"],
              snippet: sallersList[i]["address"]),
          onTap: () {
           // print(sallersList[i]["uID"]);
    
            Navigator.of(context).push(new MaterialPageRoute(
                builder: (BuildContext context) =>
                new DirectDetails()));
          },
        );
        if (this.mounted)
          setState(() {
            // adding a new marker to map
            markers[markerId] = marker;
          });
      }
    }
    }
    
    
    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(width/2);
    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: '1',
      style: TextStyle(fontSize: 65.0, color: Colors.white),
    );
    
    painter.layout();
    painter.paint(
        canvas,
        Offset((width * 0.5) - painter.width * 0.5,
            (height * .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();
    }
    

    This is what we want to achieve

    Our end requirement

    This is what I am able to achieve. My Try

    Is it possible to achieve the same via flutter or any external image dependency that can create the images on the fly.

  • newbie
    newbie over 4 years
    will this bogged down when we have many icons to be refreshed and drawn?
  • temirbek
    temirbek over 3 years
    how to add shadow to custom marker? I mean real shadow, in addition to that blue circle with opacity.
  • Anwar
    Anwar almost 3 years
    thank you its work for me in this case user-images.githubusercontent.com/35429445/…
  • Mr B
    Mr B over 2 years
    how would you overlay tag circle on top of the image? i want to achieve something like this: drive.google.com/file/d/1NM9FTPXG-wH5boMURVdZgX0jtfVPupnm/…