一、源码解读
// Container 继承于 StatelessWidget
class Container extends StatelessWidget {
/// 创建一个结合了常见绘画、定位和调整大小的小部件的小部件
Container({
Key? key,
this.alignment,
this.padding,
this.color,
this.decoration,
this.foregroundDecoration,
double? width,
double? height,
BoxConstraints? constraints,
this.margin,
this.transform,
this.transformAlignment,
this.child,
this.clipBehavior = Clip.none,
}) : assert(margin == null || margin.isNonNegative),
/// isNonNegative 是判断每个维度都是非负数,即是 top、right、bottom、left 都是大于等于零。
assert(padding == null || padding.isNonNegative),
/// debugAssertIsValid 判断对象是否有有效的配置
assert(decoration == null || decoration.debugAssertIsValid()),
assert(constraints == null || constraints.debugAssertIsValid()),
assert(clipBehavior != null),
/// decoration 为 null 时, clipBehavior 必须为 Clip.none
assert(decoration != null || clipBehavior == Clip.none),
/// decoration 和 color 不能同时设置,在 decoration 时可设置 decoration.color
assert(color == null || decoration == null,
'Cannot provide both a color and a decoration\n'
'To provide both, use "decoration: BoxDecoration(color: color)".',
),
/// 如果宽和高只有一个存在时,constraints 就是以存在的宽或者高生成的紧凑约束,否者就是自己设置的约束
constraints =
(width != null || height != null)
? constraints?.tighten(width: width, height: height)
?? BoxConstraints.tightFor(width: width, height: height)
: constraints,
super(key: key);
/// 此部件包含的子部件
final Widget? child;
/// 子部件在此部件中的位置
final AlignmentGeometry? alignment;
/// 此部件的内边距
final EdgeInsetsGeometry? padding;
/// 在子部件背后绘制的颜色
final Color? color;
/// 子部件前的装饰
final Decoration? foregroundDecoration;
/// 此部件的外边距
final EdgeInsetsGeometry? margin;
/// 绘制此部件应用的变换矩阵
final Matrix4? transform;
/// 部件进行转变时转变的定位
final AlignmentGeometry? transformAlignment;
/// Container.decoration 不为空时的裁剪形式
/// 如果Container.decoration为 null,则 clipBehavior 必须为 Clip.none
final Clip clipBehavior;
// 获取有效的内边距
EdgeInsetsGeometry? get _paddingIncludingDecoration {
// 如果 decoration 和 decoration.padding 不存在,则直接返回设置 padding 值
if (decoration == null || decoration!.padding == null)
return padding;
// 如果decoration 的 padding 存在,同时设置的 padding 也存在,则最后的 padding是两个padding 相加;
// 如果设置的 padding 不存在,则直接返回 decoration 的 padding
final EdgeInsetsGeometry? decorationPadding = decoration!.padding;
if (padding == null)
return decorationPadding;
return padding!.add(decorationPadding!);
}
@override
Widget build(BuildContext context) {
Widget? current = child;
// 子部件为空同时constraints也为空
if (child == null && (constraints == null || !constraints!.isTight)) {
// 当前部件就是限制最大宽度为零和最大高度为零以及约束填充父约束框的约束
current = LimitedBox(
maxWidth: 0.0,
maxHeight: 0.0,
child: ConstrainedBox(constraints: const BoxConstraints.expand()),
);
}
// 如果子部件定位存在,则当前部件使用 Align 部件设置定位,然后在赋值给当前部件对象
if (alignment != null)
current = Align(alignment: alignment!, child: current);
// 获取有效的内边距,如果存在,则使用 Padding 部件进行当前部件的内边距设置
final EdgeInsetsGeometry? effectivePadding = _paddingIncludingDecoration;
if (effectivePadding != null)
current = Padding(padding: effectivePadding, child: current);
// 如果color 颜色存在,则使用 ColoredBox 部件进行颜色的配置
if (color != null)
current = ColoredBox(color: color!, child: current);
if (clipBehavior != Clip.none) {
assert(decoration != null);
// 如果 clipBehavior 不等于 Clip.none 时,则使用 ClipPath 部件对当前部件裁剪配置
current = ClipPath(
// 裁剪的剪辑器,就是裁剪形状的轮廓
clipper: _DecorationClipper(
textDirection: Directionality.maybeOf(context),
decoration: decoration!,
),
clipBehavior: clipBehavior,
child: current,
);
}
// decoration 不为 null 时,则使用 DecoratedBox 部件设置此部件的装饰
if (decoration != null)
current = DecoratedBox(decoration: decoration!, child: current);
// foregroundDecoration 不为null, 则使用 DecoratedBox 部件进行此部件的前景装饰以及定位。
if (foregroundDecoration != null) {
current = DecoratedBox(
decoration: foregroundDecoration!,
position: DecorationPosition.foreground,
child: current,
);
}
// 如果 constraints 不为 null 时,则使用 ConstrainedBox 部件进行此部件的约束配置
if (constraints != null)
current = ConstrainedBox(constraints: constraints!, child: current);
// 此部件的外边距如果不为 null ,则使用 Padding 部件设置外边距
if (margin != null)
current = Padding(padding: margin!, child: current);
// 此部件transform 不为 null 时,则使用 Transform 部件根据 transformAlignment 转变定位,进行配置
if (transform != null)
current = Transform(transform: transform!, alignment: transformAlignment, child: current);
return current!;
}
// 调试模式下,属性填充
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment, showName: false, defaultValue: null));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior, defaultValue: Clip.none));
if (color != null)
properties.add(DiagnosticsProperty<Color>('bg', color));
else
properties.add(DiagnosticsProperty<Decoration>('bg', decoration, defaultValue: null));
properties.add(DiagnosticsProperty<Decoration>('fg', foregroundDecoration, defaultValue: null));
properties.add(DiagnosticsProperty<BoxConstraints>('constraints', constraints, defaultValue: null));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('margin', margin, defaultValue: null));
properties.add(ObjectFlagProperty<Matrix4>.has('transform', transform));
}
}
// 剪辑器
/// A clipper that uses [Decoration.getClipPath] to clip.
class _DecorationClipper extends CustomClipper<Path> {
_DecorationClipper({
TextDirection? textDirection,
required this.decoration,
}) : assert(decoration != null),
// 如果没有设置,默认从左到右
textDirection = textDirection ?? TextDirection.ltr;
final TextDirection textDirection;
final Decoration decoration;
@override
Path getClip(Size size) {
// 根据大小和文字方向获取裁剪路径
return decoration.getClipPath(Offset.zero & size, textDirection);
}
@override
bool shouldReclip(_DecorationClipper oldClipper) {
/// 是否重新剪辑
return oldClipper.decoration != decoration
|| oldClipper.textDirection != textDirection;
}
}
二、总结
- Container 是根据配置,然后添加相应的小部件来完成的。
- Container 的最大层级:Transform -> Padding -> ConstrainedBox->DecoratedBox->DecoratedBox->ClipPath->ColorBox->Padding->Align->child
- Container 有一些属性不能同时设置和一些默认搭配设置,比如 color 和 decoration 不能同时设置 ; decoration 为 null 时,clipBehavior 必须为 Clip.none
三、实例
Container(
height: 100,
width: 200,
padding: EdgeInsets.all(10),
margin: EdgeInsets.all(6),
decoration: BoxDecoration(
color: Colors.red,
),
clipBehavior: Clip.antiAlias,
child: Text('Container 测试'),
)