【Flutter 组件集录】PhysicalShape | 8 月更文挑战

2,096 阅读3分钟
前言:

这是我参与8月更文挑战的第 17 天,活动详情查看:8月更文挑战。为应掘金的八月更文挑战,我准备在本月挑选 31 个以前没有介绍过的组件,进行全面分析和属性介绍。这些文章将来会作为 Flutter 组件集录 的重要素材。希望可以坚持下去,你的支持将是我最大的动力~

本系列组件文章列表
1.NotificationListener2.Dismissible3.Switch
4.Scrollbar5.ClipPath6.CupertinoActivityIndicator
7.Opacity8.FadeTransition9. AnimatedOpacity
10. FadeInImage11. Offstage12. TickerMode
13. Visibility14. Padding15. AnimatedContainer
16.CircleAvatar17.PhysicalShape18.Divider
19.Flexible、Expanded 和 Spacer 20.Card

一、认识 PhysicalShape 组件

大家应该没怎么用过 PhysicalShape ,从名字中可以看出它是 Physical + Shape ,也就是物理性状。从注释中可以看出,它的作用是将子组件根据路径裁剪,这时你应该会想到 ClipPath 组件。另外可以看出这个裁剪可以具有阴影效果。


1. PhysicalShape 基本信息

下面是 PhysicalShape 组件类的定义构造方法,可以看出它继承自 SingleChildRenderObjectWidget。实例化时必须传入 clippercolor 参数。

其中 clipper 参数的类型为 CustomClipper<Path> ,这和 ClipPath 中的 clipper 入参一样。

final CustomClipper<Path> clipper;

另外,可以指定影深 elevation、阴影颜色 shadowColor 、和裁剪行为 clipBehavior。可见默认情况想是没有影深的,阴影颜色为黑色。


2. PhysicalShape 的使用

对于 CustomClipper<Path> 对象,在 ClipPath 组件 一文中已经详细介绍了,这里不再赘述,可详见之。先看两个必须的入参 clippercolor 。如下,通过 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 可以通过 elevationshadowColor 来指定阴影,效果如下:

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#_pushPhysicalShapenative 方法中。所以说 PhysicalShapeClipPath 在本质上的实现还是有差异的。

PhysicalShape 的使用方式到这里就介绍完毕,那本文到这里就结束了,谢谢观看,明天见~