ClipPath可以根据给定的Path来裁剪widget,它的定义如下:
/// Creates a path clip.
///
/// If [clipper] is null, the clip will be a rectangle that matches the layout
/// size and location of the child. However, rather than use this default,
/// consider using a [ClipRect], which can achieve the same effect more
/// efficiently.
///
/// The [clipBehavior] argument must not be null or [Clip.none].
const ClipPath({
Key? key,
this.clipper,
this.clipBehavior = Clip.antiAlias,
Widget? child,
})
ClipPath实现中间透明四周半透明步骤
第一步:定义自定义裁剪 - 上半部分
class _TopBackClipper extends CustomClipper<Path> {
final double _clipWidth;
final double _borderRadius;
_TopBackClipper({double clipWidth, double borderRadius}) : _clipWidth = clipWidth, _borderRadius = borderRadius;
@override
Path getClip(Size size) {
var path = Path();
path.moveTo(0, 0);
path.lineTo(0, size.height / 2 + 0.1);
path.lineTo(size.width / 2 - _clipWidth / 2, size.height / 2 + 0.1);
path.lineTo(size.width / 2 - _clipWidth / 2,
size.height / 2 - _clipWidth / 2 + _borderRadius);
//左上角
path.arcToPoint(Offset(size.width / 2 - _clipWidth / 2 + _borderRadius, size.height / 2 - _clipWidth / 2),
radius: Radius.circular(_borderRadius));
path.lineTo(size.width / 2 + _clipWidth / 2 - _borderRadius,
size.height / 2 - _clipWidth / 2);
//右上角
path.arcToPoint(Offset(size.width / 2 + _clipWidth / 2, size.height / 2 - _clipWidth / 2 + _borderRadius),
radius: Radius.circular(_borderRadius));
path.lineTo(size.width / 2 + _clipWidth / 2, size.height / 2 + 0.1);
path.lineTo(size.width, size.height / 2 + 0.1);
path.lineTo(size.width, 0);
path.close();
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return false;
}
}
第二步:定义自定义裁剪 - 下半部分
class _BottomBackClipper extends CustomClipper<Path> {
final double _clipWidth;
final double _borderRadius;
_BottomBackClipper({double clipWidth, double borderRadius}) : _clipWidth = clipWidth, _borderRadius = borderRadius;
@override
Path getClip(Size size) {
var path = Path();
path.moveTo(0, size.height / 2);
path.lineTo(size.width / 2 - _clipWidth / 2, size.height / 2);
path.lineTo(size.width / 2 - _clipWidth / 2,
size.height / 2 + _clipWidth / 2 - _borderRadius);
//左下角
path.arcToPoint(Offset(size.width / 2 - _clipWidth / 2 + _borderRadius, size.height / 2 + _clipWidth / 2 ),
radius: Radius.circular(_borderRadius), clockwise: false);
path.lineTo(size.width / 2 + _clipWidth / 2 - _borderRadius,
size.height / 2 + _clipWidth / 2);
//右下角
path.arcToPoint(Offset(size.width / 2 + _clipWidth / 2, size.height / 2 + _clipWidth / 2 - _borderRadius),
radius: Radius.circular(_borderRadius), clockwise: false);
path.lineTo(size.width / 2 + _clipWidth / 2, size.height / 2);
path.lineTo(size.width, size.height/2);
path.lineTo(size.width, size.height);
path.lineTo(0, size.height);
path.close();
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return false;
}
}
第三步 封装成Widget
class ClipCenter extends StatefulWidget {
final double boundWidth;//中心裁剪宽度
final double borderRadius;
final Color backgroundColor;
const ClipCenter({Key key, this.boundWidth = 100, this.borderRadius = 8, this.backgroundColor = Colors.lightBlueAccent}) : super(key: key);
@override
_ClipCenterState createState() => _ClipCenterState();
}
class _ClipCenterState extends State<ClipCenter> {
@override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned(
top: 0,
bottom: 0,
left: 0,
right: 0,
child: ClipPath(
clipper: _TopBackClipper(clipWidth: widget.boundWidth, borderRadius: widget.borderRadius),
child: Container(
color: widget.backgroundColor,
),
),
),
Positioned(
top: 0,
bottom: 0,
left: 0,
right: 0,
child: ClipPath(
clipper: _BottomBackClipper(clipWidth: widget.boundWidth, borderRadius: widget.borderRadius),
child: Container(
color: widget.backgroundColor,
),
),
),
],
);
}
}
第四部 使用