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

如何用CustomPainter创建仅含边框的气泡形状?

Flutter 实现带箭头的空心气泡边框

问题现状

参考填充气泡的实现代码,将PaintingStyle改为stroke后,箭头与气泡边框出现断开缺口,当前效果如下:

当前效果

用户完整代码:

import 'package:flutter/material.dart';

void main() {
  runApp(
    const _MyApp(),
  );
}

class _MyApp extends StatelessWidget {
  const _MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: SafeArea(
          child: Container(
            color: Colors.grey,
            child: Padding(
              padding: const EdgeInsets.all(40.0),
              child: CustomPaint(
                painter: CustomStyleArrow(),
                child: Container(
                  padding: const EdgeInsets.only(
                    left: 15,
                    right: 15,
                    bottom: 20,
                    top: 20,
                  ),
                  child: const Text(
                    "This is the custom painter for arrow down curve",
                    style: TextStyle(
                      color: Colors.black,
                    ),
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class CustomStyleArrow extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Colors.white
      ..strokeWidth = 1
      ..style = PaintingStyle.stroke;
    const double triangleH = 10;
    const double triangleW = 25.0;
    final double width = size.width;
    final double height = size.height;

        final Path trianglePath = Path()
      ..moveTo(width / 2 - triangleW / 2, height)
      ..lineTo(width / 2, triangleH + height)
      ..lineTo(width / 2 + triangleW / 2, height)
      ..lineTo(width / 2 - triangleW / 2, height);
    canvas.drawPath(trianglePath, paint);
    final BorderRadius borderRadius = BorderRadius.circular(15);
    final Rect rect = Rect.fromLTRB(0, 0, width, height);
    final RRect outer = borderRadius.toRRect(rect);
    canvas.drawRRect(outer, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

期望效果

实现箭头与气泡边框连贯的空心样式,效果如下:

期望效果

解决方案

核心是将箭头与气泡的路径合并为单个闭合路径,而非分开绘制两个独立路径,这样边框会自然连贯。修改CustomStyleArrow的paint方法如下:

class CustomStyleArrow extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Colors.white
      ..strokeWidth = 1
      ..style = PaintingStyle.stroke;
    const double triangleH = 10;
    const double triangleW = 25.0;
    final double width = size.width;
    final double height = size.height;
    const double radius = 15; // 与原代码圆角半径一致

    // 构建合并后的闭合路径
    final Path path = Path();

    // 左上角圆角
    path.moveTo(radius, 0);
    path.arcToPoint(
      Offset(0, radius),
      radius: const Radius.circular(radius),
      clockwise: false,
    );

    // 左边框
    path.lineTo(0, height);

    // 左下角圆角
    path.arcToPoint(
      Offset(radius, height),
      radius: const Radius.circular(radius),
    );

    // 底部边框到箭头左侧起点
    path.lineTo(width / 2 - triangleW / 2, height);

    // 绘制箭头部分
    path.lineTo(width / 2, height + triangleH);
    path.lineTo(width / 2 + triangleW / 2, height);

    // 底部边框到右下角起点
    path.lineTo(width - radius, height);

    // 右下角圆角
    path.arcToPoint(
      Offset(width, height - radius),
      radius: const Radius.circular(radius),
      clockwise: false,
    );

    // 右边框
    path.lineTo(width, radius);

    // 右上角圆角
    path.arcToPoint(
      Offset(width - radius, 0),
      radius: const Radius.circular(radius),
    );

    // 上边框回到起点,闭合路径
    path.lineTo(radius, 0);
    path.close();

    // 绘制完整路径
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

修改说明

  1. 放弃分开绘制三角形和圆角矩形,改用Path手动构建完整闭合路径,确保箭头与气泡边框连接处无断点。
  2. 路径按顺时针方向绘制,依次完成圆角、边框、箭头的连接,最后通过path.close()闭合路径,保证边框连贯性。

内容的提问来源于stack exchange,提问作者shingo.nakanishi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 07:55:17