前言:
这是我参与8月更文挑战的第 17 天,活动详情查看:8月更文挑战。为应掘金的八月更文挑战,我准备在本月挑选 31 个以前没有介绍过的组件,进行全面分析和属性介绍。这些文章将来会作为 Flutter 组件集录 的重要素材。希望可以坚持下去,你的支持将是我最大的动力~
一、认识 PhysicalShape 组件
大家应该没怎么用过 PhysicalShape ,从名字中可以看出它是 Physical + Shape ,也就是物理性状。从注释中可以看出,它的作用是将子组件根据路径裁剪,这时你应该会想到 ClipPath 组件。另外可以看出这个裁剪可以具有阴影效果。
1. PhysicalShape 基本信息
下面是 PhysicalShape 组件类的定义和 构造方法,可以看出它继承自 SingleChildRenderObjectWidget。实例化时必须传入 clipper 和 color 参数。
其中 clipper 参数的类型为 CustomClipper<Path> ,这和 ClipPath 中的 clipper 入参一样。
final CustomClipper<Path> clipper;
另外,可以指定影深 elevation、阴影颜色 shadowColor 、和裁剪行为 clipBehavior。可见默认情况想是没有影深的,阴影颜色为黑色。
2. PhysicalShape 的使用
对于 CustomClipper<Path> 对象,在 ClipPath 组件 一文中已经详细介绍了,这里不再赘述,可详见之。先看两个必须的入参 clipper 和 color 。如下,通过 CircleBorder 形状裁剪一个圆形,color 即为填充色。
class PhysicalShapeDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return PhysicalShape(
child: SizedBox(
width: 80,
height: 80,
),
clipBehavior: Clip.hardEdge,
clipper: const ShapeBorderClipper(
shape: CircleBorder(),
),
color: Colors.deepPurpleAccent,
);
}
}
PhysicalShape 可以通过 elevation 和 shadowColor 来指定阴影,效果如下:
class PhysicalShapeDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return PhysicalShape(
shadowColor: Colors.blueAccent,
elevation: 3,
child: SizedBox(
width: 80,
height: 80,
),
clipBehavior: Clip.hardEdge,
clipper: const ShapeBorderClipper(
shape: CircleBorder(),
),
color: Colors.deepPurpleAccent,
);
}
}
3.PhysicalShape 自定义形状裁剪
借用 ClipPath 组件 一文中自定义的形状裁剪看一下 PhysicalShape 的效果。如下是三角形裁剪 TriangleClipper 的效果,可以看出也具有阴影效果。
class TriangleClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
print(size);
Path path = Path()
..moveTo(0, size.height)
..relativeLineTo(size.width, 0)
..relativeLineTo(-size.width / 2, -size.height)
..close();
return path;
}
@override
bool shouldReclip(covariant CustomClipper<dynamic> oldClipper) {
return true;
}
}
下面是爱心路径裁剪效果:
class LoveClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
double fate = 18.5*size.height/100;
double width = size.width / 2;
double height = size.height / 4;
Path path = Path();
path.moveTo(width, height);
path.cubicTo(width, height, width + 1.1 * fate, height - 1.5 * fate, width + 2 * fate, height);
path.cubicTo(width + 2 * fate, height, width + 3.5 * fate, height + 2 * fate, width, height + 4 * fate);
path.moveTo(width, height);
path.cubicTo(width, height, width - 1.1 * fate, height - 1.5 * fate, width - 2 * fate, height);
path.cubicTo(width - 2 * fate, height, width - 3.5 * fate, height + 2 * fate, width, height + 4 * fate);
return path;
}
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
return true;
}
}
只要自定义形状,都可以通过 PhysicalShape 组件进行裁剪,并且具有阴影效果,但是这个阴影是固定的,用户无法设置阴影的偏移。另外,这个裁剪和 ClipPath 一样,PhysicalShape 可以裁剪任意组件。
二、 PhysicalShape 源码简看
它继承自 SingleChildRenderObjectWidget,会维护 RenderObject 对象。可以看出它通过 RenderPhysicalShape 渲染对象实现功能。其中颜色、裁剪器、阴影等属性都是构建 RenderPhysicalShape 的入参。
在 RenderPhysicalShape#paint 中,开始会进行阴影的绘制。
裁剪器被设置到 PhysicalModelLayer 对象上,也就是说该对象负责进行裁剪。
总的来说 PhysicalShape 还是非常简单的,就是一个加强版的 ClipPath ,具有阴影效果。下面分别是 PhysicalShape (左) 和 ClipPath (右)的裁剪效果。可以看出PhysicalShape 并没有小剪刀,说明它不是通过画布的路径裁剪实现的。
如果跟踪裁剪器的路径流向,我们可以发现最终 path 被用于 SceneBuilder#_pushPhysicalShape 的 native 方法中。所以说 PhysicalShape 和 ClipPath 在本质上的实现还是有差异的。
那PhysicalShape 的使用方式到这里就介绍完毕,那本文到这里就结束了,谢谢观看,明天见~