Flutter(十一)Padding Column Row Flex Expand Flexible

0 阅读12分钟

Padding

  • 当 child 为空时Padding 会直接生成一个宽为 left + right,高为 top + bottom 的空白区域。
  • 当 child 不为空时Padding 会将父组件传递下来的布局约束传递给 child,但会根据设置的 padding 属性缩小 child 的可用布局尺寸。随后,Padding 会根据子组件的实际尺寸加上 padding 值,来调整自身的最终尺寸。
import 'package:flutter/material.dart';

void main(List<String> args) {
  runApp(const MaterialApp(home: HomePage()));
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Align')),
      body: Container(
        color: Colors.blue,
        child: Padding(padding: EdgeInsetsGeometry.all(10), child: Container(
          color: Colors.cyan,
          child: Text("data"),
        )),
      ),
    );
  }
}

Padding进阶

import 'package:flutter/material.dart';

void main(List<String> args) {
  runApp(const MaterialApp(home: HomePage()));
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Padding 全方位知识点')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: const [
          _Section1_WhatIsPadding(),
          _Section2_EdgeInsets(),
          _Section3_DirectionalEdgeInsets(),
          _Section4_MarginVsPadding(),
          _Section5_ConstraintsFlow(),
          _Section6_PaddingVsContainer(),
          _Section7_Symmetric(),
          _Section8_Only(),
          _Section9_All(),
          _Section10_Summary(),
        ],
      ),
    );
  }
}

// ────────────────────────────────────────────────────────────────
// ① 知识点 1:Padding 的本质 —— 在 child 周围增加空白
// ────────────────────────────────────────────────────────────────
class _Section1_WhatIsPadding extends StatelessWidget {
  const _Section1_WhatIsPadding();

  @override
  Widget build(BuildContext context) {
    return _Card(
      title: '① Padding 的本质',
      subtitle: '在 child 四周插入空白区域',
      children: [
        const Text(
          '源码定义:\n'
          'class Padding extends SingleChildRenderObjectWidget {\n'
          '  final EdgeInsetsGeometry padding;\n'
          '}\n\n'
          'RenderPadding 的布局逻辑:\n'
          '1. 先把 padding.deflateConstraints(constraints) 传给 child\n'
          '   (即约束的 min/max 四边各减去 padding 值)\n'
          '2. child 布局完成后拿到 child.size\n'
          '3. Padding 自己的 size = child.size + padding 四边之和\n'
          '4. child 偏移到 padding 内部位置',
          style: TextStyle(fontSize: 11, fontFamily: 'monospace'),
        ),
        const SizedBox(height: 8),
        Row(
          children: [
            Expanded(
              child: Container(
                color: Colors.red.shade100,
                height: 100,
                child: const Text('父容器',
                    style: TextStyle(fontSize: 10, color: Colors.red)),
              ),
            ),
            const SizedBox(width: 8),
            Expanded(
              child: Container(
                color: Colors.red.shade100,
                height: 100,
                child: Padding(
                  padding: const EdgeInsets.all(12),
                  child: Container(
                    color: Colors.blue.shade200,
                    child: const Text('Padding 12\n四周留白',
                        style: TextStyle(fontSize: 10)),
                  ),
                ),
              ),
            ),
          ],
        ),
        const SizedBox(height: 4),
        const Text('↑ 右:蓝色 = child,四周灰色 = padding 空白',
            style: TextStyle(fontSize: 10, color: Colors.grey)),
      ],
    );
  }
}

// ────────────────────────────────────────────────────────────────
// ② 知识点 2:EdgeInsets —— Padding 的四种构造方式
// ────────────────────────────────────────────────────────────────
class _Section2_EdgeInsets extends StatelessWidget {
  const _Section2_EdgeInsets();

