flutter布局详解及代码示例(补充)

布局

基本布局

  • Container(基本布局):最常见widget
  • Padding(内边距布局):Container增加padding的布局
  • Center(居中布局):Container设置居中的布局
  • Align(对齐布局):Container设置自定义对齐方式的布局
  • FittedBox(自定义约束布局):可以控制子widget是否可以超出父widget约束的布局
  • AspectRatio(宽高比布局):基于宽高比约束child的布局
  • ConstrainedBox(边界约束布局):Container设置constraints的布局;基于最大最小宽高,约束child的布局
  • Baseline(基线布局):根据基线约束子widget位置的布局
  • FractionallySizedBox(自适应尺寸布局):根据现有空间,来调整child的尺寸,child就算设置了具体的尺寸,也不起作用。
  • IntrinsicHeight(不固定高度布局):根据子widget的高度调整其自身高度
  • IntrinsicWidth(不固定宽度布局):根据子widget的宽度调整其自身宽度
  • LimitedBox(限制子控件宽高的布局):一个当其自身不受约束时才限制其大小的布局
  • Offstage(显隐布局):控制某些组件是否展示,不可见但实际存在
  • OverflowBox(允许自己的子控件溢出自己父控件的布局):允许子控件溢出父控件。
  • SizedBox(尺寸布局):Container增加宽高的布局
  • SizedOverflowBox(允许超出的有宽高的布局):SizedBox与OverflowBox的结合体
  • Transform(矩阵变换布局):能平移旋转缩放等操作的布局
  • CustomSingleChildLayout(纯自定义布局):单个child所有都能自定义的布局

Container

参数解释

  • alignment child在容器内的对齐方式
  • padding child在容器内的内间距
  • margin 容器与父类的外边距
  • color 背景颜色
  • decoration 背景装饰,不能与color属性同时设置
  • foregroundDecoration 前景装饰,会遮住child,可设置颜色透明度使其可见
  • width 容器的宽度
  • height 容器的高度
  • constraints 容器宽高的最大最小限制
  • transform 在绘制容器之前应用的转换矩阵。

补充

  • Alignment有个子类FractionalOffset,可以基于坐标系偏移
  • BoxDecoration的值:
    • BoxDecoration:绘制一个不可变的矩形或圆形框
    • ShapeDecoration:绘制一个不可变的任意形状。
    • FlutterLogoDecoration:绘制一个不变的Flutter的logo和标签。(没用)

FittedBox

构造函数

复制代码
const FittedBox({
    super.key,
    this.fit = BoxFit.contain,  //默认包含
    this.alignment = Alignment.center,
    this.clipBehavior = Clip.none,  //默认超出不剪裁
    super.child,
});
  • fit: 该参数若是:
    • BoxFit.contain,则当子widget超出父widget尺寸限制时,为了确保包含,会对子widget进行缩放,尽可能包含。
    • BoxFit.none,则当子widget超出父widget尺寸限制时,子widget会无视父widget直接显示
  • clipBehavior:搭配BoxFit.none使用,因为只有子widget超出父widget尺寸约束了,才存在是否剪裁的可能。

常见实践

  • 不同设备屏幕宽度有很多,如果出现子widget太大,通过BoxFit.contain属性就能实现尽可能的对子widget缩放,而展示出完整的子widget。

补充

  • FittedBox不会在debug模式提示"尺寸溢出"错误。

AspectRatio

构造函数

复制代码
const AspectRatio({
    super.key,
    required this.aspectRatio,  // 宽:高,比如2.0/1.0,比如0.5,反正是个double
    super.child,
})
  • AspectRatio 宽高比是相对父容器的,根据宽高比,绘制对应的AspectRatio内部的child的尺寸。
  • 内部的child设置了宽高,可能不起效。

常见实践

  • 绘制一些16:9的图片或者视频view

ConstrainedBox

构造函数

复制代码
ConstrainedBox({
    super.key,
    required this.constraints,
    super.child,
})
/// constraints的构造函数
const BoxConstraints({
    this.minWidth = 0.0,
    this.maxWidth = double.infinity,
    this.minHeight = 0.0,
    this.maxHeight = double.infinity,
})
  • ConstrainedBox用于对齐子widget添加额外的约束。
  • 限制的是子widget的尺寸,且一旦冲突,以ConstrainedBox的限制为主。

代码

复制代码
ConstrainedBox(
  constraints: BoxConstraints(
    minWidth: double.infinity, //宽度尽量大
    minHeight: 50.0 //最小高度为50
  ),
  child: Container(
      height: 10.0, 
      child: xx 
  ),
)
  • 这段代码就体现了"限制的是子widget的尺寸,且一旦冲突,以ConstrainedBox的限制为主"。
  • 哪怕子widget的高度设置10.0,但是和最小高度为50冲突,于是使用ConstrainedBox的最小高度为50。

