Flutter之SizedBox学习使用

15,555 阅读8分钟

sizebox_learning

主要是学习SizeBox相关控件的使用方法.

  • 一般的SizeBox()构造方法:传width、height、child,SizedBox会强制设置它的孩子的宽度或者高度为指定值
  • SizeBox.expand():可以使SizedBox的大小充满parent的布局,相当于设置了SizedBox的宽度和高度为double.infinity(无穷大)。
  • SizeBox.fromSize():创建一个指定Size的SizedBox
  • FractionallySizedBox():可以用百分比来控制sizebox的大小。 widthFactor,heightFactor参数就是相对于父控件的比例。 alignment:可以设置sizebox在父控件里面的相对位置。
  • SizedOverflowBox():通过设置Size,允许它的child控件,溢出它的父控件,进行绘制,不会报OverFlow的错误。
  • OverflowBox():通过设置最大最小的宽高,OverflowBox允许它的child控件,溢出它的父控件,进行绘制,不会报OverFlow的错误。
  • LimtedBox():一个可以限制子控件的最大宽高的控件,child只能在这区域内进行绘制

使用场景:

  • 一般是用来限制孩子控件的大小。

  • 还有这么一种场景也可以使用SizeBox,就是可以代替padding和container,然后 用来设置两个控件之间的间距,比如在行或列中就可以设置两个控件之间的间距 主要是可以比使用一个padding或者container简单方便 (在Flutter中可能用不同的控件可以实现到相同的目的,尽量使用越简单的widget来实现)

实践

在Column中构造不同的SizedBox。

这里我利用了random_pk库,这是一个可以生成随机颜色的Container,方便我观察控件的布局区域。

这个库我也经常在开发中使用,挺方便的一个库。

Column buildColumn() {
    return new Column(
      mainAxisSize: MainAxisSize.max,
      children: <Widget>[
        new SizedBox(
          width: 100,
          height: 100,
          child: new RandomContainer(
            child: new Text("指定height和width"),
          ),
        ),
        new SizedBox(
          width: 100,
          height: 100,
        ),
        new SizedBox(
          width: 100,
          child: new RandomContainer(
            child: new Text("指定width"),
          ),
        ),
        new SizedBox(
          height: 100,
          child: new RandomContainer(
            child: new Text("指定height"),
          ),
        ),
        new ConstrainedBox(
          constraints: BoxConstraints(minHeight: 50),
          child: new RandomContainer(
            child: new Text("ConstrainedBox,minHeight: 50"),
          ),
        ),

        new RandomContainer(
          width: 100,
          height: 100,
          child: new OverflowBox(
            maxWidth: 200,
            maxHeight: 200,
            child: new RandomContainer(
              width: 300,
              height: 200,
              child: new Text("OverflowBox"),
            ),
          ),
        ),

        new LimitedBox(
          maxHeight: 100,
          child: new RandomContainer(
            height: 200,
            width: double.infinity,
            child: new Text("LimitedBox"),
          ),
        )
      ],
    );
  }
 /*
  一般的用法
  Sizebox会强制它的孩子的大小
   */
  buildSizebox() {
    return new RandomContainer(
      child: new SizedBox(
        width: 100,
        height: 100,
        child: new FlutterLogo(),
      ),
    );
  }

  /*
   * Sizebox.expand
   *会充满sizebox的父控件
   */
  RandomContainer buildSizeboxExpand() {
    return new RandomContainer(
        child: new SizedBox.expand(
      child: new FlutterLogo(),
    ));
  }

  /*
   * Sizebox的另外一种使用方法.
    参数是Size
   * 比如下面利用屏幕的size信息进行设置SizeBox的大小
   */
  buildSizeboxFromSize() {
    var deviceSize = MediaQuery.of(context).size;
    return new RandomContainer(
      child: new SizedBox.fromSize(
        size: Size(100, 100),
        child: FlutterLogo(),
      ),
//      child: new SizedBox.fromSize(size: deviceSize/2,child: FlutterLogo(),),
    );
  }

  /*
  SizedOverflowBox,允许sizebox里面的child大小比sizebox大,导致溢出显示
  而且还有个alignment的参数,可以用来设置sizebox里面的child的位置
   */
  buildSizedOverflowBox() {
    return new RandomContainer(
      child: new SizedOverflowBox(
        size: Size(100, 100),
        alignment: Alignment.bottomRight,
        child: new FlutterLogo(
          size: 50,
        ),
      ),
    );
  }

  /*
FractionallySizedBox,可以用百分比来控制sizebox的大小
widthFactor,heightFactor参数就是相对于父控件的比例
alignment:可以设置sizebox在父控件里面的相对位置
   */
  buildFractionallySizedBox() {
    return new RandomContainer(
      width: 200,
      height: 200,
      child: new FractionallySizedBox(
        alignment: Alignment.topCenter,
        widthFactor: 0.4,
        heightFactor: 0.4,
        child: new FlutterLogo(),
      ),
    );
  }


  /*
   * 在行或者列中,使用SizedBox来代替padding或者container来设置间距
   */
  buildColumn() {
    return new SafeArea(
        child: new Column(
      children: <Widget>[
        new Text(
          "11111",
          style: new TextStyle(fontSize: 30),
        ),
        new SizedBox(height: 20,),
        new Text(
          "22222",
          style: new TextStyle(fontSize: 30),
        ),
        new SizedBox(height: 20,),
        new Text(
          "33333",
          style: new TextStyle(fontSize: 30),
        ),
      ],
    ));
  }
}

