【剪裁 widget】Flutter ClipRect

简介: 【剪裁 widget】Flutter ClipRect

image.png

用一个矩形去剪裁 child,矩形以外的部分不显示。通过和一些没有剪裁功能的 widget 合用,剪裁这些 widget 溢出的部分,还能高效的实现动画。

和布局 widget 不同,剪裁 widget 功能实现是在绘制阶段。所以 剪裁 widget 的 size 是不会变的,无论怎样剪裁。

因为实现是在绘制阶段,所以具体实现是在 paint 方法中调用 PaintingContext 类的 pushClipRect 方法进行剪裁。

@override
  void paint(PaintingContext context, Offset offset) {
   ...
        layer = context.pushClipRRect(
          needsCompositing,
          offset,
          _clip!.outerRect,
          _clip!,
          super.paint,
          clipBehavior: clipBehavior,
          oldLayer: layer as ClipRRectLayer?,
        );
      }
   ...
  }
复制代码

相比于 ClipPath,ClipRect 是比较高效的,所以如果是要实现矩形剪裁,优先选用 ClipRect。

默认情况下,ClipRect 的剪裁路径是正好包含整个 child,只有溢出 child 的部分才会剪裁。我们可以指定 clipper 参数进行自定义裁剪。

举个例子,我们想裁剪出花朵的部分。图片大小为 100 x 100。


image.png

class MyClipper extends CustomClipper<Rect> {
  @override
  Rect getClip(Size size) {
     return const Rect.fromLTWH(30, 40, 50, 50);
  }
  @override
  bool shouldReclip(covariant CustomClipper<Rect> oldClipper) {
    return false;
  }
}
复制代码
Center(
    child: ClipRect(
      clipper: MyClipper(),
      child: Image.network(
        'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e993af7d8ac142d5a5818e6a12249b2c~tplv-k3u1fbpfcp-watermark.image?',
        width: 100,
        height: 100,
        fit: BoxFit.fill,
    ),
 ))
复制代码

想要有裁剪效果, 自定义 clipper 是必须的,还好并不复杂。

ClipRect 的应用场景

我们知道有的 widget 是带 clip 参数的,有剪裁功能,但有的 wiget 没有,如果想剪裁溢出的部分怎么办呢。这就需要用到 ClipRect了。

裁剪 Align 溢出部分


image.png


Center(
    child: Column(
      children: [
        Align(
          alignment: Alignment.topCenter,
          heightFactor: 0.5,
          child: Container(
            width: 100,
            height: 100,
            color: Colors.blue[300],
          ),
        ),
        const Text('IAM17 天天更新'),
      ],
  ))
复制代码

我们前面已经讲解过 Align Widget 了,正好复习一下。本例中 Align Widget 有 heightFactor 参数,所以它的高为 100 * 0.5 = 50。虽然高为 50,但在绘制的时候,还会把 child 完整绘制出来,我们看到的 child 还是高 100。下面的文本 IAM17 天天更新 会接着在 50 高的位置显示出来。效果上就是文本覆盖了蓝色 box。


加上 ClipRect 就可以把多出来的部分 clip 掉。先看下效果


image.png

Center(
    child: Column(
      children: [
        ClipRect(
          child: Align(
            alignment: Alignment.topCenter,
            heightFactor: 0.5,
            child: Container(
              width: 100,
              height: 100,
              color: Colors.blue[300],
            ),
          ),
        ),
        //省略字号等信息
        const Text('IAM17 天天更新'),
      ],
 ))
复制代码

除了可以为 Align Widget 剪裁,还可以给 OverFlowBoxSizedOverFlowBox 等其它无法自己剪裁的 widget 进行剪裁。

clip 动画

在 css 中 clip 能做动画 ,flutter 中也是一样的,大同小异。

剪裁 widget 做动画的关键在于 clipper 参数,clipper 是 CustomClipper 类型 ,CustomClipper 的参数 reclip 可以用来做动画。

/// The clipper will update its clip whenever [reclip] notifies its listeners.
  const CustomClipper({Listenable? reclip}) : _reclip = reclip;
复制代码

还是举个例子吧,这样比较直观些。我就不用图片了,用矩形代替,直接就能画,重意而非重形。


image.png

贴出全部代码,方便大家自己试试。

