Container

一、源码解读

// 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 测试'),
)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容