Flutter源码解读

Container

2022-03-18  本文已影响0人  NetWork小贱

一、源码解读

// 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(
  height: 100,
  width: 200,
  padding: EdgeInsets.all(10),
  margin: EdgeInsets.all(6),
  decoration: BoxDecoration(
    color: Colors.red,
  ),
  clipBehavior: Clip.antiAlias,
  child: Text('Container 测试'),
)
上一篇下一篇

猜你喜欢

热点阅读