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

Flutter带悬浮按钮底部导航栏自定义圆角挖孔形状实现问题

问题原因

你当前的实现是直接对底部导航栏整体做圆角后挖去正圆形区域,挖孔边缘和导航栏主体的衔接处没有过渡圆角,才会出现棱角明显的问题。想要实现FAB和导航栏衔接位置的顺滑圆弧效果,需要手动绘制带过渡圆角的路径,不能直接用两个路径求差集实现挖孔。

修改后的自定义形状代码

class MyBorderShape extends ShapeBorder {
  // 挖孔的过渡圆角大小,可自行调整
  final double transitionRadius;
  // 挖孔直径,要比FAB实际直径大8-12左右留空隙
  final double holeSize;

  MyBorderShape({
    this.transitionRadius = 16,
    this.holeSize = 70,
  });

  @override
  EdgeInsetsGeometry get dimensions => EdgeInsets.zero;

  @override
  Path getInnerPath(Rect rect, {TextDirection? textDirection}) => Path();

  @override
  Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
    final halfWidth = rect.width / 2;
    final halfHole = holeSize / 2;
    // 挖孔最顶部的Y坐标
    final holeTopY = rect.top - (holeSize / 2);
    // 导航栏的全局圆角
    final globalRadius = Radius.circular(rect.height / 2.5);

    Path path = Path();
    // 左下角圆弧
    path.moveTo(rect.left, rect.bottom - globalRadius.y);
    path.arcToPoint(
      Offset(rect.left + globalRadius.x, rect.bottom),
      radius: globalRadius,
    );
    // 底部边
    path.lineTo(rect.right - globalRadius.x, rect.bottom);
    // 右下角圆弧
    path.arcToPoint(
      Offset(rect.right, rect.bottom - globalRadius.y),
      radius: globalRadius,
    );
    // 右侧边
    path.lineTo(rect.right, rect.top + globalRadius.y);
    // 右上角圆弧
    path.arcToPoint(
      Offset(rect.right - globalRadius.x, rect.top),
      radius: globalRadius,
    );
    // 右侧到挖孔右过渡点的直线
    path.lineTo(halfWidth + halfHole + transitionRadius, rect.top);
    // 挖孔右过渡圆角
    path.arcToPoint(
      Offset(halfWidth + halfHole, rect.top + transitionRadius),
      radius: Radius.circular(transitionRadius),
      clockwise: false,
    );
    // 挖孔右半圆弧
    path.arcToPoint(
      Offset(halfWidth - halfHole, rect.top + transitionRadius),
      radius: Radius.circular(halfHole),
      center: Offset(halfWidth, holeTopY + halfHole),
    );
    // 挖孔左过渡圆角
    path.arcToPoint(
      Offset(halfWidth - halfHole - transitionRadius, rect.top),
      radius: Radius.circular(transitionRadius),
      clockwise: false,
    );
    // 左侧到左上角的直线
    path.lineTo(rect.left + globalRadius.x, rect.top);
    // 左上角圆弧
    path.arcToPoint(
      Offset(rect.left, rect.top + globalRadius.y),
      radius: globalRadius,
    );

    path.close();
    return path;
  }

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {}

  @override
  ShapeBorder scale(double t) => this;
}

使用说明

  • 在BottomAppBar组件中直接指定shape为MyBorderShape即可
  • 可根据你的FAB实际尺寸调整holeSize和transitionRadius参数,实现不同的圆润效果
  • 需要同步将Scaffold的floatingActionButtonLocation属性设置为FloatingActionButtonLocation.centerDocked,保证FAB位置和挖孔对齐

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 20:36:04