Container
默认情况下 transform 的旋转原点是 左上角 ( Offset.zero )。要让 Container 绕中心旋转 ,需要设置 transformAlignment 属性.
scala
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(
MaterialApp(
theme: ThemeData(scaffoldBackgroundColor: Colors.grey),
home: Scaffold(body: MainPage3()),
),
);
}
class MainPage extends StatelessWidget {
const MainPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// 如果不设置宽高,会覆盖整个屏幕
return Container(
width: 100,
// height: 100,
color: Colors.green,
);
}
}
class MainPage1 extends StatelessWidget {
const MainPage1({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// 如果不设置高度,宽度固定100,高度覆盖整个屏幕
// 同理,如果高度固定,宽度会填充整个屏幕
return Container(
// width: 100,
height: 100,
color: Colors.green,
);
}
}
// Color和 decoration只能同时有一个,就算外面有color里面的decoration不设置color也会报错
class MainPage2 extends StatelessWidget {
const MainPage2({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// 如果不设置高度,宽度固定100,高度覆盖整个屏幕
// 同理,如果高度固定,宽度会填充整个屏幕
return Container(
width: 100,
height: 100,
// color: Colors.green,
decoration: BoxDecoration(
color: Colors.red,
// 设置圆角
borderRadius: BorderRadius.circular(20),
),
child: Text("data"),
);
}
}
class MainPage3 extends StatelessWidget {
const MainPage3({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: 200,
margin: EdgeInsets.all(50),
height: 200,
decoration: BoxDecoration(
color: Colors.cyan,
borderRadius: BorderRadius.circular(20),
border: Border.all(width: 3, color: Colors.white),
),
// 3.14为180度旋转
transform: Matrix4.rotationZ(3.14 / 4),
transformAlignment: Alignment.center, // ✅ 旋转原点设为中心(默认是左上角)
alignment: Alignment.center,
child: Text("hello"),
);
}
}
Container进阶
less
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Container 知识体系',
theme: ThemeData(primarySwatch: Colors.blue),
home: const ContainerGuide(),
);
}
}
// ═══════════════════════════════════════════════════════════
// Container 知识体系总览页
// ═══════════════════════════════════════════════════════════
class ContainerGuide extends StatelessWidget {
const ContainerGuide({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Container 知识体系')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
// ── 本质说明 ──
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
),
child: const Text(
'📖 Container 本质:\n'
'Container 是一个「复合 Widget」,本身不渲染任何内容。\n'
'它会根据你设置的属性,内部组合 Padding / DecoratedBox /\n'
'ConstrainedBox / Transform / Align / ClipPath 等基础 Widget。\n'
'不设任何属性且 child 为 null 时,Container 会尽可能小(不占空间)。',
style: TextStyle(fontSize: 13, height: 1.6),
),
),
const SizedBox(height: 16),
// ── 章节 1:基础属性 ──
_Section(title: '1. 基础属性(color / width / height / child)'),
_BasicDemo(),
// ── 章节 2:margin vs padding ──
_Section(title: '2. margin(外边距)vs padding(内边距)'),
_MarginPaddingDemo(),
// ── 章节 3:alignment ──
_Section(title: '3. alignment(子组件对齐)'),
_AlignmentDemo(),
// ── 章节 4:decoration BoxDecoration ──
_Section(title: '4. decoration(BoxDecoration 装饰)'),
_DecorationDemo(),
// ── 章节 5:gradient 渐变 ──
_Section(title: '5. gradient(渐变背景)'),
_GradientDemo(),
// ── 章节 6:boxShadow 阴影 ──
_Section(title: '6. boxShadow(阴影)'),
_ShadowDemo(),
// ── 章节 7:shape 形状 ──
_Section(title: '7. shape(矩形 / 圆形)'),
_ShapeDemo(),
// ── 章节 8:border + borderRadius 边框 ──
_Section(title: '8. border & borderRadius(边框与圆角)'),
_BorderDemo(),
// ── 章节 9:transform 变换 ──
_Section(title: '9. transform(矩阵变换)'),
_TransformDemo(),
// ── 章节 10:constraints 约束 ──
_Section(title: '10. constraints(BoxConstraints 约束)'),
_ConstraintsDemo(),
// ── 章节 11:foregroundDecoration 前景装饰 ──
_Section(title: '11. foregroundDecoration(前景装饰)'),
_ForegroundDemo(),
// ── 章节 12:clipBehavior 裁剪 ──
_Section(title: '12. clipBehavior(裁剪行为)'),
_ClipDemo(),
// ── 章节 13:尺寸规则 ──
_Section(title: '13. Container 尺寸规则(无 child 时的撑满行为)'),
_SizeRuleDemo(),
// ── 章节 14:包裹顺序 ──
_Section(title: '14. Container 内部包裹顺序(重要!)'),
_WrapOrderDemo(),
],
),
);
}
}
// 通用章节标题组件
class _Section extends StatelessWidget {
final String title;
const _Section({required this.title});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 24, bottom: 12),
child: Text(title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
);
}
}
// ─────────────────────────────────────────────
// 1. 基础属性
// ─────────────────────────────────────────────
// color:背景色(注意:与 decoration 互斥,不能同时设置)
// width / height:固定尺寸
// child:唯一子组件
// ─────────────────────────────────────────────
class _BasicDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 只设 color:会撑满父级可用空间(因为无 child 无尺寸约束时 Container 最大化)
Container(
color: Colors.red.shade200,
width: double.infinity, // 撑满宽度
height: 40,
alignment: Alignment.center,
child: const Text('color + width + height + child'),
),
const SizedBox(height: 8),
// ✅ 推荐:用 decoration 替代 color(更灵活,能加边框/圆角/渐变)
Container(
width: 200,
height: 60,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.green.shade200, // 颜色放在 decoration 里
),
child: const Text('推荐写法:颜色放 decoration'),
),
],
);
}
}
// ─────────────────────────────────────────────
// 2. margin vs padding
// ─────────────────────────────────────────────
// margin:外边距,Container 与父级的距离(在 decoration 外面)
// padding:内边距,child 与 decoration 的距离(在 decoration 里面)
// 包裹顺序:margin → decoration → padding → child
// ─────────────────────────────────────────────
class _MarginPaddingDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
// 外层灰色 = 父容器,用于观察 margin
Container(
color: Colors.grey.shade300,
child: Container(
margin: const EdgeInsets.all(20), // 外边距:与灰色父级隔开 20
padding: const EdgeInsets.all(16), // 内边距:文字与蓝色边隔开 16
color: Colors.blue.shade200,
child: const Text('蓝色=Container本体\n灰色=margin 外露区域\n文字到蓝边=padding'),
),
),
const SizedBox(height: 8),
const Text(
'margin:decoration 外面的空白(灰色区域)\n'
'padding:decoration 里面的空白(文字到蓝边距离)',
style: TextStyle(fontSize: 12, color: Colors.grey),
),
],
);
}
}
// ─────────────────────────────────────────────
// 3. alignment
// ─────────────────────────────────────────────
// alignment:决定 child 在 Container 内的对齐位置
// 设置后 Container 会撑满可用空间(像 Align 一样)
// 常用:Alignment.center / topLeft / bottomRight / centerLeft ...
// ─────────────────────────────────────────────
class _AlignmentDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: 120,
width: double.infinity,
color: Colors.orange.shade100,
alignment: Alignment.bottomRight, // 子组件右下角对齐
child: const Text('我在右下角'),
);
}
}
// ─────────────────────────────────────────────
// 4. decoration(BoxDecoration)
// ─────────────────────────────────────────────
// BoxDecoration 是最常用的装饰,支持:
// color / border / borderRadius / gradient / boxShadow / image / shape
// ⚠️ decoration.color 与 Container.color 互斥,不能同时设置
// ─────────────────────────────────────────────
class _DecorationDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
// BoxDecoration 集中管理所有视觉装饰
decoration: BoxDecoration(
color: Colors.purple.shade100, // 背景色
borderRadius: BorderRadius.circular(12), // 圆角
border: Border.all( // 边框
color: Colors.purple,
width: 2,
),
),
child: const Text('decoration 同时设置:背景色 + 圆角 + 边框'),
);
}
}
// ─────────────────────────────────────────────
// 5. gradient 渐变
// ─────────────────────────────────────────────
// 三种渐变:
// LinearGradient 线性渐变(最常用)
// RadialGradient 径向渐变(从中心向外)
// SweepGradient 扫描渐变(雷达图效果)
// ⚠️ gradient 会覆盖 color(gradient 优先级更高)
// ─────────────────────────────────────────────
class _GradientDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
// 线性渐变
Container(
height: 60,
width: double.infinity,
alignment: Alignment.center,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft, // 起点位置
end: Alignment.centerRight, // 终点位置
colors: [Colors.red, Colors.blue], // 颜色列表
stops: [0.0, 1.0], // 每个颜色的位置(0~1)
),
),
child: const Text('LinearGradient 线性渐变', style: TextStyle(color: Colors.white)),
),
const SizedBox(height: 8),
// 径向渐变
Container(
height: 80,
width: 80,
decoration: const BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [Colors.yellow, Colors.orange, Colors.red],
),
),
),
],
);
}
}
// ─────────────────────────────────────────────
// 6. boxShadow 阴影
// ─────────────────────────────────────────────
// boxShadow 是一个 List,可以叠加多个阴影
// BoxShadow 属性:
// color 阴影颜色
// blurRadius 模糊半径(越大越虚)
// spreadRadius 扩展半径(正值扩大,负值缩小)
// offset 偏移量(dx, dy)
// ⚠️ 阴影需要 backgroundColor 或 elevation 配合才明显
// ─────────────────────────────────────────────
class _ShadowDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
margin: const EdgeInsets.all(8), // 留白让阴影可见
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
// 第一层:深色近阴影
BoxShadow(
color: Colors.black.withValues(alpha: 0.2),
blurRadius: 8, // 模糊程度
spreadRadius: 2, // 阴影扩散
offset: const Offset(0, 4), // x=0(不左右偏), y=4(向下偏)
),
// 第二层:浅色远阴影(可叠加多层制造立体感)
BoxShadow(
color: Colors.blue.withValues(alpha: 0.1),
blurRadius: 20,
spreadRadius: 0,
offset: const Offset(0, 0),
),
],
),
child: const Text('boxShadow 可叠加多层\n制造立体卡片效果'),
);
}
}
// ─────────────────────────────────────────────
// 7. shape 形状
// ─────────────────────────────────────────────
// shape 有两个值:
// BoxShape.rectangle 矩形(默认)
// BoxShape.circle 圆形
// ⚠️ 设为 circle 时,borderRadius 会被忽略
// ─────────────────────────────────────────────
class _ShapeDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
// 矩形(默认)
Container(
width: 80,
height: 80,
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.rectangle, // 默认值
color: Colors.teal,
borderRadius: BorderRadius.circular(12),
),
child: const Text('矩形', style: TextStyle(color: Colors.white)),
),
const SizedBox(width: 16),
// 圆形
Container(
width: 80,
height: 80,
alignment: Alignment.center,
decoration: const BoxDecoration(
shape: BoxShape.circle, // 设为圆形
color: Colors.indigo,
),
child: const Text('圆形', style: TextStyle(color: Colors.white)),
),
],
);
}
}
// ─────────────────────────────────────────────
// 8. border & borderRadius
// ─────────────────────────────────────────────
// border:边框,支持四边统一或分别设置
// Border.all() 四边相同
// Border(top:..., bottom:...) 各边不同
// borderRadius:圆角,支持统一或分别设置
// BorderRadius.all(Radius.circular(12)) 四角相同
// BorderRadius.only(topLeft:...) 指定角
// ⚠️ 圆形(shape: circle)下 borderRadius 无效
// ─────────────────────────────────────────────
class _BorderDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
// ① 四边相同边框 + 统一圆角(✅ 兼容)
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.yellow.shade100,
border: Border.all(color: Colors.orange, width: 2),
borderRadius: BorderRadius.circular(8),
),
child: const Text('① Border.all 四边相同 + 圆角(兼容)'),
),
const SizedBox(height: 8),
// ② 各边不同 border(⚠️ 不能配 borderRadius,否则报错)
// 错误信息:A borderRadius can only be given on borders with uniform colors
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.cyan.shade50,
border: const Border(
top: BorderSide(color: Colors.red, width: 3),
bottom: BorderSide(color: Colors.blue, width: 3),
),
// ⚠️ 这里不能加 borderRadius,因为各边颜色不同
),
child: const Text('② 各边不同 border(不能配圆角)'),
),
const SizedBox(height: 8),
// ③ 指定角圆角 + 统一颜色 border(✅ 兼容)
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.green.shade50,
border: Border.all(color: Colors.green, width: 2),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
bottomRight: Radius.circular(16),
),
),
child: const Text('③ 指定角圆角 + 统一颜色 border(兼容)'),
),
],
);
}
}
// ─────────────────────────────────────────────
// 9. transform 变换
// ─────────────────────────────────────────────
// transform:通过 Matrix4 矩阵做变换(平移/旋转/缩放/倾斜)
// 常用 Matrix4 方法:
// Matrix4.translationValues(x, y, z) 平移
// Matrix4.rotationZ(radians) 绕 Z 轴旋转
// Matrix4.scale(x, y, z) 缩放
// transformAlignment:变换的原点(默认中心)
// ⚠️ transform 不影响布局(不影响占位空间),只是视觉变换
// ─────────────────────────────────────────────
class _TransformDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
// 平移
Container(
width: 60,
height: 60,
color: Colors.green.shade300,
alignment: Alignment.center,
transform: Matrix4.translationValues(10, -10, 0), // 右移10,上移10
child: const Text('平移', style: TextStyle(fontSize: 11)),
),
const SizedBox(width: 30),
// 旋转
Container(
width: 60,
height: 60,
color: Colors.red.shade300,
alignment: Alignment.center,
transform: Matrix4.rotationZ(0.3), // 旋转 0.3 弧度
child: const Text('旋转', style: TextStyle(fontSize: 11)),
),
const SizedBox(width: 30),
// 缩放
Container(
width: 60,
height: 60,
color: Colors.purple.shade300,
alignment: Alignment.center,
transform: Matrix4.diagonal3Values(1.3, 1.3, 1.0), // 放大 1.3 倍
child: const Text('缩放', style: TextStyle(fontSize: 11)),
),
],
);
}
}
// ─────────────────────────────────────────────
// 10. constraints 约束
// ─────────────────────────────────────────────
// constraints:BoxConstraints,限制 Container 的尺寸范围
// minWidth / maxWidth / minHeight / maxHeight
// 与 width/height 的区别:
// width/height 是固定值(本质是 constraints 的 min=max)
// constraints 是范围,更灵活
// 优先级:constraints > width/height
// ─────────────────────────────────────────────
class _ConstraintsDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
// 约束:最小高度 50,最大高度 100(随 child 内容在此范围)
constraints: const BoxConstraints(
minWidth: 100,
maxWidth: 300,
minHeight: 50,
maxHeight: 100,
),
color: Colors.pink.shade100,
alignment: Alignment.center,
child: const Text('constraints 限制尺寸范围'),
);
}
}
// ─────────────────────────────────────────────
// 11. foregroundDecoration 前景装饰
// ─────────────────────────────────────────────
// foregroundDecoration:绘制在 child 之上的装饰(盖在 child 上面)
// 与 decoration 区别:
// decoration:背景层(在 child 下面)
// foregroundDecoration:前景层(在 child 上面)
// 常用于:遮罩、水印、叠加边框
// ─────────────────────────────────────────────
class _ForegroundDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
// 背景装饰:在 child 下面
decoration: BoxDecoration(
color: Colors.blue.shade100,
borderRadius: BorderRadius.circular(8),
),
// 前景装饰:在 child 上面(盖住文字)
foregroundDecoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 3),
borderRadius: BorderRadius.circular(8),
color: Colors.black.withValues(alpha: 0.2), // 半透明遮罩盖在文字上
),
child: const Text('红色边框+半透明遮罩 = foregroundDecoration\n(绘制在 child 之上)'),
);
}
}
// ─────────────────────────────────────────────
// 12. clipBehavior 裁剪
// ─────────────────────────────────────────────
// clipBehavior:当内容超出边界时是否裁剪
// Clip.none 不裁剪(默认,超出部分可见)
// Clip.hardEdge 硬边缘裁剪(最快,边缘略锯齿)
// Clip.antiAlias 抗锯齿裁剪(边缘平滑,稍慢)
// Clip.antiAliasWithSaveLayer 最平滑(最慢,少用)
// 场景:圆角容器内放图片/超大内容时需要裁剪
// ─────────────────────────────────────────────
class _ClipDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
// 不裁剪:内容溢出圆角
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(20),
),
clipBehavior: Clip.none, // 默认,内容会溢出圆角
child: Container(
margin: const EdgeInsets.all(20),
color: Colors.yellow,
),
),
const SizedBox(width: 16),
// 裁剪:内容被圆角裁剪
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(20),
),
clipBehavior: Clip.antiAlias, // 内容被圆角裁剪
child: Container(
margin: const EdgeInsets.all(20),
color: Colors.yellow,
),
),
const SizedBox(width: 8),
const Text('左:Clip.none\n右:Clip.antiAlias', style: TextStyle(fontSize: 11)),
],
);
}
}
// ─────────────────────────────────────────────
// 13. 尺寸规则
// ─────────────────────────────────────────────
// Container 的尺寸由「约束 + 属性 + child」共同决定:
// ① 有 child:默认跟随 child 尺寸
// ② 无 child 无尺寸:撑满父级可用空间(最大化)
// ③ 有 width/height:使用指定值
// ④ constraints 优先级最高
// ─────────────────────────────────────────────
class _SizeRuleDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 无 child 无尺寸:撑满高度(这里被外层 ListView 限制为不定高,所以需指定)
Container(
width: double.infinity,
height: 40,
color: Colors.amber,
alignment: Alignment.center,
child: const Text('无 child + 指定 height → 用指定值'),
),
const SizedBox(height: 8),
// 有 child:跟随 child 尺寸
Container(
color: Colors.lightGreen,
child: const Padding(
padding: EdgeInsets.all(8),
child: Text('有 child → 跟随 child 尺寸(紧贴内容)'),
),
),
],
);
}
}
// ─────────────────────────────────────────────
// 14. 内部包裹顺序(核心知识点!)
// ─────────────────────────────────────────────
// Container 内部实际生成的 Widget 树(从外到内):
//
// margin (Padding)
// └─ transform (Transform)
// └─ decoration (DecoratedBox)
// └─ foregroundDecoration + clip (ClipPath)
// └─ constraints (ConstrainedBox)
// └─ padding (Padding)
// └─ alignment (Align)
// └─ child
//
// 这就是为什么:
// - margin 在装饰外面,padding 在装饰里面
// - transform 不影响布局(在最外层视觉变换)
// - decoration 在 padding 外(背景包含 padding 区域)
// ─────────────────────────────────────────────
class _WrapOrderDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
// ① 最外层:margin(外边距)
margin: const EdgeInsets.all(16),
// ② transform(视觉变换,不影响布局)
transform: Matrix4.identity(),
// ③ decoration(背景装饰,包含 padding 区域)
decoration: BoxDecoration(
color: Colors.indigo.shade100,
border: Border.all(color: Colors.indigo, width: 2),
borderRadius: BorderRadius.circular(8),
),
// ④ constraints(尺寸约束)
constraints: const BoxConstraints(minHeight: 80),
// ⑤ padding(内边距,在装饰里面)
padding: const EdgeInsets.all(16),
// ⑥ alignment(child 对齐)
alignment: Alignment.center,
// ⑦ clipBehavior(裁剪)
clipBehavior: Clip.antiAlias,
// ⑧ 最内层:child
child: const Text(
'margin → transform → decoration\n→ clip → constraints\n→ padding → alignment → child',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 12),
),
);
}
}
Center
对齐,用Center组件,和用Container的对齐属性。性能一致。
Center本质就是Align。
scala
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(
MaterialApp(
theme: ThemeData(scaffoldBackgroundColor: Colors.grey),
home: Scaffold(body: MainPage2()),
),
);
}
class MainPage extends StatelessWidget {
const MainPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// center不能设置宽高,不能设置背景颜色
return Center(child: Text("hello"));
}
}
class MainPage1 extends StatelessWidget {
const MainPage1({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// 通过Container设置大小
return Center(
child: Container(
width: 100,
height: 100,
color: Colors.blueGrey,
alignment: .center,
child: Text("hello"),
),
);
}
}
class MainPage2 extends StatelessWidget {
const MainPage2({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// 通过Container设置大小
return Center(
child: Container(
width: 100,
height: 100,
color: Colors.blueGrey,
// alignment: .center,
child: Center(child: Text("hello")),
),
);
}
}
factor等比例缩放。
less
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(
MaterialApp(
theme: ThemeData(
scaffoldBackgroundColor: const Color.fromARGB(255, 223, 212, 212),
),
home: Scaffold(body: MainPage()),
),
);
}
class MainPage extends StatelessWidget {
const MainPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black,
child: Container(
color: Colors.white,
child: Center(
widthFactor: 2,
heightFactor: 2,
child: Container(width: 100, height: 100, color: Colors.green),
),
),
);
}
}
Center进阶
less
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('Center 全方位知识点')),
body: ListView(
padding: const EdgeInsets.all(16),
children: const [
_Section1_WhatIsCenter(),
_Section2_ShrinkWrapMechanism(),
_Section3_TightVsLooseConstraint(),
_Section4_WidthFactorDetail(),
_Section5_HeightFactorDetail(),
_Section6_InRowColumn(),
_Section7_CenterVsAlign(),
_Section8_CenterVsContainer(),
_Section9_CommonUsage(),
_Section10_Summary(),
],
),
);
}
}
// ────────────────────────────────────────────────────────────────
// ① 知识点 1:Center 的本质 ------ Align 的子类,固定居中
// ────────────────────────────────────────────────────────────────
class _Section1_WhatIsCenter extends StatelessWidget {
const _Section1_WhatIsCenter();
@override
Widget build(BuildContext context) {
return _Card(
title: '① Center 的本质',
subtitle: 'Center = Align(alignment: Alignment.center)',
children: [
const Text(
'源码定义(简化):\n'
'class Center extends Align {\n'
' const Center({ super.key, super.widthFactor,\n'
' super.heightFactor, super.child })\n'
' : super(alignment: Alignment.center);\n'
'}\n\n'
'Center 没有任何额外逻辑,就是 Align 写死了\n'
'alignment.center 的语法糖。',
style: TextStyle(fontSize: 12, fontFamily: 'monospace'),
),
const SizedBox(height: 8),
// 直接验证:两种写法等价
Row(
children: [
Expanded(
child: Container(
height: 100,
color: Colors.blue.shade50,
child: const Center(child: Text('Center 写法')),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
height: 100,
color: Colors.green.shade50,
child: const Align(
alignment: Alignment.center,
child: Text('Align 写法'),
),
),
),
],
),
const SizedBox(height: 4),
const Text('↑ 两者渲染结果完全相同',
style: TextStyle(fontSize: 11, color: Colors.grey)),
],
);
}
}
// ────────────────────────────────────────────────────────────────
// ② 知识点 2:核心机制 ------ shrinkWrap 逻辑(来自源码)
// ────────────────────────────────────────────────────────────────
class _Section2_ShrinkWrapMechanism extends StatelessWidget {
const _Section2_ShrinkWrapMechanism();
@override
Widget build(BuildContext context) {
return _Card(
title: '② 核心机制:shrinkWrap 逻辑',
subtitle: '决定 Center 是"撑满"还是"跟随 child"',
children: [
const Text(
'源码(shifted_box.dart:478-497):\n\n'
'shrinkWrapWidth = widthFactor != null || maxWidth == ∞\n'
'shrinkWrapHeight = heightFactor != null || maxHeight == ∞\n\n'
'size = constrain(Size(\n'
' shrinkWrapWidth ? child.w * factor : ∞,\n'
' shrinkWrapHeight ? child.h * factor : ∞,\n'
'))\n\n'
'┌───────────────┬────────────────────┬──────────────────┐\n'
'│ 条件 │ shrinkWrap │ Center 尺寸 │\n'
'├───────────────┼────────────────────┼──────────────────┤\n'
'│ 有 factor │ true │ child × factor │\n'
'│ 无 factor + │ false │ expand 到约束 max │\n'
'│ 有界约束 │ │ │\n'
'│ 无 factor + │ true(因为 max=∞) │ child 尺寸 │\n'
'│ 无界约束 │ │ │\n'
'└───────────────┴────────────────────┴──────────────────┘',
style: TextStyle(fontSize: 11, fontFamily: 'monospace'),
),
],
);
}
}
// ────────────────────────────────────────────────────────────────
// ③ 知识点 3:紧密约束 vs 宽松约束下的行为对比
// ────────────────────────────────────────────────────────────────
class _Section3_TightVsLooseConstraint extends StatelessWidget {
const _Section3_TightVsLooseConstraint();
@override
Widget build(BuildContext context) {
return _Card(
title: '③ 紧密约束 vs 宽松约束',
subtitle: '约束类型决定 Center 能否自由决定尺寸',
children: [
// ── 紧密约束场景 ──
const Text('场景 A:紧密约束(Container 有固定尺寸)',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12)),
const Text('constraints 是 tight → Center 被迫等于约束值',
style: TextStyle(fontSize: 11, color: Colors.grey)),
const SizedBox(height: 6),
Row(
children: [
// A1:紧密约束 + 无 factor
Expanded(
child: Container(
height: 120,
color: Colors.blue.shade100,
child: const ColoredBox(
color: Colors.blue,
child: Center(child: Text('A1 tight+无factor')),
),
),
),
const SizedBox(width: 8),
// A2:紧密约束 + 有 factor
Expanded(
child: Container(
height: 120,
color: Colors.orange.shade100,
child: ColoredBox(
color: Colors.orange.shade700,
child: const Center(
heightFactor: 1.5,
child: Text('A2 tight+有factor'),
),
),
),
),
],
),
const SizedBox(height: 4),
const Text('A1 蓝色撑满120高(tight锁死+无factor→expand)',
style: TextStyle(fontSize: 10, color: Colors.blue)),
const Text('A2 橙色也撑满120高(但不是factor不生效,'
'是橙色外层Container必须撑满父级)',
style: TextStyle(fontSize: 10, color: Colors.orange)),
const SizedBox(height: 10),
// ── 宽松约束场景 ──
const Text('场景 B:宽松约束(UnconstrainedBox 解除约束)',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12)),
const Text('constraints 是 loose → Center 可以自由决定尺寸',
style: TextStyle(fontSize: 11, color: Colors.grey)),
const SizedBox(height: 6),
Row(
children: [
// B1:宽松约束 + 无 factor
Expanded(
child: Container(
height: 120,
color: Colors.green.shade100,
child: UnconstrainedBox(
child: ColoredBox(
color: Colors.green,
child: Center(
child: Container(width: 60, height: 30,
color: Colors.white),
),
),
),
),
),
const SizedBox(width: 8),
// B2:宽松约束 + 有 factor
Expanded(
child: Container(
height: 120,
color: Colors.purple.shade100,
child: UnconstrainedBox(
child: ColoredBox(
color: Colors.purple,
child: Center(
widthFactor: 2,
heightFactor: 2,
child: Container(width: 60, height: 30,
color: Colors.white),
),
),
),
),
),
],
),
const SizedBox(height: 4),
const Text('B1 绿色=30高(宽松+无factor→child尺寸=30)',
style: TextStyle(fontSize: 10, color: Colors.green)),
const Text('B2 紫色=120×60(宽松+有factor→child×factor)',
style: TextStyle(fontSize: 10, color: Colors.purple)),
],
);
}
}
// ────────────────────────────────────────────────────────────────
// ④ 知识点 4:widthFactor 详细行为
// ────────────────────────────────────────────────────────────────
class _Section4_WidthFactorDetail extends StatelessWidget {
const _Section4_WidthFactorDetail();
@override
Widget build(BuildContext context) {
return _Card(
title: '④ widthFactor 详细行为',
subtitle: 'Center 宽度 = child 宽度 × widthFactor',
children: [
const Text('前提:必须有宽松 width 约束才能看出效果\n'
'(tight 约束下 Center 被锁死,factor 不影响实际渲染)',
style: TextStyle(fontSize: 11)),
const SizedBox(height: 8),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_FactorDemo(label: 'factor=1', factor: 1),
const SizedBox(width: 8),
_FactorDemo(label: 'factor=2', factor: 2),
const SizedBox(width: 8),
_FactorDemo(label: 'factor=3', factor: 3),
],
),
],
);
}
}
class _FactorDemo extends StatelessWidget {
final String label;
final double factor;
const _FactorDemo({required this.label, required this.factor});
@override
Widget build(BuildContext context) {
return Expanded(
child: Column(
children: [
Container(
height: 100,
color: Colors.grey.shade200,
child: UnconstrainedBox(
child: ColoredBox(
color: Colors.cyan.withValues(alpha: 0.6),
child: Center(
widthFactor: factor,
child: Container(width: 30, height: 30,
color: Colors.cyan.shade900),
),
),
),
),
const SizedBox(height: 4),
Text('$label\nchild×$factor',
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 10)),
],
),
);
}
}
// ────────────────────────────────────────────────────────────────
// ⑤ 知识点 5:heightFactor 详细行为
// ────────────────────────────────────────────────────────────────
class _Section5_HeightFactorDetail extends StatelessWidget {
const _Section5_HeightFactorDetail();
@override
Widget build(BuildContext context) {
return _Card(
title: '⑤ heightFactor 详细行为',
subtitle: 'Center 高度 = child 高度 × heightFactor',
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_HeightDemo(label: '无 factor'),
const SizedBox(width: 8),
_HeightDemo(label: 'factor=2', factor: 2),
const SizedBox(width: 8),
_HeightDemo(label: 'factor=4', factor: 4),
],
),
const SizedBox(height: 8),
const Text('注意:无 factor 时 Center 撑满父级可用高度(expand),'
'有 factor 时 shrink-wrap',
style: TextStyle(fontSize: 11)),
],
);
}
}
class _HeightDemo extends StatelessWidget {
final String label;
final double? factor;
const _HeightDemo({required this.label, this.factor});
@override
Widget build(BuildContext context) {
return Expanded(
child: Column(
children: [
Container(
height: 120,
color: Colors.grey.shade200,
child: factor != null
? UnconstrainedBox(
child: ColoredBox(
color: Colors.deepPurple.withValues(alpha: 0.6),
child: Center(
heightFactor: factor,
child: Container(width: 40, height: 25,
color: Colors.deepPurple.shade900),
),
),
)
: ColoredBox(
color: Colors.deepPurple.shade300,
child: Center(
child: Container(width: 40, height: 25,
color: Colors.deepPurple.shade900),
),
),
),
const SizedBox(height: 4),
Text(label, style: const TextStyle(fontSize: 10)),
],
),
);
}
}
// ────────────────────────────────────────────────────────────────
// ⑥ 知识点 6:Row / Column 中的 Center
// ────────────────────────────────────────────────────────────────
class _Section6_InRowColumn extends StatelessWidget {
const _Section6_InRowColumn();
@override
Widget build(BuildContext context) {
return _Card(
title: '⑥ Row / Column 中的 Center',
subtitle: 'crossAxisAlignment 决定 Center 在交叉轴的约束类型',
children: [
const Text('Row 中主轴=水平,交叉轴=垂直:',
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold)),
const SizedBox(height: 4),
Row(
crossAxisAlignment: CrossAxisAlignment.center, // 默认
children: [
Container(width: 60, height: 80, color: Colors.red),
Center(
heightFactor: 2,
child: Container(width: 40, height: 30, color: Colors.blue),
),
Container(width: 50, height: 60, color: Colors.green),
],
),
const SizedBox(height: 4),
const Text('默认 crossAxisAlignment.center → 交叉轴给宽松约束 → '
'Center(heightFactor=2) = 30×2 = 60 高',
style: TextStyle(fontSize: 11, color: Colors.blue)),
const SizedBox(height: 10),
const Text('Row 中 crossAxisAlignment.stretch:',
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold)),
const SizedBox(height: 4),
Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(width: 60, height: 80, color: Colors.red),
Center(
heightFactor: 2,
child: Container(width: 40, height: 30, color: Colors.blue),
),
Container(width: 50, height: 60, color: Colors.green),
],
),
const SizedBox(height: 4),
const Text('stretch → 交叉轴给 tight(80) 约束 → Center 高度被锁死在 80,'
'heightFactor 在 tight 下无法让 Center 变小',
style: TextStyle(fontSize: 11, color: Colors.blue)),
],
);
}
}
// ────────────────────────────────────────────────────────────────
// ⑦ 知识点 7:Center vs Align
// ────────────────────────────────────────────────────────────────
class _Section7_CenterVsAlign extends StatelessWidget {
const _Section7_CenterVsAlign();
@override
Widget build(BuildContext context) {
return _Card(
title: '⑦ Center vs Align',
subtitle: 'Center 是 Align 固定居中的简写',
children: [
const Text('两者唯一区别:alignment 是否固定',
style: TextStyle(fontSize: 12)),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: Container(
height: 90,
color: Colors.blue.shade50,
child: const Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Center',
style: TextStyle(fontSize: 11,
fontWeight: FontWeight.bold)),
Text('固定 Alignment.center',
style: TextStyle(fontSize: 10)),
Text('可设 widthFactor/heightFactor',
style: TextStyle(fontSize: 10)),
],
),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
height: 90,
color: Colors.orange.shade50,
child: const Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Align',
style: TextStyle(fontSize: 11,
fontWeight: FontWeight.bold)),
Text('可设任意 Alignment',
style: TextStyle(fontSize: 10)),
Text('可设 widthFactor/heightFactor',
style: TextStyle(fontSize: 10)),
],
),
),
),
],
),
const SizedBox(height: 8),
const Text('选择建议:纯粹居中用 Center(语义清晰),'
'需要其他对齐用 Align',
style: TextStyle(fontSize: 11, fontStyle: FontStyle.italic)),
],
);
}
}
// ────────────────────────────────────────────────────────────────
// ⑧ 知识点 8:Center vs Container + alignment
// ────────────────────────────────────────────────────────────────
class _Section8_CenterVsContainer extends StatelessWidget {
const _Section8_CenterVsContainer();
@override
Widget build(BuildContext context) {
return _Card(
title: '⑧ Center vs Container(alignment: center)',
subtitle: '性能等价,但 Container 更灵活',
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(
'Center(child) → RenderPositionedBox → child\n'
'Container( → RenderContainer\n'
' alignment: center, → └─ RenderPositionedBox → child\n'
' child: ...)',
style: TextStyle(fontSize: 11, fontFamily: 'monospace'),
),
),
const SizedBox(height: 8),
const Text('Container 额外支持:color / padding / margin / decoration / transform 等',
style: TextStyle(fontSize: 11)),
const SizedBox(height: 6),
Row(
children: [
// 只有居中 → 用 Center
Expanded(
child: Container(
height: 60,
color: Colors.blue.shade50,
child: const Center(child: Text('只有居中 → Center')),
),
),
const SizedBox(width: 8),
// 既要居中又要背景色 → Container
Expanded(
child: Container(
height: 60,
color: Colors.orange.shade100,
alignment: Alignment.center,
child: const Text('居中+背景 → Container'),
),
),
],
),
],
);
}
}
// ────────────────────────────────────────────────────────────────
// ⑨ 知识点 9:常见使用场景
// ────────────────────────────────────────────────────────────────
class _Section9_CommonUsage extends StatelessWidget {
const _Section9_CommonUsage();
@override
Widget build(BuildContext context) {
return _Card(
title: '⑨ 常见使用场景',
subtitle: '',
children: [
Row(
children: [
Expanded(
child: _UsageBox(
icon: Icons.sync,
title: '加载指示器',
code: 'const Center(child: CircularProgressIndicator())',
),
),
const SizedBox(width: 8),
Expanded(
child: _UsageBox(
icon: Icons.inbox,
title: '空数据提示',
code: 'const Center(child: Text("暂无数据"))',
),
),
],
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: _UsageBox(
icon: Icons.check_circle,
title: '成功提示',
code: 'Center(child: Column(...))',
),
),
const SizedBox(width: 8),
Expanded(
child: _UsageBox(
icon: Icons.widgets,
title: '居中卡片',
code: 'Center(child: Card(...))',
),
),
],
),
],
);
}
}
class _UsageBox extends StatelessWidget {
final IconData icon;
final String title;
final String code;
const _UsageBox({required this.icon, required this.title, required this.code});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(6),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(icon, size: 14, color: Colors.blue),
const SizedBox(width: 4),
Text(title,
style: const TextStyle(
fontSize: 11, fontWeight: FontWeight.bold)),
],
),
const SizedBox(height: 4),
Text(code,
style: const TextStyle(fontSize: 9, fontFamily: 'monospace')),
],
),
);
}
}
// ────────────────────────────────────────────────────────────────
// ⑩ 知识点 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(
'📌 Center 完整总结\n\n'
'1. 本质:Align(alignment: center) 的简写\n'
'2. 核心逻辑:shrinkWrap = factor != null || max 约束为∞\n'
' → 有 factor → shrink-wrap(尺寸 = child × factor)\n'
' → 无 factor → expand(尺寸 = 约束 max)\n'
'3. widthFactor/heightFactor 生效前提:宽松约束\n'
'4. Row/Column 中:crossAxisAlignment 决定约束类型\n'
' - center / start / end → 宽松约束\n'
' - stretch → tight 约束\n'
'5. Center 没有 color/padding/margin → 需要装饰用 Container 包裹\n'
'6. 选择建议:\n'
' - 纯粹居中 → Center\n'
' - 其他对齐 → Align\n'
' - 居中+装饰 → Container(alignment: center)',
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,
],
),
);
}
}
Align
Center就是Align设置了居中。同样可以设置factor。
less
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: Align(
alignment: Alignment.bottomRight,
child: Container(
width: 100,
height: 100,
color: Colors.green,
),
),
)
);
}
}