Baseline

构造函数

复制代码
const Baseline({
    super.key,
    required this.baseline,
    required this.baselineType,
    super.child,
})
/// TextBaseline枚举
enum TextBaseline {
  alphabetic,  //对齐字母字符字形底部
  ideographic,  //对齐表意字符的水平线
} 
  • 对齐字母字符字形底部,指的是如jpg这些字母,基线都在靠上一点,而"对齐表意字符的水平线",则当汉字和jpg这些字母在一起时,底部都是对齐的。
  • baseline 可以为负值,会把基线下调

常见实践

  • 基本都是中英文混用时,可能需要基线对齐

FractionallySizedBox

构造函数

复制代码
const FractionallySizedBox({
    super.key,
    this.alignment = Alignment.center,
    this.widthFactor,  // 宽因子,父widget宽度*这个指=child的宽度
    this.heightFactor,  // 高因子,同理
    super.child,
})
  • 若不设置宽高因子,则对应方向默认填满父widget

示例

复制代码
Container(
    color: Colors.blue,
    height: 150.0,
    width: 150.0,
    child: new FractionallySizedBox(
        alignment: Alignment.center,
        widthFactor: 1.5,
        heightFactor: 0.5,
        child: xxWidget,
    ),
)
  • xxWidget的宽度为1501.5,高度为1500.5

IntrinsicHeight

示例

复制代码
IntrinsicHeight(
    child: Row(
        children: [
            Container(
                width: 50,
                color: Colors.red,
            ),
            Container(
                width: 28,
                height: 50,
                color: Colors.red,
            ),
        ],
    ),
),

IntrinsicWidth

构造函数

复制代码
IntrinsicWidth({ super.key, this.stepWidth, this.stepHeight, super.child })
  • 相比IntrinsicHeight多了两个参数:
    • stepWidth:宽度步长
      • 当步长小于子widget宽度时,子widget宽度*步长/10=自身宽度
      • 当步长大于子widget宽度时,自身宽度=步长
    • stepHeight:高度步长(同理)

代码

  • 只展示用步长的,不用步长或者步长=1,和IntrinsicHeight用法一致

    IntrinsicWidth(
    stepWidth: 100,
    child: Container(
    color: Colors.blue,
    child: Center(
    child: Container(
    color: Colors.red,
    width: 50,
    height: 50,
    ),
    ),
    ),
    ),

这段代码IntrinsicWidth的宽度就是100

LimitedBox

构造函数

复制代码
const LimitedBox({
    super.key,
    this.maxWidth = double.infinity,
    this.maxHeight = double.infinity,
    super.child,
})

代码

  • LimitedBox只有在自身不受约束时才能限制

  • 将child限制在指定的最大宽高中,这样即使child自身没有约束大小,也会有具有外部约束,依然控制其大小

    Container(
    color: Colors.blue,
    child: UnconstrainedBox(
    child: LimitedBox(
    maxWidth: 100,
    maxHeight: 100,
    child: Text(
    '啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊'),
    ),
    ),
    ),

用UnconstrainedBox就是为了让LimitedBox没有外部限制

Offstage

构造函数

复制代码
Offstage({ super.key, this.offstage = true, super.child })
  • 如果offstage=true,那么Offstage的子child就会处于隐藏状态。这时候子child不会占用任何空间,但实际存在

OverflowBox

构造函数

复制代码
const OverflowBox({
    super.key,
    this.alignment = Alignment.center,
    this.minWidth,
    this.maxWidth,
    this.minHeight,
    this.maxHeight,
    super.child,
});
  • minWidth:子控件宽度小于这个值,按这个值显示
  • maxWidth:子控件宽度大于这个值,按这个值显示
  • minHeight:子控件高度小于这个值,按这个值显示
  • maxHeight:子控件高度大于这个值,按这个值显示
  • OverflowBox自己是没有宽高的,他的属性都是对子控件的约束
  • OverflowBox的尺寸是由他的父控件约束来决定的
  • 利用这个能实现超出布局的展示

代码

复制代码
@override
Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
        title: Text('OverflowBox'),
        ),
        body: Container(
        color: Colors.blue,
        height: 100,
        child: OverflowBox(
            alignment: Alignment.topCenter,
            minWidth: 20,
            maxWidth: 100,
            maxHeight: 200,
            minHeight: 20,
            child: Container(
            width: 50,
            height: 250,
            color: Colors.red,
            ),
        ),
        ),
    );
}
  • 如上代码,就会让OverflowBox包裹的Container,超过OverflowBox的父布局Container约束,形成类似Stack的效果。

SizedBox

代码

  • 本身是Container增加宽高的布局

  • 比较多的用于当间隔布局,如下:

    Row(
    children:[
    Container(),
    SizedBox(width:100),
    Container(),
    ]
    )

