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

如何为自定义形状的PopupMenuButton设置边框颜色?

为自定义Tooltip形状的PopupMenuButton添加边框颜色

原有的TooltipShape类仅定义了形状路径,但没有实现边框的绘制逻辑,默认使用了BorderSide.none,要让它支持自定义边框颜色,需要对类做如下修改:

1. 修改TooltipShape类,支持自定义边框

允许通过构造参数传入BorderSide,并在paint方法中绘制边框路径,同时调整内部路径和尺寸计算:

class TooltipShape extends ShapeBorder {
  final BorderSide side;
  final BorderRadiusGeometry borderRadius;

  const TooltipShape({
    this.side = const BorderSide(),
    this.borderRadius = BorderRadius.zero,
  });

  @override
  EdgeInsetsGeometry get dimensions => EdgeInsets.all(side.width);

  @override
  Path getInnerPath(
    Rect rect, {
    TextDirection? textDirection,
  }) {
    final Path path = Path();
    final RRect rrect = borderRadius.resolve(textDirection).toRRect(rect).deflate(side.width);
    
    // 生成偏移边框宽度的内部路径,避免内容被边框遮挡
    path.moveTo(0, 10 - side.width);
    path.quadraticBezierTo(side.width, side.width, 10, side.width);
    path.lineTo(rrect.width - 30, side.width);
    path.lineTo(rrect.width - 20, side.width - 10);
    path.lineTo(rrect.width - 10, side.width);
    path.quadraticBezierTo(rrect.width, side.width, rrect.width, 10);
    path.lineTo(rrect.width, rrect.height - 10);
    path.quadraticBezierTo(
        rrect.width, rrect.height - side.width, rrect.width - 10, rrect.height - side.width);
    path.lineTo(10, rrect.height - side.width);
    path.quadraticBezierTo(side.width, rrect.height - side.width, side.width, rrect.height - 10);

    return path;
  }

  @override
  Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
    final Path path = Path();
    final RRect rrect = borderRadius.resolve(textDirection).toRRect(rect);

    path.moveTo(0, 10);
    path.quadraticBezierTo(0, 0, 10, 0);
    path.lineTo(rrect.width - 30, 0);
    path.lineTo(rrect.width - 20, -10);
    path.lineTo(rrect.width - 10, 0);
    path.quadraticBezierTo(rrect.width, 0, rrect.width, 10);
    path.lineTo(rrect.width, rrect.height - 10);
    path.quadraticBezierTo(
        rrect.width, rrect.height, rrect.width - 10, rrect.height);
    path.lineTo(10, rrect.height);
    path.quadraticBezierTo(0, rrect.height, 0, rrect.height - 10);

    return path;
  }

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {
    final paint = Paint()
      ..style = PaintingStyle.stroke
      ..color = side.color
      ..strokeWidth = side.width;
    
    canvas.drawPath(getOuterPath(rect, textDirection: textDirection), paint);
  }

  @override
  ShapeBorder scale(double t) => TooltipShape(
        side: side.scale(t),
        borderRadius: borderRadius * t,
      );
}

2. 使用带边框的TooltipShape

在PopupMenuButton中指定shape参数时,传入自定义的BorderSide来设置边框颜色和宽度:

PopupMenuButton(
  offset: const Offset(0, 50),
  shape: const TooltipShape(
    side: BorderSide(
      color: Colors.blue, // 设置边框颜色
      width: 1.0, // 设置边框宽度
    ),
  ),
  // 其他参数...
)

关键说明

  • 构造函数新增side和borderRadius参数,让样式配置更灵活
  • paint方法通过canvas.drawPath绘制外部路径的边框,直接使用传入的BorderSide属性
  • getInnerPath调整了路径偏移量,确保菜单内部内容不会被边框遮挡
  • scale方法同步更新边框和圆角的缩放逻辑,保持形状一致性

内容的提问来源于stack exchange,提问作者user5182503

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 19:33:28