Flutter如何为组合实现的不规则红色图形添加贴合外边框
Flutter组合异形组件外边框实现
我编写了如下代码:
Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( height: 40.0, width: MediaQuery.of(context).size.width / 2 - 20, decoration: BoxDecoration( color: Colors.red, borderRadius: BorderRadius.only( topLeft: Radius.circular(15.0), topRight: Radius.circular(15.0), ), ), ), Container( height: 80.0, width: MediaQuery.of(context).size.width - 40, decoration: BoxDecoration( color: Colors.red, borderRadius: BorderRadius.only( bottomLeft: Radius.circular(15.0), bottomRight: Radius.circular(15.0), topRight: Radius.circular(15.0), ), ), ), ], ), ], ),
上述代码的运行效果如下:
需求为该红色组合图形添加贴合轮廓的外边框,直接嵌套Container设置Border.all()无法实现预期效果。
普通Container的边框默认是矩形,无法匹配这个L型带圆角的不规则轮廓,以下是两种可直接落地的实现方案:
方案1:CustomPaint 自绘完整轮廓
直接在一个画布上同时绘制图形填充和外边框,完全规避多组件拼接导致的衔接处断边、边框重叠问题:
// 替换原有Row/Column拼接布局 SizedBox( width: MediaQuery.of(context).size.width - 40, height: 120, child: CustomPaint( painter: RedShapePainter( borderColor: Colors.black, // 自定义边框颜色 borderWidth: 2, // 自定义边框粗细 ), ), ) class RedShapePainter extends CustomPainter { final Color fillColor = Colors.red; final Color borderColor; final double borderWidth; final double radius = 15; RedShapePainter({required this.borderColor, required this.borderWidth}); @override void paint(Canvas canvas, Size size) { final double topSectionWidth = size.width / 2; // 绘制红色填充 final fillPaint = Paint()..color = fillColor..style = PaintingStyle.fill; final fillPath = Path() ..addRRect(RRect.fromRectAndCorners( Rect.fromLTWH(0, 0, topSectionWidth, 40), topLeft: Radius.circular(radius), topRight: Radius.circular(radius), )) ..addRRect(RRect.fromRectAndCorners( Rect.fromLTWH(0, 40, size.width, 80), bottomLeft: Radius.circular(radius), bottomRight: Radius.circular(radius), topRight: Radius.circular(radius), )); canvas.drawPath(fillPath, fillPaint); // 绘制外轮廓边框,跳过两个块衔接的内部边 final borderPaint = Paint() ..color = borderColor ..style = PaintingStyle.stroke ..strokeWidth = borderWidth ..strokeCap = StrokeCap.round; final borderPath = Path() ..moveTo(0, radius) ..arcToPoint(Offset(radius, 0), radius: Radius.circular(radius)) ..lineTo(topSectionWidth - radius, 0) ..arcToPoint(Offset(topSectionWidth, radius), radius: Radius.circular(radius)) ..lineTo(topSectionWidth, 40) ..lineTo(size.width - radius, 40) ..arcToPoint(Offset(size.width, 40 + radius), radius: Radius.circular(radius)) ..lineTo(size.width, size.height - radius) ..arcToPoint(Offset(size.width - radius, size.height), radius: Radius.circular(radius)) ..lineTo(radius, size.height) ..arcToPoint(Offset(0, size.height - radius), radius: Radius.circular(radius)) ..lineTo(0, radius) ..close(); canvas.drawPath(borderPath, borderPaint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; }
方案2:自定义ShapeBorder 实现轮廓边框
通过继承OutlinedBorder自定义形状,直接用在Container的decoration里,代码复用性更高:
Container( width: MediaQuery.of(context).size.width - 40, height: 120, decoration: ShapeDecoration( color: Colors.red, shape: LShapeBorder( side: BorderSide(color: Colors.black, width: 2), // 边框配置 radius: 15, ), ), ) class LShapeBorder extends OutlinedBorder { final double radius; const LShapeBorder({ required this.radius, super.side = BorderSide.none, }); @override OutlinedBorder copyWith({BorderSide? side}) { return LShapeBorder(radius: radius, side: side ?? this.side); } @override Path getInnerPath(Rect rect, {TextDirection? textDirection}) { return getOuterPath(rect).shift(Offset(side.strokeInset, side.strokeInset)); } @override Path getOuterPath(Rect rect, {TextDirection? textDirection}) { final topWidth = rect.width / 2; final path = Path() ..addRRect(RRect.fromRectAndCorners( Rect.fromLTWH(rect.left, rect.top, topWidth, 40), topLeft: Radius.circular(radius), topRight: Radius.circular(radius), )) ..addRRect(RRect.fromRectAndCorners( Rect.fromLTWH(rect.left, rect.top + 40, rect.width, rect.height - 40), bottomLeft: Radius.circular(radius), bottomRight: Radius.circular(radius), topRight: Radius.circular(radius), )); path.fillType = PathFillType.nonZero; return path; } @override void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) { if (side.style == BorderStyle.none) return; final outerPath = getOuterPath(rect); final borderPath = Path()..addPath(outerPath, Offset.zero); canvas.drawPath(borderPath, side.toPaint()..strokeJoin = StrokeJoin.round); } @override ShapeBorder scale(double t) { return LShapeBorder(radius: radius * t, side: side.scale(t)); } }
避坑提示:不要尝试给两个拼接的Container分别加边框,衔接位置会出现边框重叠、断缝的问题,单组件绘制完整轮廓是最稳定的实现方式。
内容的提问来源于stack exchange,提问作者CastoldiG
相关产品推荐
相关产品推荐

