一、认识 Padding 组件
说到 Padding
,应该是大家入门 Flutter
时学习的第一批组件。它的功能非常简单,就是为子组件添加边距
。本文就回到 梦的起点
,来好好说说 Padding
组件的使用与其源码实现。
1.Padding 基本信息
下面是 Padding
组件类的定义
和 构造方法
,可以看出它继承自 SingleChildRenderObjectWidget
。实例化时必须传入 padding
参数,其中padding
的类型为 EdgeInsetsGeometry
。另外,还能传入一个 child
组件。
final EdgeInsetsGeometry padding;
2. Padding 组件的使用
比如下面的灰色盒子中有一个 Icon
组件。这时想让它四周有 10
的边距,我们就可以通过 Padding
组件完成。
如下 tag1
处,通过 Padding
组件,指定 padding
值,效果如下:
class PaddingDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: 100,
width: 100,
alignment: Alignment.topLeft,
color: Colors.black12,
child: const Padding( //<--- tag1
padding: EdgeInsets.all(10),
child: Icon(
Icons.ac_unit,
size: 40,
color: Colors.green,
),
),
);
}
}
无论是可见的还是不可见的组件,都有其尺寸区域。那加上 EdgeInsets.all(10)
边距之后,Padding
组件的尺寸区域是什么?下面有三个选项:
通过布局查看器可以知道,选 A
。我们都知道 padding
是内边距,margin
是外边距。从 Icon
组件的角度来看,似乎是为其添加了 外边距
来实现功能。但使用 Padding
组件,应该站在 Padding
组件的角度来看待问题:这样对于 child 组件就是通过 内边距
,拓展了 Padding
组件的占位区域。
由于 Padding
组件的边距区域是不可见
的,但占据空间,当其他组件并列排布,感觉上是两者之间有一个留白
。
其中被包裹的 Icon
组件本身并没有任何变化, Padding
组件可以在任意的组件外嵌套,这种 可插拔
的功能实现模式,是 Flutter 的一大特点,这样可以极大程度地降低组件间的耦合性
,使用起来更加灵活。Padding
组件的功能非常简单,但其中的思想是非常值得学习的:
几乎任何组件都可能使用到 padding
属性操作边距,然而框架并没有将 padding
属性作为 Widget
的公共属性。这些通用的属性无法预测有多少,如果都作为 Widget
的公共属性,维护起来自然麻烦, Widget
本身也就自然繁重。而分离出不同的组件实现功能,通过组件组合的进行使用,这样可以各取所需,自然高明许多。
3. 认识 EdgeInsetsGeometry
其实边距本身就是 左上右下
四个数字。Flutter
里将这四个数字抽象为 EdgeInsetsGeometry
。可以看出它是 const
构造,也就是说 EdgeInsetsGeometry
对象一旦创建,就无法修改该对象的
属性值。这也是为什么六个属性通过 get
方法获取,却没有 set
方法设置的原因。
EdgeInsetsGeometry
作为抽象类自然是无法直接使用的,其可用的实现类有 EdgeInsets
和 EdgeInsetsDirectional
。
我们最常使用的是 EdgeInsets
,通过 左上右下
来控制边距大小。其中维护了四个属性值,通过构造进行初始化。
主要构造有如下四个:
代码语言:javascript复制EdgeInsets.fromLTRB // 指定左、上、右、下、四个边距值(必须传入四参)
EdgeInsets.all // 指定一个值,用于左、上、右、下边距
EdgeInsets.only // 指定左、上、右、下、四个边距值(入参任意)
EdgeInsets.symmetric // 指定水平/竖直边距值
另外,由于其中重载了一下运算符,也就说明,两个 EdgeInsets
对象间可以进行运算符计算。
另外一个子类 EdgeInsetsDirectional
用的比较少,其功能基本一致,只不过是 开始、上、结束、下
的边界语义。一般来说,我们更习惯于 左上右下
的语义,而且 EdgeInsets
字母比较少。
二、 Padding 组件的源码实现
1. Padding 源码分析
它继承自 SingleChildRenderObjectWidget
就说明,该组件需要维护一个 RenderObject
对象的创建及更新。
在 createRenderObject
方法中,创建 RenderPadding
,padding
作为构造入参。在 updateRenderObject
中,对 RenderPadding
对象进行更新。也就是说,添加边距的功能是在 RenderPadding
中实现的。
2. RenderPadding 源码
RenderPadding
中最主要的操作是在 performLayout
方法中进步布局处理。可以看出,如果 child
为空,会将 RenderPadding
的尺寸根据 _resolvedPadding
进行处理。也就是说,即使没有子组件,Padding
也可以有占位区间。
如果子组件非空,那么 RenderPadding
的尺寸会根据 _resolvedPadding
和 子组件的尺寸进行计算得出。其实我们一直说 组件的占位区域
其实并不严谨,Widget
本身只是属性配置类而已,真正有尺寸 size
概念的是 RenderObject
。只不过 RenderObject
都有对应的 RenderObjectWidget
进行维护,我们说组件的尺寸,更加形象,容易理解。
最后,在 child
绘制时,可以看出做了偏移处理。这也是为什么加了 Padding
组件后,子组件绘制的位置会变化的原因。
那Padding
的使用方式到这里就介绍完毕,那本文到这里就结束了,谢谢观看,明天见~