You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter实现谷歌地图自定义标记:加载用户图片URL并显示用户名

Flutter 谷歌地图自定义带用户名头像标记修复方案

核心问题原因

  • 绘制顺序错误:姓名标签绘制在头像之前,被后续绘制的头像图层覆盖
  • 裁剪后未恢复画布状态:头像绘制前做了圆形裁剪,未调用restore恢复画布,导致后续绘制的姓名标签也被裁剪
  • 姓名标签尺寸固定,未适配用户名实际长度,布局逻辑错误
  • 文字绘制偏移量写死,未做居中对齐计算
  • 字体大小固定,未适配不同尺寸的标记,显示比例不协调

修复后完整代码

仅需要修改getMarkerIcon函数即可,修改后代码如下:

Future<BitmapDescriptor> getMarkerIcon(
    String imagePath, Size size, NearbyUsers user) async {
  final ui.PictureRecorder pictureRecorder = ui.PictureRecorder();
  final Canvas canvas = Canvas(pictureRecorder);
  final Radius radius = Radius.circular(size.width / 2);
  final Paint nameTagPaint = Paint()..color = MyTheme.primaryColor;
  final Paint shadowPaint = Paint()
    ..color = MyTheme.primaryColor.withAlpha(120);
  final double shadowWidth = 15.0;
  final Paint borderPaint = Paint()..color = Colors.white;
  final double borderWidth = 3.0;
  final double imageOffset = shadowWidth + borderWidth;
  // ========== 提前计算文字尺寸,动态适配姓名标签大小 ==========
  TextPainter textPainter = TextPainter(textDirection: TextDirection.ltr);
  // 按标记尺寸动态适配字体大小,150宽标记对应约20号字体,120宽对应约16号
  double fontSize = size.width * 0.13;
  textPainter.text = TextSpan(
    text: '${user.fname} ${user.lname}',
    style: TextStyle(
        fontSize: fontSize,
        color: Colors.white,
        fontWeight: FontWeight.w500),
  );
  textPainter.layout();
  // 姓名标签内边距,可自行调整
  double tagPaddingH = 8, tagPaddingV = 4;
  double tagWidth = textPainter.width + tagPaddingH * 2;
  double tagHeight = textPainter.height + tagPaddingV * 2;
  // ========== 原有头像绘制逻辑 ==========
  // 绘制阴影圈
  canvas.drawRRect(
      RRect.fromRectAndCorners(
        Rect.fromLTWH(0.0, 0.0, size.width, size.height),
        topLeft: radius,
        topRight: radius,
        bottomLeft: radius,
        bottomRight: radius,
      ),
      shadowPaint);
  // 绘制白色边框圈
  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);
  // 保存画布状态,裁剪前必须调用
  canvas.save();
  // 头像圆形裁剪区
  Rect oval = Rect.fromLTWH(imageOffset, imageOffset,
      size.width - (imageOffset * 2), size.height - (imageOffset * 2));
  canvas.clipPath(Path()..addOval(oval));
  // 绘制用户头像
  ui.Image image = await getImageFromPath(imagePath);
  paintImage(canvas: canvas, image: image, rect: oval, fit: BoxFit.cover);
  // 恢复画布状态,取消圆形裁剪
  canvas.restore();
  // ========== 绘制姓名标签和文字 ==========
  // 姓名标签位置:头像底部居中,向下偏移10px,可自行调整偏移量修改位置
  double tagX = (size.width - tagWidth) / 2;
  double tagY = size.height - tagHeight / 2 + 10;
  // 绘制标签背景
  canvas.drawRRect(
      RRect.fromRectAndRadius(
          Rect.fromLTWH(tagX, tagY, tagWidth, tagHeight), Radius.circular(8)),
      nameTagPaint);
  // 绘制文字,在标签内居中
  textPainter.paint(canvas, Offset(tagX + tagPaddingH, tagY + tagPaddingV));
  // 转换为BitmapDescriptor,画布高度增加标签+偏移高度,避免内容被截断
  final ui.Image markerAsImage = await pictureRecorder
      .endRecording()
      .toImage(size.width.toInt(), (size.height + tagHeight + 10).toInt());
  final ByteData byteData =
      await markerAsImage.toByteData(format: ui.ImageByteFormat.png);
  final Uint8List uint8List = byteData.buffer.asUint8List();
  return BitmapDescriptor.fromBytes(uint8List);
}

补充说明

  • 如果需要将姓名标签放在头像顶部,只需要修改tagY的取值即可,比如改为-tagHeight + 10
  • 标签内边距、偏移量、字体大小系数都可以根据自己的设计需求调整
  • 头像填充模式改为BoxFit.cover避免用户头像拉伸变形

内容的提问来源于stack exchange,提问作者Shruti Ramnandan Sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 21:45:03