Flutter中如何在Container内实现带圆角的自定义三角形遮罩?
实现带圆角边框的遮罩三角形(Flutter)
我已经完成了卡片Widget的编码,现在需要在卡片内创建自定义三角形形状并添加遮罩,同时让三角形带有圆角边框。之前尝试用CustomPaint实现,但效果不理想,恳请提供可行方案。
现有卡片代码
//Card form fulfiled by IMG from the profile Container( height: MediaQuery.of(context).size.height / 1.5533, width: MediaQuery.of(context).size.width / 1.075, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(20)), boxShadow: [ BoxShadow( color: Colors.grey, spreadRadius: 1, blurRadius: 10, blurStyle: BlurStyle.outer, ) ], image: DecorationImage( image: AssetImage('assets/img1.JPG'), fit: BoxFit.cover, ), ), ), //The rectangle containing Nickname & Tags Positioned( top: 20, right: 20, child: ClipRRect( borderRadius: BorderRadius.circular(20), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), child: Opacity( opacity: 0.3, child: Container( height: MediaQuery.of(context).size.height / 9.6082474, width: MediaQuery.of(context).size.width / 2.1182266, decoration: const BoxDecoration( color: Color(0XFFD9D9D9), ), ))))), //The Nickname of the user Positioned( top: 15, right: 25, child: ClipRRect( borderRadius: BorderRadius.circular(20), child: userNickname("Paul", context))), // Tags Positioned( top: 90, right: 90, child: tags("#EFREI", context, Colors.purple)), //The code for the red and green triangle to insert
之前尝试的CustomPaint代码
调用代码
Positioned( top: 360, child: CustomPaint( painter: CustomShape(blurSigma: 30), ))
CustomShape类
class CustomShape extends CustomPainter { double blurSigma; CustomShape({required this.blurSigma}); @override void paint(Canvas canvas, Size size) { var paint = Paint(); // TODO: Set properties to paint paint.color = const Color(0XFFFF0000).withOpacity(0.8); paint.maskFilter = MaskFilter.blur(BlurStyle.inner, blurSigma); var path = Path(); // TODO: Draw your path path.lineTo(100, 240); path.lineTo(0, 240); canvas.drawPath(path, paint); } @override bool shouldRepaint(CustomPainter oldDelegate) { return true; } }
解决方案
方法一:改进CustomPaint实现圆角三角形+遮罩
要实现带圆角的三角形,需要用Path的arcToPoint处理圆角,同时结合遮罩效果。下面是修改后的CustomShape:
class RoundedTrianglePainter extends CustomPainter { final Color color; final double blurSigma; final double cornerRadius; RoundedTrianglePainter({ required this.color, required this.blurSigma, this.cornerRadius = 8.0, }); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = color.withOpacity(0.8) ..maskFilter = MaskFilter.blur(BlurStyle.inner, blurSigma); final path = Path(); // 定义三角形三个顶点(示例:右下角直角三角形,可根据设计调整坐标) final point1 = Offset(size.width, 0); final point2 = Offset(size.width, size.height); final point3 = Offset(0, size.height); // 绘制带圆角的三角形路径 path.moveTo(point1.dx - cornerRadius, point1.dy); path.arcToPoint( Offset(point1.dx, point1.dy + cornerRadius), radius: Radius.circular(cornerRadius), clockwise: false, ); path.lineTo(point2.dx, point2.dy - cornerRadius); path.arcToPoint( Offset(point2.dx - cornerRadius, point2.dy), radius: Radius.circular(cornerRadius), clockwise: false, ); path.lineTo(point3.dx + cornerRadius, point3.dy); path.arcToPoint( Offset(point3.dx, point3.dy - cornerRadius), radius: Radius.circular(cornerRadius), clockwise: false, ); path.close(); canvas.drawPath(path, paint); } @override bool shouldRepaint(covariant RoundedTrianglePainter oldDelegate) { return oldDelegate.color != color || oldDelegate.blurSigma != blurSigma || oldDelegate.cornerRadius != cornerRadius; } }
调用时指定大小和位置:
Positioned( top: 360, left: 0, child: SizedBox( width: 100, height: 100, child: CustomPaint( painter: RoundedTrianglePainter( color: Colors.red, blurSigma: 30, cornerRadius: 10, ), ), ), ), // 绿色三角形同理 Positioned( top: 360, right: 0, child: SizedBox( width: 100, height: 100, child: CustomPaint( painter: RoundedTrianglePainter( color: Colors.green, blurSigma: 30, cornerRadius: 10, ), ), ), ),
方法二:用ClipPath + Container实现带边框的圆角三角形
如果需要同时显示边框,可以用ClipPath结合Container,再叠加遮罩:
class RoundedTriangleClipper extends CustomClipper<Path> { final double cornerRadius; RoundedTriangleClipper({this.cornerRadius = 8.0}); @override Path getClip(Size size) { final path = Path(); final point1 = Offset(size.width, 0); final point2 = Offset(size.width, size.height); final point3 = Offset(0, size.height); path.moveTo(point1.dx - cornerRadius, point1.dy); path.arcToPoint( Offset(point1.dx, point1.dy + cornerRadius), radius: Radius.circular(cornerRadius), clockwise: false, ); path.lineTo(point2.dx, point2.dy - cornerRadius); path.arcToPoint( Offset(point2.dx - cornerRadius, point2.dy), radius: Radius.circular(cornerRadius), clockwise: false, ); path.lineTo(point3.dx + cornerRadius, point3.dy); path.arcToPoint( Offset(point3.dx, point3.dy - cornerRadius), radius: Radius.circular(cornerRadius), clockwise: false, ); path.close(); return path; } @override bool shouldReclip(covariant RoundedTriangleClipper oldDelegate) { return oldDelegate.cornerRadius != cornerRadius; } }
使用时:
Positioned( top: 360, left: 0, child: SizedBox( width: 100, height: 100, child: ClipPath( clipper: RoundedTriangleClipper(cornerRadius: 10), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 30, sigmaY: 30), child: Container( color: Colors.red.withOpacity(0.8), decoration: BoxDecoration( border: Border.all(color: Colors.white, width: 2), // 边框 ), ), ), ), ), ),
关键说明
- 两种方法都可实现圆角三角形,方法一更适合纯形状绘制,方法二更适合需要边框或复杂装饰的场景
- 坐标点可根据Figma设计调整,示例用的是右下角直角三角形,修改顶点坐标即可适配需求
- 遮罩效果通过
MaskFilter(CustomPaint)或BackdropFilter(ClipPath)实现,按需选择
内容的提问来源于stack exchange,提问作者Akksel
相关产品推荐
相关产品推荐

