在flutter中给布局和控件设置padding用到的是EdgeInsetsGeometry类,该类位于依赖库中Dart Packages库的flutter/lib/src/painting/edge_insets.dart文件中
设置padding有两种用法
第一种在布局widget中设置padding属性,例如:
new Container(
padding: const EdgeInsets.all(8.0),
);
new ListView(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
);
第二种直接使用Padding widgets设置paddng属性,然后在Padding嵌套其他widget
new Padding(
padding: const EdgeInsets.only(left: 8.0),
child: new Text("padding"),
);
他们都用到了EdgeInsetsGeometry类的子类EdgeInsets
EdgeInsetsGeometry类有三个子类分别是:
EdgeInsets
EdgeInsetsDirectional
_MixedEdgeInsets:私有子类
EdgeInsetsGeometry是一个抽象类,其中默认使用的子类是私有子类_MixedEdgeInsets,很多非抽象方法中的操作直接由这个类完成
定义了上下左右,开始,结束6个变量
double get _bottom;
double get _end;
double get _left;
double get _right;
double get _start;
double get _top;
这里只是先定义了变量的get抽象方法,三个子类中会定义具体的变量
然后在这6个变量的基础上继续定义了新的变量
/// 判断所有的变量是否都非负,即是否全部大于0
bool get isNonNegative {
return _left >= 0.0
&& _right >= 0.0
&& _start >= 0.0
&& _end >= 0.0
&& _top >= 0.0
&& _bottom >= 0.0;
}
/// 水平方向上的间距之和
double get horizontal => _left + _right + _start + _end;
/// 垂直防线上的间距之和
double get vertical => _top + _bottom;
/// 整个padding占用空间的大小,用Size类表示
Size get collapsedSize => Size(horizontal, vertical);
/// 用当前的水平方向的值和垂直方向的值将给定的Size宽高增加,返回新的Size
Size inflateSize(Size size) {
return Size(size.width + horizontal, size.height + vertical);
}
/// 用给出的Size的宽高分别减去当前的水平方向的值和垂直方向的值,返回新的Size
Size deflateSize(Size size) {
return Size(size.width - horizontal, size.height - vertical);
}
下面是抽象属性flipped,含义是使用当前类的6个属性创建一个新的EdgeInsetsGeometry变量,子类中重写该方法,并返回子类的类型。该方法中默认使用了私有子类_MixedEdgeInsets的命名构造方法
EdgeInsetsGeometry get flipped => _MixedEdgeInsets.fromLRSETB(_right, _left, _end, _start, _bottom, _top);
下面是一些运算相关的方法
1对本类的运算
/// 两个EdgeInsetsGeometry相减,即将6个属性分别相减之后,
/// 使用私有内部类_MixedEdgeInsets的fromLRSETB方法创建新的对象
EdgeInsetsGeometry subtract(EdgeInsetsGeometry other) {
return _MixedEdgeInsets.fromLRSETB(
_left - other._left,
_right - other._right,
_start - other._start,
_end - other._end,
_top - other._top,
_bottom - other._bottom,
);
}
/// 返回两个EdgeInsetsGeometry之和,即将6个属性全部相加之后
/// 使用私有内部类_MixedEdgeInsets的fromLRSETB方法创建新的对象
EdgeInsetsGeometry add(EdgeInsetsGeometry other) {
return _MixedEdgeInsets.fromLRSETB(
_left + other._left,
_right + other._right,
_start + other._start,
_end + other._end,
_top + other._top,
_bottom + other._bottom,
);
}
2 重写一些操作符 重写了=运算,对所有类型进行比较,剩下的运算是对double变量进行算术运算,都是抽象方法,由子类实现
///只有当类型是EdgeInsetsGeometry并且6个属性都相等的情况下才算是相等
@override
bool operator ==(dynamic other) {
if (other is! EdgeInsetsGeometry)
return false;
final EdgeInsetsGeometry typedOther = other;
return _left == typedOther._left
&& _right == typedOther._right
&& _start == typedOther._start
&& _end == typedOther._end
&& _top == typedOther._top
&& _bottom == typedOther._bottom;
}
/// 对EdgeInsetsGeometry取反,等同于 *-1.0 运算
EdgeInsetsGeometry operator -();
EdgeInsetsGeometry operator *(double other);//乘
EdgeInsetsGeometry operator /(double other);//除
EdgeInsetsGeometry operator ~/(double other);//整除
EdgeInsetsGeometry operator %(double other);//取余
抽象的运算分别是乘,除,整除,取余,剩下加减等操作在子类中定义并实现
静态方法lerp
static EdgeInsetsGeometry lerp(EdgeInsetsGeometry a, EdgeInsetsGeometry b, double t) {
assert(t != null);
if (a == null && b == null)
return null;
if (a == null)
return b * t;
if (b == null)
return a * (1.0 - t);
if (a is EdgeInsets && b is EdgeInsets)
return EdgeInsets.lerp(a, b, t);
if (a is EdgeInsetsDirectional && b is EdgeInsetsDirectional)
return EdgeInsetsDirectional.lerp(a, b, t);
return _MixedEdgeInsets.fromLRSETB(
ui.lerpDouble(a._left, b._left, t),
ui.lerpDouble(a._right, b._right, t),
ui.lerpDouble(a._start, b._start, t),
ui.lerpDouble(a._end, b._end, t),
ui.lerpDouble(a._top, b._top, t),
ui.lerpDouble(a._bottom, b._bottom, t),
);
}
该方法返回新的EdgeInsetsGeometry,对参数a和参数b分别进行判断
如果a,b都是空则返回空
如果a空b不空,返回b*t
如果a不空b空,返回a*(1.0-t)
如果a与b类型是相同的子类,则调用对应子类的lerp方法,子类的lerp方法与父类的类似。
以上条件都不满足则,对a和b的6个参数分别与参数t进行lerpDouble运算,并用返回的值创建一个新的_MixedEdgeInsets对象返回。
这个方法的含义根据注释以及运算可以得出,是对两个EdgeInsetsGeometry对象中的每个参数进行线性插值运算,类似于属性动画的匀速渐变效果的运算,参数t是插值因数,从最终调用的lerpDouble可以得出结论代码如下:
/// Linearly interpolate between two numbers.
double lerpDouble(num a, num b, double t) {
if (a == null && b == null)
return null;
a ??= 0.0;
b ??= 0.0;
return a + (b - a) * t;
}
这个方法可以理解成算出,从a值到b值在给定的因数t下得出的值,
例如要算出在10秒内从0数到20,每一秒数到几,就可以用上面的公式
1秒 :lerpDouble(0,20,1/10)= 2
2秒:lerpDouble(0,20,2/10)= 4
3秒:lerpDouble(0,20,3/10)= 6
resolve方法
EdgeInsets resolve(TextDirection direction);
抽象方法,当前对象根据TextDirection转换成EdgeInsets对象,子类实现
EdgeInsets
这个类定义了6个变量中的,_left,_top,_right,_bottom对应的变量,分别是left,top,right,bottom
将_start,_end设置成默认返回0.0,
此类中的所有操作都使用四个方向的值即left,top,right,bottom来执行,start与end保持不变。
只支持left-right,不支持right-left反向布局
EdgeInsetsDirectional
这个类定义了6个变量中的,_start,_top,_end,_bottom对应的变量,分别是,start,top,end,bottom
将_left,_right设置成默认返回0.0,
此类中的所有操作都使用四个方向的值即start,top,end,bottom来执行,left与right保持不变。
支持left-right布局,right-left反向布局
_MixedEdgeInsets:私有子类
,外界不能使用,其他两个外界可以使用
在父类中有些非抽象方法中,使用的就是这个私有子类完成的逻辑,它定义了全部6个变量。
常用的五个命名构造函数
/// 命名构造函数,根据left,top,right,bottom四个方向的值创建EdgeInsets
const EdgeInsets.fromLTRB(this.left, this.top, this.right, this.bottom);
/// 把上下左右四个方向的间距都设置成给定的值
const EdgeInsets.all(double value)
: left = value,
top = value,
right = value,
bottom = value;
/// 可设置上下左右 的任意0个1个或多个值,没有设置的值默认为0
const EdgeInsets.only({
this.left = 0.0,
this.top = 0.0,
this.right = 0.0,
this.bottom = 0.0
});
/// 设置左右对称的间距horizontal,赋值给left和right
/// 或者上下对称的间距vertical,赋值给top和bottom
const EdgeInsets.symmetric({
double vertical = 0.0,
double horizontal = 0.0,
}) : left = horizontal,
top = vertical,
right = horizontal,
bottom = vertical;
/// 根据WindowPadding和比率设置四周间距
EdgeInsets.fromWindowPadding(ui.WindowPadding padding, double devicePixelRatio)
: left = padding.left / devicePixelRatio,
top = padding.top / devicePixelRatio,
right = padding.right / devicePixelRatio,
bottom = padding.bottom / devicePixelRatio;
没有间距可以使用EdgeInsets.zero,可以看出它调用了EdgeInsets.only()方法
/// An [EdgeInsets] with zero offsets in each direction.
static const EdgeInsets zero = EdgeInsets.only();
下面两个方法是对Rect的操作
///把所给的rect按照间隔扩大
Rect inflateRect(Rect rect) {
return Rect.fromLTRB(rect.left - left, rect.top - top, rect.right + right, rect.bottom + bottom);
}
///把所给的rect按照间隔缩小
Rect deflateRect(Rect rect) {
return Rect.fromLTRB(rect.left + left, rect.top + top, rect.right - right, rect.bottom - bottom);
}
Rect代表一个区域,放大就是在坐标上左侧向左移动,右侧向右移动,上边向上移动,下边向下移动,所以flutter的坐标轴与安卓的坐标轴保持不变
缩小相反,left增大,right减小,top增大,bottom减小
下面是EdgeInsets对象之间的操作
@override
EdgeInsetsGeometry subtract(EdgeInsetsGeometry other) {
if (other is EdgeInsets)
return this - other;
return super.subtract(other);
}
@override
EdgeInsetsGeometry add(EdgeInsetsGeometry other) {
if (other is EdgeInsets)
return this + other;
return super.add(other);
}
/// Returns the difference between two [EdgeInsets].
EdgeInsets operator -(EdgeInsets other) {
return EdgeInsets.fromLTRB(
left - other.left,
top - other.top,
right - other.right,
bottom - other.bottom,
);
}
/// Returns the sum of two [EdgeInsets].
EdgeInsets operator +(EdgeInsets other) {
return EdgeInsets.fromLTRB(
left + other.left,
top + other.top,
right + other.right,
bottom + other.bottom,
);
}
subtract内部调用了 重写运算符 - 内部实现就是用上下左右四个数相减的结果,调用命名构造函数,创建新的EdgeInsets对象
add方法类似
还有父类中定义的 重写运算符实现的方法类似,取反符号可以看做是*-1.0得出的结果
resolve方法返回自己本身,因为这个方法含义就是将其他子类转换成EdgeInsets,所以不需要处理
@override
EdgeInsets resolve(TextDirection direction) => this;
copyWith调用了only方法
EdgeInsets copyWith({
double left,
double top,
double right,
double bottom,
}) {
return EdgeInsets.only(
left: left ?? this.left,
top: top ?? this.top,
right: right ?? this.right,
bottom: bottom ?? this.bottom,
);
}
它只有两个构造方法
const EdgeInsetsDirectional.fromSTEB(this.start, this.top, this.end, this.bottom);
const EdgeInsetsDirectional.only({
this.start = 0.0,
this.top = 0.0,
this.end = 0.0,
this.bottom = 0.0
});
resolve方法
@override
EdgeInsets resolve(TextDirection direction) {
assert(direction != null);
switch (direction) {
case TextDirection.rtl:
return EdgeInsets.fromLTRB(end, top, start, bottom);
case TextDirection.ltr:
return EdgeInsets.fromLTRB(start, top, end, bottom);
}
return null;
}
在EdgeInsetsDirectional中,根据TextDirection的方向返回不同的EdgeInsets,
如果是右到左方向则end作为left,start作为right
如果是左到右方向则start作为left,end作为right,正常使用
唯一的构造函数,6个变量都使用了
const _MixedEdgeInsets.fromLRSETB(this._left, this._right, this._start, this._end, this._top, this._bottom);
resolve方法
@override
EdgeInsets resolve(TextDirection direction) {
assert(direction != null);
switch (direction) {
case TextDirection.rtl:
return EdgeInsets.fromLTRB(_end + _left, _top, _start + _right, _bottom);
case TextDirection.ltr:
return EdgeInsets.fromLTRB(_start + _left, _top, _end + _right, _bottom);
}
return null;
}
这个方法也对TextDirection的方向进行了判断
如果是右到左方向,用end加上left作为新的left,start加上right作为新的right
如果是左到右方向,用start加上left作为新的left,end加上right作为新的right