一、源碼解讀
// Container 繼承于 StatelessWidget
class Container extends StatelessWidget {
/// 創(chuàng)建一個結(jié)合了常見繪畫、定位和調(diào)整大小的小部件的小部件
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 是判斷每個維度都是非負(fù)數(shù),即是 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 不能同時設(shè)置,在 decoration 時可設(shè)置 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 就是以存在的寬或者高生成的緊湊約束,否者就是自己設(shè)置的約束
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;
/// 此部件的內(nèi)邊距
final EdgeInsetsGeometry? padding;
/// 在子部件背后繪制的顏色
final Color? color;
/// 子部件前的裝飾
final Decoration? foregroundDecoration;
/// 此部件的外邊距
final EdgeInsetsGeometry? margin;
/// 繪制此部件應(yīng)用的變換矩陣
final Matrix4? transform;
/// 部件進(jìn)行轉(zhuǎn)變時轉(zhuǎn)變的定位
final AlignmentGeometry? transformAlignment;
/// Container.decoration 不為空時的裁剪形式
/// 如果Container.decoration為 null,則 clipBehavior 必須為 Clip.none
final Clip clipBehavior;
// 獲取有效的內(nèi)邊距
EdgeInsetsGeometry? get _paddingIncludingDecoration {
// 如果 decoration 和 decoration.padding 不存在,則直接返回設(shè)置 padding 值
if (decoration == null || decoration!.padding == null)
return padding;
// 如果decoration 的 padding 存在,同時設(shè)置的 padding 也存在,則最后的 padding是兩個padding 相加;
// 如果設(shè)置的 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)) {
// 當(dāng)前部件就是限制最大寬度為零和最大高度為零以及約束填充父約束框的約束
current = LimitedBox(
maxWidth: 0.0,
maxHeight: 0.0,
child: ConstrainedBox(constraints: const BoxConstraints.expand()),
);
}
// 如果子部件定位存在,則當(dāng)前部件使用 Align 部件設(shè)置定位,然后在賦值給當(dāng)前部件對象
if (alignment != null)
current = Align(alignment: alignment!, child: current);
// 獲取有效的內(nèi)邊距,如果存在,則使用 Padding 部件進(jìn)行當(dāng)前部件的內(nèi)邊距設(shè)置
final EdgeInsetsGeometry? effectivePadding = _paddingIncludingDecoration;
if (effectivePadding != null)
current = Padding(padding: effectivePadding, child: current);
// 如果color 顏色存在,則使用 ColoredBox 部件進(jìn)行顏色的配置
if (color != null)
current = ColoredBox(color: color!, child: current);
if (clipBehavior != Clip.none) {
assert(decoration != null);
// 如果 clipBehavior 不等于 Clip.none 時,則使用 ClipPath 部件對當(dāng)前部件裁剪配置
current = ClipPath(
// 裁剪的剪輯器,就是裁剪形狀的輪廓
clipper: _DecorationClipper(
textDirection: Directionality.maybeOf(context),
decoration: decoration!,
),
clipBehavior: clipBehavior,
child: current,
);
}
// decoration 不為 null 時,則使用 DecoratedBox 部件設(shè)置此部件的裝飾
if (decoration != null)
current = DecoratedBox(decoration: decoration!, child: current);
// foregroundDecoration 不為null, 則使用 DecoratedBox 部件進(jìn)行此部件的前景裝飾以及定位。
if (foregroundDecoration != null) {
current = DecoratedBox(
decoration: foregroundDecoration!,
position: DecorationPosition.foreground,
child: current,
);
}
// 如果 constraints 不為 null 時,則使用 ConstrainedBox 部件進(jìn)行此部件的約束配置
if (constraints != null)
current = ConstrainedBox(constraints: constraints!, child: current);
// 此部件的外邊距如果不為 null ,則使用 Padding 部件設(shè)置外邊距
if (margin != null)
current = Padding(padding: margin!, child: current);
// 此部件transform 不為 null 時,則使用 Transform 部件根據(jù) transformAlignment 轉(zhuǎn)變定位,進(jìn)行配置
if (transform != null)
current = Transform(transform: transform!, alignment: transformAlignment, child: current);
return current!;
}
// 調(diào)試模式下,屬性填充
@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),
// 如果沒有設(shè)置,默認(rèn)從左到右
textDirection = textDirection ?? TextDirection.ltr;
final TextDirection textDirection;
final Decoration decoration;
@override
Path getClip(Size size) {
// 根據(jù)大小和文字方向獲取裁剪路徑
return decoration.getClipPath(Offset.zero & size, textDirection);
}
@override
bool shouldReclip(_DecorationClipper oldClipper) {
/// 是否重新剪輯
return oldClipper.decoration != decoration
|| oldClipper.textDirection != textDirection;
}
}
二、總結(jié)
- Container 是根據(jù)配置,然后添加相應(yīng)的小部件來完成的。
- Container 的最大層級:Transform -> Padding -> ConstrainedBox->DecoratedBox->DecoratedBox->ClipPath->ColorBox->Padding->Align->child
- Container 有一些屬性不能同時設(shè)置和一些默認(rèn)搭配設(shè)置,比如 color 和 decoration 不能同時設(shè)置 ; decoration 為 null 時,clipBehavior 必須為 Clip.none
三、實(shí)例
Container(
height: 100,
width: 200,
padding: EdgeInsets.all(10),
margin: EdgeInsets.all(6),
decoration: BoxDecoration(
color: Colors.red,
),
clipBehavior: Clip.antiAlias,
child: Text('Container 測試'),
)