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 主轴对齐方式
- MainAxisAlignment.start(默认值)
将子组件线性排列,整体靠顶部对齐。 - MainAxisAlignment.end
将子组件线性排列,整体靠底部对齐。 - MainAxisAlignment.center
将子组件线性排列,整体在垂直方向上居中。 - MainAxisAlignment.spaceBetween
将主轴方向上的空白区域均分,使得子组件之间的间距相等。第一个和最后一个子组件分别紧贴顶部和底部,首尾与边缘之间没有间隙。 - MainAxisAlignment.spaceAround
将主轴方向上的空白区域均分,使得子组件之间的间距相等。但首尾子组件与顶部/底部边缘的间距,是子组件之间间距的一半。 - 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
- CrossAxisAlignment.center(默认值)
将子组件在水平方向上居中对齐。 - CrossAxisAlignment.start
将子组件在水平方向上靠左对齐(如果设置了从右到左的文本方向,则靠右对齐)。 - CrossAxisAlignment.end
将子组件在水平方向上靠右对齐(如果设置了从右到左的文本方向,则靠左对齐)。 - CrossAxisAlignment.stretch
拉伸子组件以填满整个交叉轴(即水平方向)的可用空间。需要注意的是,使用此属性时,子组件自身不能设置固定的宽度约束。 - 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)),
),
),
],
),
),
],
),
);
}
}