  @override
  Widget build(BuildContext context) {
    return _Card(
      title: '② EdgeInsets 的四种构造',
      subtitle: 'EdgeInsets 是 EdgeInsetsGeometry 的最常用实现',
      children: [
        const Text('EdgeInsets 类的四种静态方法:',
            style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold)),
        const SizedBox(height: 6),
        _EdgeInsetsRow(label: 'all(8)', code: 'EdgeInsets.all(8)',
            top: 8, right: 8, bottom: 8, left: 8),
        _EdgeInsetsRow(label: 'symmetric(h:10, v:6)',
            code: 'EdgeInsets.symmetric(horizontal: 10, vertical: 6)',
            top: 6, right: 10, bottom: 6, left: 10),
        _EdgeInsetsRow(label: 'only(left:4)',
            code: 'EdgeInsets.only(left: 4)',
            top: 0, right: 0, bottom: 0, left: 4),
        _EdgeInsetsRow(label: 'fromLTRB(1,2,3,4)',
            code: 'EdgeInsets.fromLTRB(1, 2, 3, 4)',
            top: 2, right: 3, bottom: 4, left: 1),
      ],
    );
  }
}

class _EdgeInsetsRow extends StatelessWidget {
  final String label;
  final String code;
  final double top;
  final double right;
  final double bottom;
  final double left;
  const _EdgeInsetsRow({
    required this.label,
    required this.code,
    required this.top,
    required this.right,
    required this.bottom,
    required this.left,
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 6),
      child: Row(
        children: [
          Container(
            width: 70,
            height: 50,
            color: Colors.grey.shade200,
            child: Padding(
              padding: EdgeInsets.only(
                  top: top, right: right, bottom: bottom, left: left),
              child: Container(color: Colors.blue, width: double.infinity,
                  height: double.infinity),
            ),
          ),
          const SizedBox(width: 10),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(label,
                    style: const TextStyle(
                        fontSize: 11, fontWeight: FontWeight.bold)),
                Text(code,
                    style: const TextStyle(
                        fontSize: 10, fontFamily: 'monospace',
                        color: Colors.grey)),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

// ────────────────────────────────────────────────────────────────
// ③ 知识点 3:DirectionalEdgeInsets —— 支持 RTL 的边距
// ────────────────────────────────────────────────────────────────
class _Section3_DirectionalEdgeInsets extends StatelessWidget {
  const _Section3_DirectionalEdgeInsets();

  @override
  Widget build(BuildContext context) {
    return _Card(
      title: '③ EdgeInsetsDirectional',
      subtitle: '适配 RTL(阿拉伯语等从右到左语言)的边距',
      children: [
        const Text('普通 EdgeInsets 用 left/right(固定物理方向)\n'
            'EdgeInsetsDirectional 用 start/end(逻辑方向):',
            style: TextStyle(fontSize: 12)),
        const SizedBox(height: 6),
        Row(
          children: [
            Expanded(
              child: Column(
                children: [
                  Container(
                    height: 70,
                    color: Colors.grey.shade200,
                    child: Padding(
                      padding: const EdgeInsetsDirectional.only(start: 20),
                      child: Container(color: Colors.orange,
                          child: const Text('start:20\nLTR→左20\nRTL→右20',
                              style: TextStyle(fontSize: 10))),
                    ),
                  ),
                  const SizedBox(height: 4),
                  const Text('EdgeInsetsDirectional.only(start: 20)',
                      style: TextStyle(fontSize: 10, fontFamily: 'monospace')),
                ],
              ),
            ),
            const SizedBox(width: 8),
            Expanded(
              child: Column(
                children: [
                  Container(
                    height: 70,
                    color: Colors.grey.shade200,
                    child: Padding(
                      padding: const EdgeInsets.only(left: 20),
                      child: Container(color: Colors.orange.shade700,
                          child: const Text('left:20\n永远在左边',
                              style: TextStyle(fontSize: 10,
                                  color: Colors.white))),
                    ),
                  ),
                  const SizedBox(height: 4),
                  const Text('EdgeInsets.only(left: 20)',
                      style: TextStyle(fontSize: 10, fontFamily: 'monospace')),
                ],
              ),
            ),
          ],
        ),
        const SizedBox(height: 8),
        const Text('核心区别:start/end 会根据 Directionality 自动翻转,\n'
            'left/right 永远固定。做国际化适配时优先用 EdgeInsetsDirectional。',
            style: TextStyle(fontSize: 11)),
      ],
    );
  }
}

// ────────────────────────────────────────────────────────────────
// ④ 知识点 4:margin vs padding —— 内外之分
// ────────────────────────────────────────────────────────────────
class _Section4_MarginVsPadding extends StatelessWidget {
  const _Section4_MarginVsPadding();

  @override
  Widget build(BuildContext context) {
    return _Card(
      title: '④ margin vs padding',
      subtitle: 'margin 在外、padding 在内,视觉对比一目了然',
      children: [
        const Text('margin = Container 外面的空白(兄弟组件之间)\n'
            'padding = Container 里面的空白(内容与边框之间)',
            style: TextStyle(fontSize: 12)),
        const SizedBox(height: 6),
        Container(
          color: Colors.green.shade100,
          padding: const EdgeInsets.all(8),
          child: Row(
            children: [
              // 只有 padding
              Expanded(
                child: Container(
                  margin: const EdgeInsets.only(left: 10), // ← margin
                  color: Colors.blue.shade100,
                  padding: const EdgeInsets.all(10),       // ← padding
                  child: Container(
                    color: Colors.blue.shade800,
                    height: 40,
                    child: const Text('padding',
                        style: TextStyle(color: Colors.white, fontSize: 11)),
                  ),
                ),
              ),
              const SizedBox(width: 8),
              // 只有 margin
              Container(
                margin: const EdgeInsets.only(right: 10),
                color: Colors.orange.shade100,
                child: Container(
                  color: Colors.orange.shade700,
                  height: 60,
                  width: 60,
                  child: const Text('margin',
                      style: TextStyle(color: Colors.white, fontSize: 11)),
                ),
              ),
            ],
          ),
        ),
        const SizedBox(height: 6),
        const Text('绿色=外层容器\n'
            '蓝色:10 margin + 10 padding → 内容周围既有外边距又有内边距\n'
            '橙色:只有 margin → 容器外有空白,容器内没有',
            style: TextStyle(fontSize: 10)),
      ],
    );
  }
}

// ────────────────────────────────────────────────────────────────
// ⑤ 知识点 5:对约束和尺寸的影响
// ────────────────────────────────────────────────────────────────
class _Section5_ConstraintsFlow extends StatelessWidget {
  const _Section5_ConstraintsFlow();

  @override
  Widget build(BuildContext context) {
    return _Card(
      title: '⑤ 约束传递原理',
      subtitle: 'Padding 如何"缩小"约束,又"放大"自己的尺寸',
      children: [
        const Text(
          '源码逻辑(RenderPadding):\n\n'
          '1. childConstraints = constraints.deflate(padding)\n'
          '   父约束 maxWidth=300, padding=20\n'
          '   → child 收到 maxWidth=300-20*2=260\n\n'
          '2. child.layout(childConstraints)\n'
          '   child 决定自己的 size,比如 200×100\n\n'
          '3. Padding.size = child.size + padding\n'
          '   = 200+40 × 100+40 = 240×140\n\n'
          '┌──── Padding(40) ────────────┐\n'
          '│  ┌───── padding ───────┐    │\n'
          '│  │  ┌────────────────┐ │    │\n'
          '│  │  │     child      │ │    │\n'
          '│  │  │     200×100    │ │    │\n'
          '│  │  └────────────────┘ │    │\n'
          '│  └────────────────────┘    │\n'
          '└─────────────────────────────┘',
          style: TextStyle(fontSize: 10, fontFamily: 'monospace'),
        ),
        const SizedBox(height: 8),
        Row(
          children: [
            Expanded(
              child: Container(
                height: 70,
                color: Colors.grey.shade300,
                alignment: Alignment.center,
                child: Container(
                  color: Colors.blue,
                  width: 150,
                  height: 30,
                ),
              ),
            ),
            const SizedBox(width: 8),
            Expanded(
              child: Container(
                height: 70,
                color: Colors.grey.shade300,
                alignment: Alignment.center,
                child: Padding(
                  padding: const EdgeInsets.all(15),
                  child: Container(
                    color: Colors.blue,
                    width: 150,
                    height: 30,
                  ),
                ),
              ),
            ),
          ],
        ),
        const SizedBox(height: 4),
        const Text('右:Padding 包裹后整体宽高多了 30(15×2)',
            style: TextStyle(fontSize: 10, color: Colors.grey)),
      ],
    );
  }
}

// ────────────────────────────────────────────────────────────────
// ⑥ 知识点 6:Padding vs Container(padding:)
// ────────────────────────────────────────────────────────────────
class _Section6_PaddingVsContainer extends StatelessWidget {
  const _Section6_PaddingVsContainer();

  @override
  Widget build(BuildContext context) {
    return _Card(
      title: '⑥ Padding vs Container(padding:)',
      subtitle: 'Container 内部就是 Padding',
      children: [
        const Text('RenderObject 树等价:',
            style: TextStyle(fontSize: 12)),
        const SizedBox(height: 4),
        Container(
          padding: const EdgeInsets.all(8),
          color: Colors.grey.shade100,
          child: const Text(
            'Padding(child)         → RenderPadding → child\n'
            'Container(padding:)    → RenderContainer → RenderPadding → child\n'
            '                                   ↑ 多了一层但同类型',
            style: TextStyle(fontSize: 10, fontFamily: 'monospace'),
          ),
        ),
        const SizedBox(height: 8),
        const Text('选择建议:',
            style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold)),
        const SizedBox(height: 4),
        Row(
          children: [
            Expanded(
              child: Container(
                padding: const EdgeInsets.all(6),
                margin: const EdgeInsets.only(right: 4),
                decoration: BoxDecoration(
                    color: Colors.blue.shade50,
                    borderRadius: BorderRadius.circular(4)),
                child: const Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('只用 padding → Padding',
                        style: TextStyle(
                            fontSize: 10, fontWeight: FontWeight.bold)),
                    SizedBox(height: 2),
                    Text('更轻量,语义清晰',
                        style: TextStyle(fontSize: 9)),
                  ],
                ),
              ),
            ),
            Expanded(
              child: Container(
                padding: const EdgeInsets.all(6),
                decoration: BoxDecoration(
                    color: Colors.orange.shade50,
                    borderRadius: BorderRadius.circular(4)),
                child: const Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('padding+背景 → Container',
                        style: TextStyle(
                            fontSize: 10, fontWeight: FontWeight.bold)),
                    SizedBox(height: 2),
                    Text('同时设 decoration',
                        style: TextStyle(fontSize: 9)),
                  ],
                ),
              ),
            ),
          ],
        ),
      ],
    );
  }
}