代码地址

github.com/LXD31256949…

SizedBox的源码

/// A box with a specified size。
/// 意思就是一个指定大小的盒子,SizedBox会强制设置它的孩子的宽度或者高度为指定值。
///
/// SizedBox的构造函数的参数:width,height, child。
/// 如果width,height, child都指定的话,那SizedBox和child的宽度和高度都为指定值。
/// 如果只指定width和child的话,那child的宽度为指定值,child的高度自适应,SizedBox的高度将跟child的高度一样。
/// 如果只指定height和child的话,那child的高度为指定值,child的宽度自适应,SizedBox的宽度将跟child的宽度一样。
/// 如果没有指定child,SizedBox的大小为指定的大小。
///
/// [new SizedBox.expand]的构造方法可以使SizedBox的大小充满parent的布局,相当于设置了SizedBox
/// 的宽度和高度为[double.infinity](无穷大)。
class SizedBox extends SingleChildRenderObjectWidget {
  /// 创建一个SizedBox,参数[width]和[height] 可以为空,表示width或者height对应的方向不受约束,会自适应。
  const SizedBox({ Key key, this.width, this.height, Widget child })
    : super(key: key, child: child);

  /// 创建一个SizedBox,会去充满父布局
  const SizedBox.expand({ Key key, Widget child })
    : width = double.infinity,
      height = double.infinity,
      super(key: key, child: child);

  /// 创建一个SizeBox,尽可能地小。
  const SizedBox.shrink({ Key key, Widget child })
    : width = 0.0,
      height = 0.0,
      super(key: key, child: child);

  /// 创建一个指定Size的SizedBox。
  SizedBox.fromSize({ Key key, Widget child, Size size })
    : width = size?.width,
      height = size?.height,
      super(key: key, child: child);

  /// 如果width不为空,会要求它的child具有这个宽度
  final double width;

  /// 如果height不为空,会要求它的height具有这个高度
  final double height;

  @override
  RenderConstrainedBox createRenderObject(BuildContext context) {
    return RenderConstrainedBox(
      additionalConstraints: _additionalConstraints,
    );
  }

  BoxConstraints get _additionalConstraints {
    return BoxConstraints.tightFor(width: width, height: height);
  }

  @override
  void updateRenderObject(BuildContext context, RenderConstrainedBox renderObject) {
    renderObject.additionalConstraints = _additionalConstraints;
  }

  @override
  String toStringShort() {
    String type;
    if (width == double.infinity && height == double.infinity) {
      type = '$runtimeType.expand';
    } else if (width == 0.0 && height == 0.0) {
      type = '$runtimeType.shrink';
    } else {
      type = '$runtimeType';
    }
    return key == null ? '$type' : '$type-$key';
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    DiagnosticLevel level;
    if ((width == double.infinity && height == double.infinity) ||
        (width == 0.0 && height == 0.0)) {
      level = DiagnosticLevel.hidden;
    } else {
      level = DiagnosticLevel.info;
    }
    properties.add(DoubleProperty('width', width, defaultValue: null, level: level));
    properties.add(DoubleProperty('height', height, defaultValue: null, level: level));
  }
}