import 'package:flutter/material.dart';
void main() => runApp(const ClipRectApp());
class ClipRectApp extends StatefulWidget {
  const ClipRectApp({super.key});
  @override
  State<ClipRectApp> createState() => _ClipRectAppState();
}
class _ClipRectAppState extends State<ClipRectApp>
    with TickerProviderStateMixin {
  late AnimationController _sizeController;
  late Animation<double> _animation;
  @override
  void initState() {
    _sizeController =
        AnimationController(vsync: this, duration: const Duration(seconds: 2))
          ..repeat();
    _animation = _sizeController.drive(Tween<double>(begin: 0.0, end: 100.0));
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ClipRect(
        clipper: MyClipper(
          reclip: _animation,
        ),
          child: Container(
        width: 100,
        height: 100,
        color: Colors.blue[300],
      )),
    );
  }
}
class MyClipper extends CustomClipper<Rect> {
  MyClipper({required Animation<double> reclip})
      :_reclip=reclip, super(reclip: reclip);
  Animation<double> _reclip;
  @override
  Rect getClip(Size size) {
    return Rect.fromCenter(
        center: const Offset(50, 50), width: _reclip.value, height: _reclip.value);
  }
  @override
  bool shouldReclip(covariant CustomClipper<Rect> oldClipper) {
    return false;
  }
}
复制代码


clip 动画是很高效的,因为它省略了 build 的开销。

虽然 ClipRect 是 剪裁中最简单的一个,但它的应用却是最广泛的。自己能剪裁的 widget 实现的大都是矩形剪裁。比如 Stack

从源头上来说,其实大家都用的都是 PaintingContext 类的 pushClipRect 实现的矩形剪裁功能。


最后要注意的


如果你发现 ClipRect 没有效果从两个方向找原因

  1. clipper 参数是否正确
  2. 可能是 ClipRect 比 child 大,剪在了 child 之外。
目录
相关文章
|
2月前
Flutter-底部弹出框(Widget层级)
文章描述了如何在Flutter中使用DraggableScrollableSheet创建一个底部弹出框,同时保持其可手势滑动关闭。作者遇到问题并提出对原控件进行扩展,以支持头部和列表布局的滑动关闭功能。
129 0
|
3月前
Flutter StreamBuilder 实现局部刷新 Widget
Flutter StreamBuilder 实现局部刷新 Widget
28 0
|
4月前
|
Android开发
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
|
4月前
|
JavaScript 前端开发 开发者
【Flutter前端技术开发专栏】Flutter中的Widget与状态管理
【4月更文挑战第30天】本文探讨了Flutter的Widget和状态管理。Widget是Flutter构建UI的基础,分为有状态和无状态两种。状态管理确保UI随应用状态变化更新,影响应用性能和可维护性。文章介绍了`setState`、`Provider`、`Riverpod`、`Bloc`和`Redux`等状态管理方法,并通过计数器应用展示了其实现。选择合适的状态管理策略对高效开发至关重要。
59 0
【Flutter前端技术开发专栏】Flutter中的Widget与状态管理
|
4月前
|
编解码 算法 开发者
Flutter的布局系统:深入探索布局Widget与布局原则
【4月更文挑战第26天】Flutter布局系统详解,涵盖布局Widget(Row/Column、Stack、GridView/ListView、CustomSingleChildLayout)和布局原则(弹性布局、约束优先、流式布局、简洁明了)。文章旨在帮助开发者理解并运用Flutter的布局系统,创建适应性强、用户体验佳的界面。通过选择合适的布局Widget和遵循原则,可实现复杂且高效的UI设计。
|
4月前
|
前端开发 开发者 UED
Flutter的自定义Painter:深入探索自定义绘制Widget的技术实现
【4月更文挑战第26天】Flutter的自定义Painter允许开发者根据需求绘制独特UI,通过继承`CustomPaint`类和重写`paint`方法实现。在`paint`中使用`Canvas`绘制图形、路径等。创建自定义Painter类后,将其作为`CustomPaint` Widget的`painter`属性使用。此技术可实现自定义形状、渐变、动画等复杂效果,提升应用视觉体验。随着Flutter的进化,自定义Painter将提供更丰富的功能。
|
4月前
|
开发框架 搜索推荐 Android开发
Flutter的Widget基础:概念、分类与深入探索
【4月更文挑战第26天】Flutter Widget详解:基础、分类与工作原理。Widget是Flutter UI的核心,描述界面外观而非直接渲染。分为基础、布局、可滚动及状态管理四大类。基于响应式编程,状态变化时自动更新UI。了解其概念、分类和原理,能助开发者高效构建精美应用。随着Flutter生态发展,Widget系统潜力无限。
Flutter源码分析笔记:Widget类源码分析
本文记录阅读与分析Flutter源码 - Widget类源码分析。
84 0
Flutter源码分析笔记:Widget类源码分析
|
Dart 前端开发 开发工具
谷歌移动UI框架Flutter教程之Widget
谷歌移动UI框架Flutter教程之Widget
|
API Android开发 容器
Flutter控件之基类Widget封装
基类的Widget主要确定以下几个方面,第一就是,自定义一个抽象类还是非抽象类,第二、继承方式,采取有状态还是无状态,第三、关于组件的点击方式,如何进行实现。
150 0