// ────────────────────────────────────────────────────────────────
// ⑦ 知识点 7:EdgeInsets.symmetric
// ────────────────────────────────────────────────────────────────
class _Section7_Symmetric extends StatelessWidget {
  const _Section7_Symmetric();

  @override
  Widget build(BuildContext context) {
    return _Card(
      title: '⑦ EdgeInsets.symmetric',
      subtitle: '水平和垂直分别设值,最常用的写法之一',
      children: [
        const Text('EdgeInsets.symmetric(horizontal: 16, vertical: 8)\n'
            '等价于 EdgeInsets.only(left:16, right:16, top:8, bottom:8)',
            style: TextStyle(fontSize: 12)),
        const SizedBox(height: 8),
        Row(
          children: [
            _SymDemo(label: 'horizontal:16\nvertical:8',
                h: 16, v: 8),
            const SizedBox(width: 6),
            _SymDemo(label: 'horizontal:24\nvertical:0',
                h: 24, v: 0),
            const SizedBox(width: 6),
            _SymDemo(label: 'horizontal:0\nvertical:12',
                h: 0, v: 12),
          ],
        ),
        const SizedBox(height: 8),
        const Text('典型场景:ListView 的 padding 通常用 symmetric',
            style: TextStyle(fontSize: 11)),
      ],
    );
  }
}

