Container

一、源碼解讀

// 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 測試'),
)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內(nèi)容