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

Flutter新手求助:如何实现右下角内凹圆角边框效果

实现Flutter右下角内凹圆角的UI效果

你当前代码里用BorderRadius设置的是外凸圆角,而目标是右下角的内凹圆角,这需要通过自定义路径裁剪或绘制来实现,下面提供两种实用方案:

方案一:使用ClipPath + CustomClipper裁剪

通过自定义裁剪路径,把容器右下角的圆形区域"挖掉",实现内凹效果:

  1. 先定义一个自定义裁剪器:
import 'dart:math' as math;
import 'package:flutter/material.dart';

class InvertedBottomRightClipper extends CustomClipper<Path> {
  final double radius;

  const InvertedBottomRightClipper(this.radius);

  @override
  Path getClip(Size size) {
    final path = Path();
    // 构建路径:左上角 → 右上角 → 右下角左侧半径点 → 内凹圆弧 → 右下角 → 左下角 → 闭合
    path.moveTo(0, 0);
    path.lineTo(size.width, 0);
    path.lineTo(size.width - radius, 0);
    path.lineTo(size.width - radius, size.height - radius);
    // 绘制内凹的四分之一圆弧
    path.arcTo(
      Rect.fromLTWH(size.width - 2 * radius, size.height - 2 * radius, 2 * radius, 2 * radius),
      0,
      -math.pi / 2,
      false,
    );
    path.lineTo(size.width, size.height);
    path.lineTo(0, size.height);
    path.close();
    return path;
  }

  @override
  bool shouldReclip(covariant InvertedBottomRightClipper oldClipper) {
    return oldClipper.radius != radius;
  }
}
  1. 修改你的Folder组件,用ClipPath包裹下方容器:
class Folder extends StatelessWidget {
  final String title;
  final Color? backgroundColor;
  final Widget child;

  const Folder({
    super.key,
    required this.title,
    this.backgroundColor,
    required this.child,
  });

  @override
  Widget build(BuildContext context) {
    final bgColor = backgroundColor ?? Theme.of(context).colorScheme.secondary;
    final titleStyle = Theme.of(context).textTheme.titleMedium;

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Container(
          width: 100,
          height: 30,
          decoration: BoxDecoration(
            color: bgColor,
            borderRadius: const BorderRadius.only(
              topRight: Radius.circular(10),
              topLeft: Radius.circular(10),
            ),
          ),
          child: Center(child: Text(title, style: titleStyle)),
        ),
        // 用ClipPath实现内凹裁剪
        ClipPath(
          clipper: const InvertedBottomRightClipper(10),
          child: Container(
            width: double.infinity,
            color: bgColor,
            child: Padding(padding: const EdgeInsets.all(16.0), child: child),
          ),
        ),
      ],
    );
  }
}

方案二:使用CustomPaint绘制完整背景(推荐)

直接绘制带内凹的完整背景形状,避免两个容器拼接的缝隙,视觉效果更统一:

  1. 定义自定义绘制器:
import 'dart:math' as math;
import 'package:flutter/material.dart';

class FolderBackgroundPainter extends CustomPainter {
  final Color color;
  final double radius;
  final double topBarWidth;

  const FolderBackgroundPainter({
    required this.color,
    required this.radius,
    required this.topBarWidth,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..color = color;
    final path = Path();

    // 绘制顶部标题栏的圆角路径
    path.moveTo(0, 0);
    path.lineTo(topBarWidth - radius, 0);
    path.arcTo(
      Rect.fromLTWH(topBarWidth - radius, 0, radius, radius),
      math.pi,
      math.pi / 2,
      false,
    );
    path.lineTo(topBarWidth, radius);

    // 绘制下方容器的路径,包含右下角内凹
    path.lineTo(size.width, radius);
    path.lineTo(size.width, size.height - radius);
    path.arcTo(
      Rect.fromLTWH(size.width - 2 * radius, size.height - 2 * radius, 2 * radius, 2 * radius),
      0,
      -math.pi / 2,
      false,
    );
    path.lineTo(size.width, size.height);
    path.lineTo(0, size.height);
    path.lineTo(0, radius);
    path.arcTo(
      Rect.fromLTWH(0, 0, radius, radius),
      -math.pi / 2,
      math.pi / 2,
      false,
    );
    path.close();

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant FolderBackgroundPainter oldDelegate) {
    return oldDelegate.color != color || 
           oldDelegate.radius != radius || 
           oldDelegate.topBarWidth != topBarWidth;
  }
}
  1. 修改Folder组件为Stack结构,把标题和内容叠加在绘制的背景上:
class Folder extends StatelessWidget {
  final String title;
  final Color? backgroundColor;
  final Widget child;

  const Folder({
    super.key,
    required this.title,
    this.backgroundColor,
    required this.child,
  });

  @override
  Widget build(BuildContext context) {
    final bgColor = backgroundColor ?? Theme.of(context).colorScheme.secondary;
    final titleStyle = Theme.of(context).textTheme.titleMedium;
    const radius = 10.0;
    const topBarWidth = 100.0;

    return Stack(
      children: [
        // 绘制完整的带内凹的背景
        CustomPaint(
          size: Size.infinite,
          painter: FolderBackgroundPainter(
            color: bgColor,
            radius: radius,
            topBarWidth: topBarWidth,
          ),
        ),
        // 叠加标题和内容
        Padding(
          padding: const EdgeInsets.only(top: 8.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              SizedBox(
                width: topBarWidth,
                height: 30,
                child: Center(child: Text(title, style: titleStyle)),
              ),
              Padding(
                padding: const EdgeInsets.all(16.0).copyWith(top: 0),
                child: child,
              ),
            ],
          ),
        ),
      ],
    );
  }
}

关键说明

  • BorderRadius只能实现外凸圆角,内凹效果需要通过修改路径实现;
  • ClipPath通过裁剪现有容器来实现内凹,适合简单场景;
  • CustomPaint直接绘制完整形状,视觉连贯性更好,适合需要统一背景的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 16:55:09