class _SymDemo extends StatelessWidget {
  final String label;
  final double h;
  final double v;
  const _SymDemo({required this.label, required this.h, required this.v});

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Column(
        children: [
          Container(
            height: 100,
            color: Colors.grey.shade200,
            child: Padding(
              padding: EdgeInsets.symmetric(horizontal: h, vertical: v),
              child: Container(color: Colors.cyan,
                  child: const Text('内容',
                      style: TextStyle(color: Colors.white, fontSize: 11))),
            ),
          ),
          const SizedBox(height: 4),
          Text(label,
              textAlign: TextAlign.center,
              style: const TextStyle(fontSize: 9)),
        ],
      ),
    );
  }
}

// ────────────────────────────────────────────────────────────────
// ⑧ 知识点 8:EdgeInsets.only —— 只设某一边
// ────────────────────────────────────────────────────────────────
class _Section8_Only extends StatelessWidget {
  const _Section8_Only();

  @override
  Widget build(BuildContext context) {
    return _Card(
      title: '⑧ EdgeInsets.only',
      subtitle: '只设某一边(其他三边默认为 0)',
      children: [
        const Text('EdgeInsets.only(left/right/top/bottom: value)',
            style: TextStyle(fontSize: 12)),
        const SizedBox(height: 8),
        Container(
          height: 120,
          color: Colors.grey.shade200,
          padding: const EdgeInsets.all(4),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              // 只有 top
              Padding(
                padding: const EdgeInsets.only(top: 15),
                child: Container(
                  width: 80, height: 25, color: Colors.blue,
                  child: const Text('only(top:15)',
                      style: TextStyle(fontSize: 9, color: Colors.white)),
                ),
              ),
              const SizedBox(height: 3),
              // 只有 left
              Padding(
                padding: const EdgeInsets.only(left: 25),
                child: Container(
                  width: 80, height: 25, color: Colors.orange,
                  child: const Text('only(left:25)',
                      style: TextStyle(fontSize: 9, color: Colors.white)),
                ),
              ),
              const SizedBox(height: 3),
              // 组合
              Padding(
                padding: const EdgeInsets.only(right: 30, bottom: 10),
                child: Container(
                  width: 80, height: 25, color: Colors.green,
                  child: const Text('only(r:30, b:10)',
                      style: TextStyle(fontSize: 9, color: Colors.white)),
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }
}

// ────────────────────────────────────────────────────────────────
// ⑨ 知识点 9:EdgeInsets.all —— 四边统一
// ────────────────────────────────────────────────────────────────
class _Section9_All extends StatelessWidget {
  const _Section9_All();

  @override
  Widget build(BuildContext context) {
    return _Card(
      title: '⑨ EdgeInsets.all',
      subtitle: '四边统一值的简写',
      children: [
        const Text('EdgeInsets.all(16)\n'
            '等价于 EdgeInsets.symmetric(horizontal: 16, vertical: 16)',
            style: TextStyle(fontSize: 12)),
        const SizedBox(height: 8),
        Row(
          children: [
            _AllDemo(label: 'all(4)', v: 4),
            const SizedBox(width: 6),
            _AllDemo(label: 'all(12)', v: 12),
            const SizedBox(width: 6),
            _AllDemo(label: 'all(24)', v: 24),
          ],
        ),
      ],
    );
  }
}

class _AllDemo extends StatelessWidget {
  final String label;
  final double v;
  const _AllDemo({required this.label, required this.v});

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Column(
        children: [
          Container(
            height: 100,
            color: Colors.grey.shade200,
            child: Padding(
              padding: EdgeInsets.all(v),
              child: Container(color: Colors.deepPurple,
                  child: const Text('内容',
                      style: TextStyle(color: Colors.white, fontSize: 11))),
            ),
          ),
          const SizedBox(height: 4),
          Text(label, style: const TextStyle(fontSize: 10)),
        ],
      ),
    );
  }
}

// ────────────────────────────────────────────────────────────────
// ⑩ 知识点 10:完整总结
// ────────────────────────────────────────────────────────────────
class _Section10_Summary extends StatelessWidget {
  const _Section10_Summary();

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: Colors.amber.shade100,
        borderRadius: BorderRadius.circular(8),
      ),
      child: const Text(
        '📌 Padding 完整总结\n\n'
        '1. 本质:在 child 四周增加空白区域\n'
        '2. 布局逻辑:\n'
        '   - 向下传递:constraints.deflate(padding)\n'
        '   - 向上传递:size = child.size + padding\n'
        '3. EdgeInsets 四种构造:\n'
        '   - all(v)        四边统一\n'
        '   - symmetric(h,v) 水平垂直分别\n'
        '   - only(l/r/t/b)  指定某边\n'
        '   - fromLTRB(...)  全手动\n'
        '4. EdgeInsetsDirectional:start/end 支持 RTL\n'
        '5. margin vs padding:\n'
        '   - margin 在装饰外层(Container 外边)\n'
        '   - padding 在装饰内层(内容与边框之间)\n'
        '6. Container(padding:) 内部就是 Padding',
        style: TextStyle(fontSize: 12),
      ),
    );
  }
}

