Flavio
Flavio

Reputation: 61

Flutter create Google maps custom marker from canvas

I'm new in Flutter, and I am trying to create a custom marker for google maps. So far i have been able to create a circle with stroke, background color and icon from a canvas, but I need to add a background image from assets instead of the icon. This is my code:

  Future<BitmapDescriptor> createCustomMarkerBitmap({IconData iconData, Color iconColor, Color fillColor, Color strokeColor}) async {
    ByteData data = await rootBundle.load('assets/images/iconTry.png');
    Uint8List lst = new Uint8List.view(data.buffer);
    Codec codec = await ui.instantiateImageCodec(lst);
    FrameInfo frame = await codec.getNextFrame();
    final pictureRecorder = PictureRecorder();
    final canvas = Canvas(pictureRecorder); 
    paintCircleFill(canvas, fillColor);
    paintCircleStroke(canvas, strokeColor);
    paintIcon(canvas, iconColor, iconData);
    paintBackgroundImage(canvas, frame.image);
    final picture = pictureRecorder.endRecording();
    final image = await picture.toImage(markerSize, markerSize);
    final bytes = await image.toByteData(format: ImageByteFormat.png);
    BitmapDescriptor.fromBytes(bytes.buffer.asUint8List());

}

 void paintCircleFill(Canvas canvas, Color color) {
    final paint = Paint()
      ..style = PaintingStyle.fill
      ..color = color;
    canvas.drawCircle(Offset(circleOffset, circleOffset), innerRadius, paint);
  }

  void paintCircleStroke(Canvas canvas, Color color) {
    final paint = Paint()
      ..style = PaintingStyle.stroke
      ..color = color
      ..strokeWidth = (stroke);
    canvas.drawCircle(Offset(circleOffset, circleOffset), outerRadius, paint);
  }


void paintIcon(Canvas canvas, Color color, IconData iconData) {
    final textPainter = TextPainter(textDirection: TextDirection.ltr);
    textPainter.text = TextSpan(
        text: String.fromCharCode(iconData.codePoint),
        style: TextStyle(
          letterSpacing: 0.0,
          fontSize: iconSize,
          fontFamily: iconData.fontFamily,
          color: color,
        ));
    textPainter.layout();
    textPainter.paint(canvas, Offset(iconOffset, iconOffset));
  }

Upvotes: 2

Views: 1862

Answers (1)

dm_tr
dm_tr

Reputation: 4783

Use this class and call only the function createBitmapDescriptorFromIconData each time you want to create a bitmapIcon

class MarkerGenerator {
  final _markerSize;
  double _circleStrokeWidth;
  double _circleOffset;
  double _outlineCircleWidth;
  double _fillCircleWidth;
  double _iconSize;
  double _iconOffset;

  MarkerGenerator(this._markerSize) {
    // calculate marker dimensions
    _circleStrokeWidth = _markerSize / 12.0;
    _circleOffset = _markerSize / 2;
    _outlineCircleWidth = _circleOffset - (_circleStrokeWidth / 2);
    _fillCircleWidth = _markerSize / 2;
    final outlineCircleInnerWidth = _markerSize - (2 * _circleStrokeWidth);
    _iconSize = sqrt(pow(outlineCircleInnerWidth, 2) / 4);
    final rectDiagonal = sqrt(2 * pow(_markerSize, 2));
    final circleDistanceToCorners = (rectDiagonal - outlineCircleInnerWidth) / 2;
    _iconOffset = sqrt(pow(circleDistanceToCorners, 2) / 1);
  }

  /// Creates a BitmapDescriptor from an IconData
  Future<BitmapDescriptor> createBitmapDescriptorFromIconData(IconData iconData, Color iconColor, Color circleColor, Color backgroundColor) async {
    final pictureRecorder = PictureRecorder();
    final canvas = Canvas(pictureRecorder);

    _paintCircleFill(canvas, backgroundColor);
    _paintCircleStroke(canvas, circleColor);
    _paintIcon(canvas, iconColor, iconData);

    final picture = pictureRecorder.endRecording();
    final image = await picture.toImage(_markerSize.round(), _markerSize.round());
    final bytes = await image.toByteData(format: ImageByteFormat.png);

    return BitmapDescriptor.fromBytes(bytes.buffer.asUint8List());
  }

  /// Paints the icon background
  void _paintCircleFill(Canvas canvas, Color color) {
    final paint = Paint()
      ..style = PaintingStyle.fill
      ..color = color;
    canvas.drawCircle(Offset(_circleOffset, _circleOffset), _fillCircleWidth, paint);
  }

  /// Paints a circle around the icon
  void _paintCircleStroke(Canvas canvas, Color color) {
    final paint = Paint()
      ..style = PaintingStyle.stroke
      ..color = color
      ..strokeWidth = _circleStrokeWidth;
    canvas.drawCircle(Offset(_circleOffset, _circleOffset), _outlineCircleWidth, paint);
  }

  /// Paints the icon
  void _paintIcon(Canvas canvas, Color color, IconData iconData) {
    final textPainter = TextPainter(textDirection: TextDirection.ltr);
    textPainter.text = TextSpan(
        text: String.fromCharCode(iconData.codePoint),
        style: TextStyle(
          letterSpacing: 0.0,
          fontSize: _iconSize,
          fontFamily: iconData.fontFamily,
          package: iconData.fontPackage,
          color: color,
        ));
    textPainter.layout();
    textPainter.paint(canvas, Offset(_iconOffset, _iconOffset));
  }
}

Edit
If you want to use an asset instead, this class may be helpful

class Common {
  static 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();
  }
}

Then to create a bitmapIcon, you'll do like this

var bytes = await Common.getBytesFromAsset("your/asset/path", 100);
BitmapDescriptor.fromBytes(bytes);

Upvotes: 4

Related Questions