Flutter 自定义裁剪之圆形豁口/缺口

本文介绍了如何在Flutter中自定义裁剪以创建带有圆形缺口的组件。通过继承CustomClipper并实现getClip方法,结合ClipPath widget,详细讲解了裁剪的实际代码和思路分析。重点在于理解裁剪路径的构建,特别是矩形轮廓的重要性,以及如何处理阴影效果以避免被裁剪。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如图所示,图中的圆形缺口,需要我们自定义裁剪,才能实现。
在这里插入图片描述

Flutter自定义裁剪

裁剪,我们想到的是剪刀,实际上,Flutter的裁剪原理,和我们现实物理世界的剪刀是一样的,一定要想清楚,自己起点终点,哪些是保留部分,哪些是裁掉的部分

Flutter的自定义裁剪类CustomClipper

可以看到,CustomClipper并不是一个widget,是一个抽象类,因此,需要我们继承这个抽象类,实现抽象方法getclip,之后再配合ClipPath裁剪想要裁剪的widget

abstract class CustomClipper<T> extends Listenable {
  /// Creates a custom clipper.
  ///
  /// The clipper will update its clip whenever [reclip] notifies its listeners.
  const CustomClipper({ Listenable? reclip }) : _reclip = reclip;

  final Listenable? _reclip;

裁剪的实际代码

开篇中讲的效果,直接上代码

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

  MyClipper(this.radius);

  
  Path getClip(Size size) {
    var path = new Path();
    path.lineTo(0.0, size.height);
    path.lineTo(size.width, size.height);
    path.lineTo(size.width, 0.0);
    path.addOval(
        Rect.fromCircle(center: Offset(0.0, size.height / 2), radius: radius));
    path.addOval(Rect.fromCircle(
        center: Offset(size.width, size.height / 2), radius: radius));
    return path;
  }

  
  bool shouldReclip(CustomClipper<Path> oldClipper) => true;
}

思路分析

前三句lineTo 从组件widget的左上角原点位置开始,向组件左下角移动,再画到右下角,再到右上角,至此,一个矩形组件的轮廓描绘出来。
接着,通过path.addOval来添加圆形缺口,看下这个addOval方法

/// Adds a new sub-path that consists of a curve that forms the
  /// ellipse that fills the given rectangle.
  ///
  /// To add a circle, pass an appropriate rectangle as `oval`. [Rect.fromCircle]
  /// can be used to easily describe the circle's center [Offset] and radius.
  void addOval(Rect oval) {
    assert(_rectIsValid(oval));
    _addOval(oval.left, oval.top, oval.right, oval.bottom);
  }

注释翻译:

添加一条新的子路径,该路径由一条曲线组成,该曲线形成填充给定矩形的椭圆

简单理解就是在矩形上填充一个椭圆,通过Rect.fromCircle来添加圆形,Offset指定了圆心的位置。
通常我们想到的ClipOval是实现不了这个效果,因为这个会把要裁剪的widget,当作child,这个时候,要裁剪的组件整个就会被裁剪圆。

注意点

  1. 前三句lineTo实际才是关键,必须要把真个矩形轮廓拿到,才能进行下面的裁剪,这个也是最开始最难想到的点
  2. 实际上通过三阶贝塞尔曲线也可以实现类似的效果,但是实现起来非常复杂,需要计算控制点,每次只能裁剪半个圆,需要多次裁剪才能完成
  3. 裁剪效果会影响阴影效果,会把阴影效果裁剪掉,这个解决办法可以在完整代码中看下笔者的思路

完整代码


  Widget build(BuildContext context) {
    return ClipPath(
      clipper: MyClipper(10.0),
      child: Material(
        elevation: 4.0,
        shadowColor: Color(0x30E5E5E5),
        color: Colors.transparent,
        child: ClipPath(
          clipper: MyClipper(12.0),
          child: Card(
            elevation: 0.0,
            margin: const EdgeInsets.all(2.0),
            child: _buildCardContent(),
          ),
        ),
      ),
    );
  }

通过Material作为底层,先做一次裁剪,上层在做裁剪,这样可以保留Material的阴影效果,避免被裁剪掉,切记,裁剪Clipper是可以把阴影裁剪掉的

总结

简单的一个裁剪效果,如果对你实现裁剪效果有帮助,那就是这篇文章的意义

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ezview_uniview

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值