【剪裁 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 Widget 解析
|
2天前
|
存储 容器
Flutter 有状态Widget 和 无状态Widget
Flutter 有状态Widget 和 无状态Widget
|
1月前
深入理解Flutter鸿蒙next版本 中的Widget继承:使用extends获取数据与父类约束
本文详细介绍了Flutter中如何通过继承其他Widget来创建自定义组件。首先解释了Widget继承的基本概念,包括StatelessWidget和StatefulWidget的区别。接着通过具体示例展示了如何继承StatelessWidget和StatefulWidget,并在子类中访问父类的build方法和状态。最后,结合多个自定义Widget展示了如何在实际应用中灵活使用继承和组合来构建复杂的UI。
76 8
|
1月前
|
容器
flutter&鸿蒙next 使用 InheritedWidget 实现跨 Widget 传递状态
在 Flutter 中,状态管理至关重要。本文详细介绍了如何使用 InheritedWidget 实现跨 Widget 的状态传递。InheritedWidget 允许数据在 Widget 树中向下传递,适用于多层嵌套的场景。通过一个简单的计数器示例,展示了如何创建和使用 InheritedWidget,包括其基础概念、工作原理及代码实现。虽然 InheritedWidget 较底层,但它是许多高级状态管理解决方案的基础。
103 2
|
2月前
|
容器
flutter:第一个flutter&Widget的使用 (二)
本文介绍了Flutter框架下的基本组件及其用法,包括简单的 Stateless Widget 如文本和按钮,以及更复杂的 StatefulWidget 示例。详细解释了如何使用 `context` 获取祖先小部件的信息,并展示了 `MaterialApp` 的属性及用途。此外,还探讨了 `StatefulWidget` 与 `StatelessWidget` 的区别,以及 `AppBar` 的常见属性配置方法。适合Flutter初学者参考学习。
|
1月前
|
Dart JavaScript 前端开发
Flutter 的 Widget 概述与常用 Widgets 与鸿蒙 Next 的对比
Flutter 是 Google 开发的开源 UI 框架,用于快速构建高性能的移动、Web 和桌面应用。Flutter 通过 Widget 构建 UI,每个 UI 元素都是 Widget,包括文本、按钮、图片等。Widget 不仅描述外观,还描述行为,是不可变的。常见的 Widget 包括结构型(Container、Column、Row)、呈现型(Text、Image)、交互型(ElevatedButton)和状态管理型(StatefulWidget)。Flutter 与鸿蒙 Next 在组件化架构、开发语言、布局系统、性能和跨平台支持方面各有优势
75 0
|
5月前
Flutter-底部弹出框(Widget层级)
文章描述了如何在Flutter中使用DraggableScrollableSheet创建一个底部弹出框,同时保持其可手势滑动关闭。作者遇到问题并提出对原控件进行扩展,以支持头部和列表布局的滑动关闭功能。
199 0
|
6月前
Flutter StreamBuilder 实现局部刷新 Widget
Flutter StreamBuilder 实现局部刷新 Widget
52 0
|
7月前
|
Android开发
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
|
7月前
|
开发框架 前端开发 搜索推荐
【Flutter前端技术开发专栏】Flutter中的自定义Widget与渲染流程
【4月更文挑战第30天】探索Flutter的自定义Widget与渲染流程。自定义Widget是实现复杂UI设计的关键,优点在于个性化设计、功能扩展和代码复用,但也面临性能优化和复杂性管理的挑战。创建步骤包括设计结构、定义Widget类、实现构建逻辑和处理交互。Flutter渲染流程涉及渲染对象树、布局、绘制和合成阶段。实践案例展示如何创建带渐变背景和阴影的自定义按钮。了解这些知识能提升应用体验并应对开发挑战。查阅官方文档以深入学习。
86 0
【Flutter前端技术开发专栏】Flutter中的自定义Widget与渲染流程