// ────────────────────────────────────────────────────────────────
// 通用卡片组件
// ────────────────────────────────────────────────────────────────
class _Card extends StatelessWidget {
  final String title;
  final String subtitle;
  final List<Widget> children;
  const _Card({required this.title, this.subtitle = '', required this.children});

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.only(bottom: 16),
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(10),
        border: Border.all(color: Colors.grey.shade300),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withValues(alpha: 0.05),
            blurRadius: 6,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(title,
              style: const TextStyle(
                  fontSize: 14, fontWeight: FontWeight.bold)),
          if (subtitle.isNotEmpty) ...[
            const SizedBox(height: 2),
            Text(subtitle,
                style: TextStyle(
                    fontSize: 11,
                    color: Colors.grey.shade600,
                    fontStyle: FontStyle.italic)),
          ],
          const SizedBox(height: 10),
          ...children,
        ],
      ),
    );
  }
}

Column

mainAxisAlignment 主轴对齐方式

  1. MainAxisAlignment.start(默认值)
    将子组件线性排列,整体靠顶部对齐。
  2. MainAxisAlignment.end
    将子组件线性排列,整体靠底部对齐。
  3. MainAxisAlignment.center
    将子组件线性排列,整体在垂直方向上居中。
  4. MainAxisAlignment.spaceBetween
    将主轴方向上的空白区域均分,使得子组件之间的间距相等。第一个和最后一个子组件分别紧贴顶部和底部,首尾与边缘之间没有间隙。
  5. MainAxisAlignment.spaceAround
    将主轴方向上的空白区域均分,使得子组件之间的间距相等。但首尾子组件与顶部/底部边缘的间距,是子组件之间间距的一半。
  6. MainAxisAlignment.spaceEvenly
    将主轴方向上的空白区域均分,使得子组件之间,以及首尾子组件与顶部/底部边缘的间距都完全相等。