/// ConstrainedBox是一个小部件,可以对它的child增加额外的约束。
/// 例如,你想让child的最小高度为50.0,那么可以使用const BoxConstraints(minHeight: 50.0)作为[constraints]属性。
class ConstrainedBox extends SingleChildRenderObjectWidget {
  /// 创建一个小部件,对它的孩子增加一些额外的约束
  ///
  /// [constraints]参数不能为空。
  ConstrainedBox({
    Key key,
    @required this.constraints,
    Widget child
  }) : assert(constraints != null),
       assert(constraints.debugAssertIsValid()),
       super(key: key, child: child);

  /// 对child增加的约束constraints
  final BoxConstraints constraints;

  @override
  RenderConstrainedBox createRenderObject(BuildContext context) {
    return RenderConstrainedBox(additionalConstraints: constraints);
  }

  @override
  void updateRenderObject(BuildContext context, RenderConstrainedBox renderObject) {
    renderObject.additionalConstraints = constraints;
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<BoxConstraints>('constraints', constraints, showName: false));
  }
}


/// A widget that sizes its child to a fraction of the total available space.
/// 一个小部件,会使它的子部件大小,调整为剩余可用空间的一部分,可以使用百分比
/// See also:
///
///  * [Align], 根据子元素的大小,调整自己的大小,根据[Alignment]的参数值,调整子元素的位置。
class FractionallySizedBox extends SingleChildRenderObjectWidget {

  ///  [widthFactor] and [heightFactor] 不能是负数。
  const FractionallySizedBox({
    Key key,
    this.alignment = Alignment.center,
    this.widthFactor,
    this.heightFactor,
    Widget child,
  }) : assert(alignment != null),
       assert(widthFactor == null || widthFactor >= 0.0),
       assert(heightFactor == null || heightFactor >= 0.0),
       super(key: key, child: child);

  /// If non-null, the fraction of the incoming width given to the child.
  ///
  /// If non-null, the child is given a tight width constraint that is the max
  /// incoming width constraint multiplied by this factor.
  ///
  /// If null, the incoming width constraints are passed to the child
  /// unmodified.
  final double widthFactor;

  /// If non-null, the fraction of the incoming height given to the child.
  ///
  /// If non-null, the child is given a tight height constraint that is the max
  /// incoming height constraint multiplied by this factor.
  ///
  /// If null, the incoming height constraints are passed to the child
  /// unmodified.
  final double heightFactor;

  /// 根据alignment对齐孩子,默认值为[Alignment.center]
  /// Alignment(this.x, this.y),利用x和y的值来控制水平方向和竖直方向的对齐。
  /// 比如x为 -1.0,表示child的左边缘和parent的左边缘对齐,x为1则表示child的右边缘和
  /// parent的右边缘对齐。
  /// x为0则表示child的中心和parent的中心对齐。
  /// 其他值利用线性插值的方法去计算。默认会有一些自定义的常量,比如Alignment.center表示(0,0)
  final AlignmentGeometry alignment;

  @override
  RenderFractionallySizedOverflowBox createRenderObject(BuildContext context) {
    return RenderFractionallySizedOverflowBox(
      alignment: alignment,
      widthFactor: widthFactor,
      heightFactor: heightFactor,
      textDirection: Directionality.of(context),
    );
  }

  @override
  void updateRenderObject(BuildContext context, RenderFractionallySizedOverflowBox renderObject) {
    renderObject
      ..alignment = alignment
      ..widthFactor = widthFactor
      ..heightFactor = heightFactor
      ..textDirection = Directionality.of(context);
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
    properties.add(DoubleProperty('widthFactor', widthFactor, defaultValue: null));
    properties.add(DoubleProperty('heightFactor', heightFactor, defaultValue: null));
  }
}

/// 一个可以限制子控件的最大宽高的控件。
/// 如果LimtedBox本身控件的最大宽度是没有限制的话,那么它的child控件的最大宽度就是所设置的[maxWidth]。
/// 如果LimtedBox本身控件的最大高度是没有限制的话,那么它的child控件的最大高度就是所设置的[maxHeight]。
class LimitedBox extends SingleChildRenderObjectWidget {
  /// Creates a box that limits its size only when it's unconstrained.
  /// The [maxWidth] and [maxHeight] arguments must not be null and must not be
  /// negative.
  const LimitedBox({
    Key key,
    this.maxWidth = double.infinity,
    this.maxHeight = double.infinity,
    Widget child,
  }) : assert(maxWidth != null && maxWidth >= 0.0),
       assert(maxHeight != null && maxHeight >= 0.0),
       super(key: key, child: child);

