flutter:组件的约束问题

Flutter 布局三步走

Flutter 的布局核心规则是:

  1. 父给子约束(constraints: min/max 宽高)
  2. 子在约束内确定自己的大小(大小可自由 or 被强制)
  3. 父再根据子大小决定自己的大小和位置

约束的类型

紧约束 (tight constraints):宽或高的 min = max,子必须是这个大小。
👉 子没自由。

松散约束 (loose constraints):只有 max,min=0,子可以小于 max。
👉 子有自由。

常见组件的约束传递

父组件 给子约束的类型 子能否决定大小? 说明
Container(width: 140, height: 42) 紧约束:w=140, h=42 ❌ 不能 子被强制 (140,42),即使子写了 maximumSize 也无效
SizedBox(width: 100, height: 50) 紧约束:w=100, h=50 ❌ 不能 子必须是 (100,50)
Expanded / Flexible(在 Row/Column 里) 紧约束(填满主轴空间) ❌ 完全受控 子 被拉伸到父要求的大小
Center 松散约束:最大等于父范围 ✅ 可以 子决定大小,例如 Button 遵循 maximumSize
Align 松散约束 ✅ 可以 类似 Center,但带对齐
Row / Column(mainAxis) 紧约束(如果用 Expanded/Flexible) ❌ 不能 子被拉伸填满剩余空间
Row / Column(无 Expanded/Flexible) 松散约束 ✅ 可以 子自由决定大小,Row/Column 再排布
UnconstrainedBox 不传递约束(但还受祖先约束) ✅ 可以 子完全根据自身大小渲染,可能溢出父
ConstrainedBox 传递 min/max 范围 ✅ 部分 子大小必须落在范围内

父组件没有设置大小时的情况

父组件类型 没有写宽高时的行为 给子约束
Container() 如果没写 width/height,它会根据 子组件大小 来决定自己的大小(受祖先约束限制)。 把祖先的约束原封不动传递给子(通常是松约束)。
Center() 自己会尽量占满父(受祖先约束),但给子传 松约束(最大值等于父)。 松约束
Align() 类似 Center,自己先占满父,但给子松约束。 松约束
Row/Column 沿着主轴给子松约束(除非用 Expanded/Flexible),交叉轴是如果Row/Column有其父类的约束,那么它的约束就是紧约束,如果没有父类的约束,那么它会根据 子组件大小 来决定自己的大小 松 or 紧混合
Expanded 它会强制子在主轴方向填满(紧约束)。 紧约束
ListView/SingleChildScrollView 在滚动方向上给子松约束(允许无限大),另一方向是紧约束。 松+紧

父组件 没写宽高 ≠ 一定是松约束。

要看父组件是什么:

  • Center、Align、Padding:一般给子松约束
  • Row/Column:部分方向紧,部分方向松
  • Expanded/Flexible:紧约束
  • Container:看子来决定大小,但受祖先约束

父 vs 子 谁说了算?

父决定子(tight constraints)

  • Container/SizedBox 直接写死宽高
  • Expanded/Flexible 在 Row/Column 中
  • 父 min=max 的情况

Container+Contaier

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

在这里案例中,尽管子组件Container设定了自己的长宽都为50,但是由于父组件是紧约束,子组件还是遵循了父组件的约束,大小变为width=100height=100

Container+ElevatedButton

Container(
  height: 42,
  width: 140,
  child: ElevatedButton(
    onPressed: () {},
    style: ButtonStyle(
      // width<=80 height<=40
      maximumSize: WidgetStateProperty.all(Size(80, 40)),
      backgroundColor: WidgetStateProperty.all(Colors.orange),
    ),
    child: const Text(
      "Get Started",
      style: TextStyle(
        fontSize: 16,
        fontWeight: FontWeight.w300,
        color: Colors.white,
      ),
    ),
  ),
),

在上面这个案例中,经过我设置了ElevatedButton的width<=80 height<=40,但是由于受到父组件严格约束的影响,ElevatedButton的大小还是变成了width=140 height=42

子决定父(loose constraints)

  • Center / Align / Padding 等传递松散约束

  • IntrinsicWidth / IntrinsicHeight 测量子后再决定父

  • 父只提供上限,子返回自己需要的大小

Scaffold+Column+Container

我希望实现的效果:

我实际实现的效果:

为什么会这样?我我分析一下我的代码:

class _SplashPageState extends State<SplashPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        decoration: const BoxDecoration(color: AppColors.backgroundSplash),// 没有效果
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center, // 没有效果
          children: [
            Container(
              alignment: Alignment.center,
              width: 120,
              height: 120,
              color: Colors.white,
            ),
          ],
        ),
      ),
    );
  }
}

为什么背景色会实现:

  • BoxDecoration(color: AppColors.backgroundSplash):我的背景色设置了蓝色。这个蓝色不是整个页面Scaffold的蓝色,而是Container的蓝色。

  • Container由于自己没有设置大小,那么它的大小完全由子组件Column决定。其中Columnw=120,所以Containerw=120。因此背景色也只是120的宽度,而不是覆盖整个页面。

为什么交叉轴没有居中?

  • 因为子组件Container的大小等于Column的大小。所以Columnwidth=120
  • 所以,实际上在交叉轴上,子组件Container是居中的,只是Column宽度等于Container的宽度,所以看不出来。

正确的做法:

class _SplashPageState extends State<SplashPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: AppColors.backgroundSplash,
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(width: 120, height: 120, color: Colors.white),
            Text("Online Market"),
            Text("10"),
          ],
        ),
      ),
    );
  }
}

实际上,我们发现Column的width并没有变。但是Center是父组件的大小:

  • Center()在没有写大小的时候,自己会尽量占满父(受祖先约束),但给子传 松约束(最大值等于父)。

×

喜欢就点赞,疼爱就打赏