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

Flutter如何用CustomPainter绘制带圆角尖端的动态箭头?

实现带圆角尖端的动态箭头容器

要实现底部带圆角尖端的三角形箭头(与上方圆角容器衔接),同时支持动态调整箭头位置,可以通过以下步骤修改你的CustomPainter:

核心思路

  1. 替换尖角为圆弧:放弃直接用lineTo绘制尖角,改用arcTo绘制圆角尖端,通过计算圆弧的圆心和外接矩形实现平滑圆角。
  2. 添加可配置参数:允许自定义箭头的位置、大小、圆角半径以及容器的样式,让组件具备动态性。

完整实现代码

import 'package:flutter/material.dart';

class CustomStyleArrow extends CustomPainter {
  final Color color;
  final double containerBorderRadius;
  final double arrowWidth;
  final double arrowHeight;
  final double arrowCornerRadius;
  final double arrowCenterX; // 箭头中心的水平位置(相对于容器左边缘)

  CustomStyleArrow({
    this.color = Colors.white,
    this.containerBorderRadius = 15.0,
    this.arrowWidth = 25.0,
    this.arrowHeight = 10.0,
    this.arrowCornerRadius = 4.0,
    required this.arrowCenterX,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = color
      ..style = PaintingStyle.fill;

    final double width = size.width;
    final double height = size.height;

    // 计算箭头的关键坐标
    final double arrowLeftX = arrowCenterX - arrowWidth / 2;
    final double arrowRightX = arrowCenterX + arrowWidth / 2;
    // 圆弧的圆心:位于尖端上方arrowCornerRadius处
    final Offset arcCenter = Offset(
      arrowCenterX,
      height + arrowHeight - arrowCornerRadius,
    );
    // 圆弧的外接矩形
    final Rect arcRect = Rect.fromCircle(
      center: arcCenter,
      radius: arrowCornerRadius,
    );

    // 构建带圆角尖端的箭头路径
    final Path arrowPath = Path()
      ..moveTo(arrowLeftX, height) // 箭头左起点(容器底部左端点)
      ..lineTo(arcRect.left, arcRect.top) // 画到圆弧左端点
      ..arcTo(arcRect, pi, pi, false) // 绘制圆角圆弧(从左到右顺时针扫过180度)
      ..lineTo(arrowRightX, height) // 画到箭头右起点(容器底部右端点)
      ..close(); // 闭合路径

    canvas.drawPath(arrowPath, paint);

    // 绘制上方的圆角容器
    final RRect containerRRect = BorderRadius.circular(containerBorderRadius)
        .toRRect(Rect.fromLTRB(0, 0, width, height));
    canvas.drawRRect(containerRRect, paint);
  }

  @override
  bool shouldRepaint(CustomStyleArrow oldDelegate) {
    // 仅当参数变化时重绘,优化性能
    return oldDelegate.color != color ||
        oldDelegate.containerBorderRadius != containerBorderRadius ||
        oldDelegate.arrowWidth != arrowWidth ||
        oldDelegate.arrowHeight != arrowHeight ||
        oldDelegate.arrowCornerRadius != arrowCornerRadius ||
        oldDelegate.arrowCenterX != arrowCenterX;
  }
}

使用示例

你可以通过传入arrowCenterX参数自由控制箭头的位置,比如:

箭头在容器底部中间

CustomPaint(
  size: const Size(200, 100), // 容器宽高
  painter: CustomStyleArrow(
    arrowCenterX: 100, // 容器宽度的一半,即中间位置
    arrowCornerRadius: 5, // 调整尖端圆角大小
    color: Colors.blueAccent,
  ),
)

箭头在容器底部左侧

CustomPaint(
  size: const Size(200, 100),
  painter: CustomStyleArrow(
    arrowCenterX: 50, // 距离左边缘50px的位置
    arrowWidth: 20, // 缩小箭头宽度
  ),
)

关键细节说明

  • 圆弧绘制逻辑:arcTo方法的startAngle设为pi(180度),sweepAngle设为pi(180度),会从圆弧的左端点顺时针绘制到右端点,形成向下凸的半圆,刚好作为箭头的圆角尖端。
  • 动态配置:所有样式参数都通过构造函数传入,你可以根据需求调整容器圆角、箭头尺寸、颜色等属性。
  • 性能优化:shouldRepaint方法仅在参数变化时返回true,避免不必要的重绘。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 22:30:53