  /// The maximum width limit to apply in the absence of a
  /// [BoxConstraints.maxWidth] constraint.
  final double maxWidth;

  /// The maximum height limit to apply in the absence of a
  /// [BoxConstraints.maxHeight] constraint.
  final double maxHeight;

  @override
  RenderLimitedBox createRenderObject(BuildContext context) {
    return RenderLimitedBox(
      maxWidth: maxWidth,
      maxHeight: maxHeight
    );
  }

  @override
  void updateRenderObject(BuildContext context, RenderLimitedBox renderObject) {
    renderObject
      ..maxWidth = maxWidth
      ..maxHeight = maxHeight;
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DoubleProperty('maxWidth', maxWidth, defaultValue: double.infinity));
    properties.add(DoubleProperty('maxHeight', maxHeight, defaultValue: double.infinity));
  }
}

/// 一个小部件,OverflowBox允许它的child控件,溢出它的父控件,进行绘制,不会报OverFlow的错误。
class OverflowBox extends SingleChildRenderObjectWidget {
  /// Creates a widget that lets its child overflow itself.
  const OverflowBox({
    Key key,
    this.alignment = Alignment.center,
    this.minWidth,
    this.maxWidth,
    this.minHeight,
    this.maxHeight,
    Widget child,
  }) : super(key: key, child: child);

  /// 跟上面的解释是一样的
  final AlignmentGeometry alignment;

  /// 设置child的最小宽度
  /// 如果为空,则默认是使用来自OverflowBox的父节点的约束
  final double minWidth;

  /// 设置child的最大宽度
  /// 如果为空,则默认是使用来自OverflowBox的父节点的约束
  final double maxWidth;

  /// 设置child的最小高度
  final double minHeight;
  ///设置child的最大高度
  final double maxHeight;

  @override
  RenderConstrainedOverflowBox createRenderObject(BuildContext context) {
    return RenderConstrainedOverflowBox(
      alignment: alignment,
      minWidth: minWidth,
      maxWidth: maxWidth,
      minHeight: minHeight,
      maxHeight: maxHeight,
      textDirection: Directionality.of(context),
    );
  }

  @override
  void updateRenderObject(BuildContext context, RenderConstrainedOverflowBox renderObject) {
    renderObject
      ..alignment = alignment
      ..minWidth = minWidth
      ..maxWidth = maxWidth
      ..minHeight = minHeight
      ..maxHeight = maxHeight
      ..textDirection = Directionality.of(context);
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
    properties.add(DoubleProperty('minWidth', minWidth, defaultValue: null));
    properties.add(DoubleProperty('maxWidth', maxWidth, defaultValue: null));
    properties.add(DoubleProperty('minHeight', minHeight, defaultValue: null));
    properties.add(DoubleProperty('maxHeight', maxHeight, defaultValue: null));
  }
}

/// 原理跟上面的OverflowBox差不多
class SizedOverflowBox extends SingleChildRenderObjectWidget {
  /// Creates a widget of a given size that lets its child overflow.
  ///
  /// The [size] argument must not be null.
  const SizedOverflowBox({
    Key key,
    @required this.size,
    this.alignment = Alignment.center,
    Widget child,
  }) : assert(size != null),
       assert(alignment != null),
       super(key: key, child: child);


  final AlignmentGeometry alignment;

  /// The size this widget should attempt to be.
  final Size size;

  @override
  RenderSizedOverflowBox createRenderObject(BuildContext context) {
    return RenderSizedOverflowBox(
      alignment: alignment,
      requestedSize: size,
      textDirection: Directionality.of(context),
    );
  }

  @override
  void updateRenderObject(BuildContext context, RenderSizedOverflowBox renderObject) {
    renderObject
      ..alignment = alignment
      ..requestedSize = size
      ..textDirection = Directionality.of(context);
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
    properties.add(DiagnosticsProperty<Size>('size', size, defaultValue: null));
  }
}