掌握 Flutter 中的形状和裁剪

OldBird

实用Tips|2024-11-13|最后更新: 2024-11-13|
type
status
date
slug
summary
tags
category
icon
password
 
 
以下是将你提供的 Medium 文章翻译为中文的版本:

掌握 Flutter 中的形状和裁剪

在处理形状时,如果你不知道如何实现设计的 UI,可能会感到有些不知所措。在本文中,我们将从基础开始,然后逐步介绍一些高级示例。
 
<ins/>

1. 什么是形状和盒子?

形状可以是任何由路径定义的形式。
盒子是一个矩形形状,由 4 个点组成。它可以具有额外的属性,如边框半径。
在 Flutter 中,盒子可能出现在不同的上下文中,例如:
  • RenderBox:非 sliver 上下文中的 widget 渲染对象
  • BoxDecoration
  • BoxBorder
另外,还有一些类,如 ShapeDecoration 和 ShapeBorder。
我们经常使用 BoxDecoration 来为 ContainerDecoratedSliversDecoratedBoxes 添加样式:
就这么简单,我们得到了如下效果:
notion image
另外,我们还可以使用 ShapeDecoration,它提供了类似的颜色、阴影和渐变的定制,主要的区别在于它的 shape 参数接受 ShapeBorder,而不是 BoxShape
notion image
那么,什么是 ShapeBorder 呢?
如果我们查看其实现,就会更清楚了:它有一些超类,比如 OutlinedBorderStarBorderBeveledRectangleBorder,这些都是用来装饰 widget 的不同形状。另外,还有一个 WidgetStateOutlinedBorder。如果你不熟悉 WidgetStates,可以阅读 这篇文章
边框的本质意味着应该有两条路径,内路径和外路径:
notion image
如果你不熟悉贝塞尔曲线,建议你查阅 这个交互式指南。Flutter 的 Path 支持线性、二次、三次和圆锥形段。
了解了术语后,接下来我们来做些有趣的事情:创建一些自定义形状!

2. 自定义 ShapeBorder 实现

为了实现这一点,我们需要创建 ShapeBorderOutlinedBorder 的一个超类,并实现以下方法:
  • getInnerPathgetOuterPath:返回相应路径的方法
  • paint:绘制形状的方法
  • scalecopyWith
让我们使用圆锥形段来创建一个消息气泡形状,并使权重(w)参数可变。
notion image
接下来,实现 getOuterPath 方法。这里,路径由从左下角开始并顺时针方向的线段和圆锥段组成。
为了更清楚,下面是每一行所做的操作的可视化:
notion image
现在,我们来创建一个稍微不同的 路径:
notion image
现在,我们可以像这样在任何 DecoratedBox 中使用这个形状:
notion image
为了改变,我们可以使用 AnimatedBuilder 动画化圆锥形段的权重。如果你对 Flutter 中的动画不太熟悉,可以参考 官方教程
notion image

3. 裁剪器的应用

在 Flutter 中,有几个内置的 裁剪器,例如:
  • ClipRect:矩形裁剪
  • ClipRRect:圆角矩形裁剪
  • ClipOval:圆形和椭圆形裁剪
  • ClipPath:自定义路径裁剪
前三个非常简单,只需将你的 widget 包装在它们之一中即可进行裁剪。如果你想了解更多关于这些类的信息,可以参考 官方文档
ClipPath 接受一个 CustomClipper 作为参数。在大多数情况下,只需将一个 ShapeBorder 传递给 ShapeBorderClipper,它是 CustomClipper 的实现,通过 ShapeBorder 的外路径裁剪子项:
notion image
一个重要的要点是,每次在布局中使用裁剪器时,都会创建一个新的图层,这是一个相对昂贵的操作,因此在可能的情况下,使用装饰而非裁剪。

4. 自定义裁剪器

在某些情况下,我们需要更多的裁剪控制,例如,当裁剪应该依赖于内容或某些兄弟 widget 时。让我们创建一个票据形状的 widget,它的缺口将依赖于内容的大小:
notion image
这里的难点在于,顶部和底部的子项大小可能不同,并且缺口应该依赖于此。在 Flutter 中,可以使用 RenderBox 获取有关 widget 大小的信息,它是 RenderObject 的子类。如果你不熟悉 RenderObject,可以查阅 官方文档
首先,创建我们的布局,并为将顶部和底部子项分开的 SizedBox 添加一个 GlobalKey
GlobalKey 允许我们获取一个 widget 的 BuildContext,而我们需要上下文来获取 RenderObject。我们需要在票据 widget 的上下文中获取 SizedBox 的坐标。为此,我们需要相应的 RenderBox。由于我们这里没有使用 Slivers,因此可以安全地将 RenderObject 转换为 RenderBox
而裁剪器本身将是这样:
注意,Flutter 支持 路径操作,例如 differenceintersection 等。在这里,我们通过从一个圆角矩形中减去两个圆形,得到了所需的形状。
notion image
 
<ins/>
 
 
Loading...