这样两个Container之间就有100间距了

SizedOverflowBox

  • SizedBox与OverflowBox的结合体
  • SizedBox的特性部分:
    • 通过将自身的固定尺寸,传递给child,来达到控制child尺寸
  • OverflowBox的特性部分:
    • 通过设置超出的属性,来决定如何渲染超出的child

构造函数

复制代码
const SizedOverflowBox({
    super.key,
    required this.size,
    this.alignment = Alignment.center,
    super.child,
})

代码示例

复制代码
SizedOverflowBox(
    size: Size(100.0, 200.0),
    child: Container(
        color: Colors.red,
        width: 200.0,
        height: 100.0,
    ),
)

Transform

构造函数

复制代码
const Transform({
    super.key,
    required this.transform,
    this.origin,
    this.alignment,
    this.transformHitTests = true,
    this.filterQuality,
    super.child,
});

旋转child

  • pi/4就是 180 / 4 = 45 度

    Transform.rotate(
    angle: pi/4,
    child: const Icon(
    "图片地址",
    size: 200,
    color: Colors.blue,
    ),
    );

缩放child

  • scale: 0.5就是缩放50%

    Transform.scale(
    scale: 0.5,
    child: const Icon(
    "图片地址",
    size: 200,
    color: Colors.blue,
    ),
    );

平移child

  • offset:const Offset(50.0, 0), 就是向右平移50

    Transform.translate(
    offset:const Offset(50.0, 0),
    child: const Icon(
    "图片地址",
    size: 200,
    color: Colors.blue,
    ),
    );

CustomSingleChildLayout

  • 灵活实现自己想要的布局以及约束情况
  • 确定 child 的 constrains => 实现getConstraintsForChild
  • 确定 自己的 大小 => 实现getSize
  • 摆放 child => 实现getPositionForChild

构造函数

复制代码
const CustomSingleChildLayout({
    super.key,
    required this.delegate,
    super.child,
})
  • delegate需要继承SingleChildLayoutDelegate

    /// SingleChildLayoutDelegate的定义
    abstract class SingleChildLayoutDelegate {

    复制代码
    const SingleChildLayoutDelegate({ Listenable? relayout }) : _relayout = relayout;
    
    final Listenable? _relayout;

    ///获取父容器约束条件,来确定layout的大小
    Size getSize(BoxConstraints constraints) => constraints.biggest;

    ///确定child的约束
    BoxConstraints getConstraintsForChild(BoxConstraints constraints) => constraints;

    ///确定child的位置,返回一个相对于parent的偏移值;size是layout的大小,由getSize确定;childSize由getConstraintsForChild得出的Constraints对child进行约束,得到child自身的size
    Offset getPositionForChild(Size size, Size childSize) => Offset.zero;

    ///是否需要relayout
    bool shouldRelayout(covariant SingleChildLayoutDelegate oldDelegate);
    }

delegate代码

复制代码
class _MyDelegate extends SingleChildLayoutDelegate {
  @override
  Size getSize(BoxConstraints constraints) {
    //用父容器约束条件
    return super.getSize(constraints);
  }

  @override
  bool shouldRelayout(covariant SingleChildLayoutDelegate oldDelegate) {
    //不需要relayout
    return false;
  }

  @override
  BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
    //child的大小
    var childWidth = min(constraints.maxWidth, constraints.maxHeight);
    var childBoxConstraints = BoxConstraints.tight(
      Size(childWidth / 2, childWidth / 2),
    );
    return childBoxConstraints;
  }

  @override
  Offset getPositionForChild(Size size, Size childSize) {
    // 确定child的位置,返回一个相对于parent的偏移值
    // size由getSize确定
    // childSize由getConstraintsForChild得出的Constraints对child进行约束,得到child自身的size
    var dx = (size.width - childSize.width) / 2;
    var dy = (size.height - childSize.height) / 2;
    return Offset(dx, dy);
  }
}
相关推荐
火柴就是我20 小时前
flutter 之真手势冲突处理
android·flutter
Speed12321 小时前
`mockito` 的核心“打桩”规则
flutter·dart
法的空间21 小时前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
恋猫de小郭21 小时前
Android 将强制应用使用主题图标,你怎么看?
android·前端·flutter
玲珑Felone1 天前
从flutter源码看其渲染机制
android·flutter
ALLIN2 天前
Flutter 三种方式实现页面切换后保持原页面状态
flutter
Dabei2 天前
Flutter 国际化
flutter
Dabei2 天前
Flutter MQTT 通信文档
flutter
Dabei2 天前
Flutter 中实现 TCP 通信
flutter
孤鸿玉2 天前
ios flutter_echarts 不在当前屏幕 白屏修复
flutter