import 'package:flutter/material.dart';

void main(List<String> args) {
  runApp(const MaterialApp(home: HomePage1()));
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Column')),
      body: Container(
        color: Colors.blue,
        child: Column(
          children: [
            Text("1"),
            Text("2"),
            Text("3"),
          ],
        ))
      );

  }
}

class HomePage1 extends StatelessWidget {
  const HomePage1({super.key});

  @override
  Widget build(BuildContext context) {
    Widget buildExample(String title, MainAxisAlignment alignment) {
      return Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14)),
          const SizedBox(height: 4),
          Container(
            height: 150, // 给一个固定高度,让对齐效果显现出来
            width: double.infinity,
            color: Colors.grey[200], // 灰色背景框
            child: Column(
              mainAxisAlignment: alignment,
              children: [
                Container(width: 50, height: 30, color: Colors.blue),
                Container(width: 50, height: 30, color: Colors.red),
                Container(width: 50, height: 30, color: Colors.green),
              ],
            ),
          ),
        ],
      );
    }
    return Scaffold(
      appBar: AppBar(title: const Text('Column')),
      body: ListView(
        // color: Colors.blue,
        // child: Column(
          children: [
            buildExample('1. start (默认)', MainAxisAlignment.start),
            const SizedBox(height: 20),
            buildExample('2. end', MainAxisAlignment.end),
            const SizedBox(height: 20),
            buildExample('3. center', MainAxisAlignment.center),
            const SizedBox(height: 20),
            buildExample('4. spaceBetween', MainAxisAlignment.spaceBetween),
            const SizedBox(height: 20),
            buildExample('5. spaceAround', MainAxisAlignment.spaceAround),
            const SizedBox(height: 20),
            buildExample('6. spaceEvenly', MainAxisAlignment.spaceEvenly),
          ],
        // ))
      ));

  }
}


