如何在 Flutter 中自定义谷歌地图标记图标

Posted

技术标签:

【中文标题】如何在 Flutter 中自定义谷歌地图标记图标【英文标题】:How to customize google maps marker icon in Flutter 【发布时间】:2019-10-29 01:34:37 【问题描述】:

我在我的颤振应用程序中使用 google_maps_flutter 来使用谷歌地图我有自定义标记图标,我用 BitmapDescriptor.fromBytes(markerIcon) 加载它但我想显示来自 Url 的图标和一些文本。这是我的颤振代码:

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

这就是我们想要实现的目标

这是我能够实现的。

是否有可能通过颤振或任何可以动态创建图像的外部图像依赖项来实现相同的目标。

【问题讨论】:

在Canvas上画图需要开启软件渲染。例如颤振运行--启用软件渲染。只需将您的代码复制到没有谷歌地图的空项目上即可检查它是否正确绘制图像。 【参考方案1】:

要实现上述样式的休闲步骤如下:

    导入包
import 'dart:async';
import 'dart:ui' as ui;
import 'dart:typed_data';
import 'dart:io';
import 'package:flutter/material.dart';
    (可选)添加帮助函数以通过路径获取图像
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;

    添加函数getMarkerIcon() 返回BitmapDescriptor。这是您的功能的替代品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);

    像这样使用它
final Marker marker = Marker(
      icon: await getMarkerIcon("path/to/your/image.png", Size(150.0, 150.0))
);

注意: 出于演示目的,带有数字的标签“松散”地放置 - 您可能需要对其进行不同的样式设置,以便它可以随内容扩展。

【讨论】:

当我们有很多图标要刷新和绘制时,这是否会陷入困境? 如何为自定义标记添加阴影?我的意思是真正的阴影,除了那个不透明的蓝色圆圈。 感谢您在这种情况下为我所做的工作user-images.githubusercontent.com/35429445/… 如何在图片顶部叠加标签圈?我想实现这样的目标:drive.google.com/file/d/1NM9FTPXG-wH5boMURVdZgX0jtfVPupnm/…【参考方案2】:
    使用图片包https://pub.dev/packages/image作为im 下载图片 文件 f = await _downloadFile(url, "border"); im.Image img = im.decodeImage(f.readAsBytesSync()); 使用 drawString() 在上面写数字 BitmapDescriptor.fromBytes(im.encodePng(img))

静态变量 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;
  

【讨论】:

以上是关于如何在 Flutter 中自定义谷歌地图标记图标的主要内容,如果未能解决你的问题,请参考以下文章

使用集群自定义谷歌地图上的标记

为当前位置自定义谷歌地图蓝点

如何自定义谷歌地图以接收特定位置的通知

如何自定义谷歌智能锁账户列表对话框

使用自定义谷歌登录按钮时如何退出

如何自定义谷歌课堂分享按钮的外观