如何在Flutter中实现类似CSS的Squircle形状容器?
Flutter 实现 Squircle UI 容器方案
普通的 BorderRadius.circular 只能生成圆形圆角,无法实现 Squircle 那种包含边部平滑曲线的特殊形状,你可以通过以下几种原生方法实现需求:
方法1:椭圆圆角(简单快速,接近基础 Squircle)
使用 Radius.elliptical 为每个角设置不同的水平/垂直半径,模拟 Squircle 的椭圆边角效果,适合快速实现基础样式:
import 'package:flutter/material.dart'; class FrostedGlassBox extends StatelessWidget { const FrostedGlassBox({super.key}); @override Widget build(BuildContext context) { return Container( width: 300, // *根据需求设置宽高* height: 200, decoration: BoxDecoration( color: Colors.amber, // *水平半径44,垂直半径22,可根据需求调整* borderRadius: BorderRadius.all(Radius.elliptical(44, 22)), ), ); } }
方法2:自定义 ClipPath(完全自定义曲线)
通过 ClipPath 绘制完全符合需求的 Squircle 路径,适合需要边部也有弧度的场景:
首先定义自定义 Clipper:
class SquircleClipper extends CustomClipper<Path> { final double curveRadius; SquircleClipper(this.curveRadius); @override Path getClip(Size size) { final path = Path(); final centerX = size.width / 2; final centerY = size.height / 2; // 用三次贝塞尔曲线绘制四边平滑曲线 path.moveTo(centerX, 0); path.cubicTo( size.width - curveRadius, 0, size.width, curveRadius, size.width, centerY, ); path.cubicTo( size.width, size.height - curveRadius, size.width - curveRadius, size.height, centerX, size.height, ); path.cubicTo( curveRadius, size.height, 0, size.height - curveRadius, 0, centerY, ); path.cubicTo( 0, curveRadius, curveRadius, 0, centerX, 0, ); path.close(); return path; } @override bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false; }
然后在组件中使用:
class FrostedGlassBox extends StatelessWidget { const FrostedGlassBox({super.key}); @override Widget build(BuildContext context) { return ClipPath( clipper: SquircleClipper(22), // *调整曲线弧度* child: Container( width: 300, height: 200, color: Colors.amber, ), ); } }
方法3:自定义 ShapeBorder(集成到 BoxDecoration)
自定义 ShapeBorder,可以直接在 BoxDecoration 的 shape 属性中使用,无需额外的 Clip 组件:
首先定义自定义 Border:
class SquircleBorder extends ShapeBorder { final double curveRadius; SquircleBorder(this.curveRadius); @override EdgeInsetsGeometry get dimensions => EdgeInsets.zero; @override Path getInnerPath(Rect rect, {TextDirection? textDirection}) => getOuterPath(rect); @override Path getOuterPath(Rect rect, {TextDirection? textDirection}) { final centerX = rect.center.dx; final centerY = rect.center.dy; final path = Path(); path.moveTo(centerX, rect.top); path.cubicTo( rect.right - curveRadius, rect.top, rect.right, rect.top + curveRadius, rect.right, centerY, ); path.cubicTo( rect.right, rect.bottom - curveRadius, rect.right - curveRadius, rect.bottom, centerX, rect.bottom, ); path.cubicTo( rect.left + curveRadius, rect.bottom, rect.left, rect.bottom - curveRadius, rect.left, centerY, ); path.cubicTo( rect.left, rect.top + curveRadius, rect.left + curveRadius, rect.top, centerX, rect.top, ); path.close(); return path; } @override void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {} @override ShapeBorder scale(double t) => SquircleBorder(curveRadius * t); }
然后在组件中使用:
class FrostedGlassBox extends StatelessWidget { const FrostedGlassBox({super.key}); @override Widget build(BuildContext context) { return Container( width: 300, height: 200, decoration: BoxDecoration( color: Colors.amber, shape: SquircleBorder(22), // *调整曲线弧度* ), ); } }
内容的提问来源于stack exchange,提问作者Satyam Mishra
相关产品推荐
相关产品推荐