CrossAxisAlignment

  1. CrossAxisAlignment.center(默认值)
    将子组件在水平方向上居中对齐。
  2. CrossAxisAlignment.start
    将子组件在水平方向上靠左对齐(如果设置了从右到左的文本方向,则靠右对齐)。
  3. CrossAxisAlignment.end
    将子组件在水平方向上靠右对齐(如果设置了从右到左的文本方向,则靠左对齐)。
  4. CrossAxisAlignment.stretch
    拉伸子组件以填满整个交叉轴(即水平方向)的可用空间。需要注意的是,使用此属性时,子组件自身不能设置固定的宽度约束。
  5. CrossAxisAlignment.baseline
    按照子组件的文本基线(typographic baseline)进行对齐。这通常用于水平主轴(如 Row),当子组件包含不同字体大小或不同字体的文本时,使用基线对齐能产生更好的视觉效果。如果主轴是垂直的(如 Column),该值会被当作 start 处理。
import 'package:flutter/material.dart';

void main(List<String> args) {
  runApp(const MaterialApp(home: HomePage()));
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  Widget buildExample(String title, CrossAxisAlignment alignment) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      
      children: [
        Text(title,
            style: const TextStyle(
                fontWeight: FontWeight.bold, fontSize: 14)),
        const SizedBox(height: 4),
        Container(
          width: 200,
          height: 150,
          color: Colors.grey.shade200,
          child: Column(
            crossAxisAlignment: alignment,
            textBaseline: TextBaseline.alphabetic,
            children: [
              Container(width: 80, height: 30, color: Colors.blue),
              Container(width: 120, height: 30, color: Colors.red),
              Container(width: 50, height: 30, color: Colors.green),
            ],
          ),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('CrossAxisAlignment 演示')),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            buildExample('1. center (默认)', CrossAxisAlignment.center),
            const SizedBox(height: 20),
            buildExample('2. start', CrossAxisAlignment.start),
            const SizedBox(height: 20),
            buildExample('3. end', CrossAxisAlignment.end),
            const SizedBox(height: 20),
            buildExample('4. stretch', CrossAxisAlignment.stretch),
            const SizedBox(height: 20),
            buildExample('5. baseline', CrossAxisAlignment.baseline),
          ],
        ),
      ),
    );
  }
}

Row

和Column类似,只是Row是水平方向的。

Flex

Flex可以设置水平或者垂直方向布局。

Expanded的Flex可以决定占多大的位置。

import 'package:flutter/material.dart';

void main(List<String> args) {
  runApp(const MaterialApp(home: HomePage2()));
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flext 演示')),
      body: Container(
        color: Colors.blue,
        child: Flex(
          direction: Axis.horizontal,
          children: [
            Expanded(
              flex: 2,
              child: Container(color: Colors.green, height: 20),
            ),
            Expanded(flex: 1, child: Container(color: Colors.black)),
          ],
        ),
      ),
    );
  }
}

// Flexible如果设置了fit:FlexFit.tight等于Expand组件。
// 只有设置fit:FlextFit.loose,才会是真实的宽度
class HomePage1 extends StatelessWidget {
  const HomePage1({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flext 演示')),
      body: Container(
        color: Colors.blue,
        child: Flex(
          direction: Axis.horizontal,
          children: [
            Flexible(
              fit: FlexFit.loose,
              flex: 1,
              child: Container(color: Colors.green, height: 20, width: 20),
            ),
            Flexible(
              fit: FlexFit.loose,
              flex: 2,
              child: Container(color: Colors.black, height: 20, width: 20),
            ),
          ],
        ),
      ),
    );
  }
}

class HomePage2 extends StatelessWidget {
  const HomePage2({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flext 演示')),
      body: Container(
        color: Colors.blue,
        child: Flex(
          direction: Axis.horizontal,
          children: [
            Flexible(
              fit: FlexFit.loose,
              flex: 1,
              child: Container(color: Colors.green, height: 20, width: 200),
            ),
            Flexible(
              fit: FlexFit.loose,
              flex: 2,
              child: Container(color: Colors.black, height: 20, width: 200),
            ),
            Flexible(
              fit: FlexFit.loose,
              flex: 20,
              child: Container(color: Colors.cyan, height: 20, width: 200),
            ),
          ],
        ),
      ),
    );
  }
}

import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: FlexTestPage()));

class FlexTestPage extends StatelessWidget {
  const FlexTestPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('FlexFit.loose 真相验证')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const Text('1. 使用 FlexFit.loose (不强制拉伸)'),
          const SizedBox(height: 10),
          
          // 这里的灰色背景是为了让你看清 Row 到底给了子组件多少空间
          Container(
            color: Colors.grey[300], 
            height: 60,
            child: Row(
              children: [
                // 绿色块:loose,宽 50
                Flexible(
                  flex: 1,
                  fit: FlexFit.loose,
                  child: Container(
                    color: Colors.green,
                    width: 50, // 只要 50
                    height: 50,
                    alignment: Alignment.center,
                    child: const Text('50', style: TextStyle(color: Colors.white)),
                  ),
                ),
                
                // 黑色块:loose,宽 50
                Flexible(
                  flex: 2, // 虽然 flex 是 2,但在 loose 下没用
                  fit: FlexFit.loose,
                  child: Container(
                    color: Colors.black,
                    width: 50, // 只要 50
                    height: 50,
                    alignment: Alignment.center,
                    child: const Text('50', style: TextStyle(color: Colors.white)),
                  ),
                ),

                // 【关键】黄色块:用来抢占剩余空间
                // 如果前面两个被拉伸了,这个黄色块就会消失或变小
                Expanded(
                  flex: 1,
                  child: Container(
                    color: Colors.yellow,
                    alignment: Alignment.center,
                    child: const Text('我是剩余空间', style: TextStyle(color: Colors.black)),
                  ),
                ),
              ],
            ),
          ),

          const SizedBox(height: 40),
          const Text('2. 使用 FlexFit.tight (强制拉伸,即 Expanded)'),
          const SizedBox(height: 10),

          Container(
            color: Colors.grey[300],
            height: 60,
            child: Row(
              children: [
                // 绿色块:tight,会被拉伸
                Flexible(
                  flex: 1,
                  fit: FlexFit.tight, // 等同于 Expanded
                  child: Container(
                    color: Colors.green,
                    width: 50, // 这个宽度会被忽略,强制拉伸
                    height: 50,
                    alignment: Alignment.center,
                    child: const Text('拉伸了', style: TextStyle(color: Colors.white)),
                  ),
                ),
                
                // 黑色块:tight,会被拉伸
                Flexible(
                  flex: 2,
                  fit: FlexFit.tight,
                  child: Container(
                    color: Colors.black,
                    width: 50, // 这个宽度会被忽略,强制拉伸
                    height: 50,
                    alignment: Alignment.center,
                    child: const Text('我也拉伸了', style: TextStyle(color: Colors.white)),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}