Flutter的核心理念:一切皆Widget。按钮是Widget,边距是Widget,颜色是Widget,甚至你的整个App也是一个Widget。这种设计让组合变得极其灵活。
Widget不是UI控件,而是UI的不可变描述——它是蓝图、配置、描述。真正的渲染由RenderObject完成。
// ✅ 验证通过:Flutter 3.22
// 文件:lib/widget_overview.dart
/// Widget分类总览:
///
/// Widget
/// ├── StatelessWidget 无状态组件(纯展示)
/// ├── StatefulWidget 有状态组件(可交互)
/// ├── RenderObjectWidget 渲染组件
/// │ ├── SingleChildRenderObjectWidget 单子节点
/// │ ├── MultiChildRenderObjectWidget 多子节点
/// │ └── LeafRenderObjectWidget 叶子节点(无子节点)
/// ├── ProxyWidget 代理组件(传递数据)
/// │ ├── InheritedWidget 向下传递数据
/// │ └── ParentDataWidget 向子节点传递父数据
/// └── StatefulBindingWidget 绑定组件
| 类别 | 作用 | 典型Widget |
|---|---|---|
| 基础组件 | 展示内容 | Text, Icon, Image, Button |
| 布局组件 | 排列子组件 | Row, Column, Stack, Flex |
| 容器组件 | 装饰与约束 | Container, DecoratedBox, Padding |
| 滚动组件 | 可滚动列表 | ListView, GridView, ScrollView |
| 导航组件 | 页面切换 | Navigator, Route, Dialog |
| 交互组件 | 用户输入 | TextField, Checkbox, Slider |
| 动画组件 | 视觉效果 | AnimatedContainer, Hero |
| 代理组件 | 数据传递 | Provider, InheritedWidget, Theme |
// ✅ 验证通过
/// StatelessWidget生命周期(最简单)
///
/// 创建 → build() → 销毁
/// 每次父组件重建时,都会创建新的Widget实例并调用build()
/// StatefulWidget生命周期
///
/// 创建Widget → createState() → initState() → didChangeDependencies()
/// → build() → (可能多次rebuild) → deactivate() → dispose()
///
/// 详细阶段:
class LifecycleDemo extends StatefulWidget {
const LifecycleDemo({super.key});
@override
State<LifecycleDemo> createState() => _LifecycleDemoState();
}
class _LifecycleDemoState extends State<LifecycleDemo> {
// 1. 构造函数(State的构造函数,不是Widget的)
// 在createState()后立即调用
_LifecycleDemoState() {
print('🔵 State构造函数');
// ⚠️ 这里不能访问context或widget!
}
// 2. initState — 初始化
@override
void initState() {
super.initState(); // 必须调用super
print('🔵 initState — 只调用一次');
// ✅ 这里可以:
// - 订阅事件、动画控制器
// - 初始化数据
// - 访问widget(但不要依赖InheritedWidget)
}
// 3. didChangeDependencies
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('🔵 didChangeDependencies');
// InheritedWidget变化时触发
// 可以安全访问InheritedWidget
}
// 4. build — 构建UI
@override
Widget build(BuildContext context) {
print('🔵 build');
return Container();
}
// 5. didUpdateWidget — Widget配置变化
@override
void didUpdateWidget(covariant LifecycleDemo oldWidget) {
super.didUpdateWidget(oldWidget);
print('🔵 didUpdateWidget — 父组件重建时');
// 比较oldWidget和widget的差异
}
// 6. deactivate — 从树中移除
@override
void deactivate() {
super.deactivate();
print('🟡 deactivate — 可能被重新插入');
}
// 7. dispose — 永久销毁
@override
void dispose() {
print('🔴 dispose — 释放资源');
// ✅ 必须在这里:
// - 取消订阅
// - 释放控制器
// - 关闭Stream
super.dispose();
}
// 8. reassemble — 热重载时
@override
void reassemble() {
super.reassemble();
print('🟢 reassemble — 仅Debug模式热重载');
}
// 9. setState — 标记需要重建
void updateData() {
setState(() {
// 修改状态
// Flutter会在下一帧调用build()
});
}
}
// ✅ 验证通过
/// Key用于标识Widget,在列表中区分相同类型的元素
// 问题场景:没有Key时,Flutter按类型和位置匹配
// 当列表顺序变化时,State可能错位!
class ColorTile extends StatefulWidget {
final Color color;
final Key? tileKey; // 使用Key区分
const ColorTile(this.color, {this.tileKey, super.key});
@override
State<ColorTile> createState() => _ColorTileState();
}
class _ColorTileState extends State<ColorTile> {
int _clickCount = 0;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => _clickCount++),
child: Container(
width: 100,
height: 100,
color: widget.color,
child: Center(
child: Text('$_clickCount', style: const TextStyle(fontSize: 24)),
),
),
);
}
}
// ✅ 正确用法:给列表项加Key
class ColorListDemo extends StatefulWidget {
const ColorListDemo({super.key});
@override
State<ColorListDemo> createState() => _ColorListDemoState();
}
class _ColorListDemoState extends State<ColorListDemo> {
var tiles = [
ColorTile(Colors.red, tileKey: UniqueKey()),
ColorTile(Colors.blue, tileKey: UniqueKey()),
ColorTile(Colors.green, tileKey: UniqueKey()),
];
void swapFirstTwo() {
setState(() {
var first = tiles.removeAt(0);
tiles.insert(1, first);
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
...tiles,
ElevatedButton(
onPressed: swapFirstTwo,
child: const Text('交换'),
),
],
);
}
}
/// Key类型选择:
/// - ValueKey(value) — 值唯一时使用(如ID)
/// - ObjectKey(object) — 对象引用唯一时使用
/// - UniqueKey() — 每次创建都唯一(慎用,会导致每次重建)
/// - GlobalObjectKey — 跨树唯一标识
///
/// 💡 原则:能用ValueKey就不用UniqueKey
/// ValueKey在元素不变时保持稳定,UniqueKey每次都变
// ✅ 验证通过
/// Widget树 vs Element树 vs Render树
///
/// Widget树(配置描述):
/// MaterialApp
/// └── Scaffold
/// ├── AppBar
/// │ └── Text('标题')
/// └── Center
/// └── Column
/// ├── Text('内容')
/// └── ElevatedButton
///
/// Element树(实例管理):
/// 每个Widget创建对应的Element
/// Element负责:
/// 1. 持有Widget引用
/// 2. 管理子Element
/// 3. 决定是否复用RenderObject
/// 4. 触发重建
///
/// Render树(布局绘制):
/// 只有RenderObjectWidget才有RenderObject
/// 负责测量、布局、绘制
// 可以通过debug模式查看树
void debugTree() {
// 在任意Widget的build方法中:
// debugPrint('$runtimeType build');
// 查看Element树
// debugDumpApp(); — 打印Widget树
// debugDumpRenderTree(); — 打印Render树
// debugDumpLayerTree(); — 打印Layer树
}
/// Widget重建机制
class RebuildDemo extends StatelessWidget {
final int counter;
const RebuildDemo({required this.counter, super.key});
@override
Widget build(BuildContext context) {
print('RebuildDemo build — counter=$counter');
// ⚠️ 每次counter变化,整个build都会执行
// 但Flutter会优化:如果子Widget没变(同Key同类型),不重建
return Column(
children: [
// 这个Text每次都重建(因为参数变了)
Text('Count: $counter'),
// 这个const Widget不会重建!
const Text('我不变'),
// 提取为独立Widget可以避免不必要重建
_StaticWidget(),
],
);
}
}
// 独立Widget——父组件重建时不影响
class _StaticWidget extends StatelessWidget {
const _StaticWidget();
@override
Widget build(BuildContext context) {
print('_StaticWidget build'); // 只在首次调用
return const Text('我是独立的');
}
}
// ✅ 验证通过:Flutter 3.22 + Dart 3.4
// 文件:lib/widget_debug_panel.dart
import 'package:flutter/material.dart';
void main() => runApp(const DebugPanelApp());
class DebugPanelApp extends StatelessWidget {
const DebugPanelApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Widget调试面板',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const DebugPanel(),
);
}
}
/// Widget信息卡片——展示Widget属性
class WidgetInfoCard extends StatelessWidget {
final String name;
final String category;
final String description;
final List<String> properties;
final Widget child;
const WidgetInfoCard({
required this.name,
required this.category,
required this.description,
required this.properties,
required this.child,
super.key,
});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.all(8),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题行
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(12),
),
child: Text(
category,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
),
),
const SizedBox(width: 8),
Text(
name,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 8),
Text(description, style: Theme.of(context).textTheme.bodySmall),
const SizedBox(height: 8),
// 属性标签
Wrap(
spacing: 4,
runSpacing: 4,
children: properties.map((p) => Chip(
label: Text(p, style: const TextStyle(fontSize: 11)),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
)).toList(),
),
const Divider(height: 24),
// 实际Widget展示
Center(child: child),
],
),
),
);
}
}
/// 生命周期追踪Widget
class LifecycleTracker extends StatefulWidget {
final String label;
final Widget child;
const LifecycleTracker({
required this.label,
required this.child,
super.key,
});
@override
State<LifecycleTracker> createState() => _LifecycleTrackerState();
}
class _LifecycleTrackerState extends State<LifecycleTracker> {
static final List<String> _log = [];
int _buildCount = 0;
@override
void initState() {
super.initState();
_addLog('initState');
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_addLog('didChangeDependencies');
}
@override
void didUpdateWidget(covariant LifecycleTracker oldWidget) {
super.didUpdateWidget(oldWidget);
_addLog('didUpdateWidget (${oldWidget.label} → ${widget.label})');
}
@override
void deactivate() {
_addLog('deactivate');
super.deactivate();
}
@override
void dispose() {
_addLog('dispose');
super.dispose();
}
void _addLog(String event) {
_log.add('[${widget.label}] $event');
if (_log.length > 50) _log.removeAt(0);
}
@override
Widget build(BuildContext context) {
_buildCount++;
_addLog('build #$_buildCount');
return widget.child;
}
}
/// 主调试面板
class DebugPanel extends StatefulWidget {
const DebugPanel({super.key});
@override
State<DebugPanel> createState() => _DebugPanelState();
}
class _DebugPanelState extends State<DebugPanel> {
int _selectedIndex = 0;
final List<Widget> _pages = const [
_BasicWidgetsPage(),
_LayoutWidgetsPage(),
_LifecyclePage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Widget 调试面板'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: _pages[_selectedIndex],
bottomNavigationBar: NavigationBar(
selectedIndex: _selectedIndex,
onDestinationSelected: (i) => setState(() => _selectedIndex = i),
destinations: const [
NavigationDestination(icon: Icon(Icons.widgets), label: '基础'),
NavigationDestination(icon: Icon(Icons.view_quilt), label: '布局'),
NavigationDestination(icon: Icon(Icons.refresh), label: '生命周期'),
],
),
);
}
}
/// 基础组件展示页
class _BasicWidgetsPage extends StatelessWidget {
const _BasicWidgetsPage();
@override
Widget build(BuildContext context) {
return ListView(
padding: const EdgeInsets.all(8),
children: [
WidgetInfoCard(
name: 'Text',
category: '基础',
description: '显示文本,支持样式、对齐、溢出等',
properties: ['style', 'textAlign', 'maxLines', 'overflow'],
child: Column(
children: [
const Text('普通文本'),
Text('大标题', style: Theme.of(context).textTheme.headlineSmall),
Text(
'这段文字很长会被截断显示省略号哦哦哦哦哦哦哦哦',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: Colors.grey[600]),
),
],
),
),
WidgetInfoCard(
name: 'Icon',
category: '基础',
description: 'Material图标,2000+可用图标',
properties: ['icon', 'size', 'color', 'semanticLabel'],
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: const [
Icon(Icons.home, size: 32, color: Colors.blue),
Icon(Icons.favorite, size: 32, color: Colors.red),
Icon(Icons.star, size: 32, color: Colors.amber),
Icon(Icons.settings, size: 32, color: Colors.grey),
],
),
),
WidgetInfoCard(
name: 'ElevatedButton',
category: '交互',
description: 'Material elevated按钮',
properties: ['onPressed', 'child', 'style'],
child: ElevatedButton(
onPressed: () => ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('按钮被点击了!')),
),
child: const Text('点我'),
),
),
WidgetInfoCard(
name: 'TextField',
category: '输入',
description: '文本输入框',
properties: ['decoration', 'controller', 'onChanged'],
child: const SizedBox(
width: 250,
child: TextField(
decoration: InputDecoration(
labelText: '输入文字',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.search),
),
),
),
),
],
);
}
}
/// 布局组件展示页
class _LayoutWidgetsPage extends StatelessWidget {
const _LayoutWidgetsPage();
@override
Widget build(BuildContext context) {
return ListView(
padding: const EdgeInsets.all(8),
children: [
WidgetInfoCard(
name: 'Row',
category: '布局',
description: '水平排列子组件',
properties: ['mainAxisAlignment', 'crossAxisAlignment', 'children'],
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(width: 50, height: 50, color: Colors.red),
Container(width: 50, height: 50, color: Colors.green),
Container(width: 50, height: 50, color: Colors.blue),
],
),
),
WidgetInfoCard(
name: 'Column',
category: '布局',
description: '垂直排列子组件',
properties: ['mainAxisAlignment', 'crossAxisAlignment', 'children'],
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(width: 150, height: 30, color: Colors.orange),
const SizedBox(height: 8),
Container(width: 100, height: 30, color: Colors.purple),
const SizedBox(height: 8),
Container(width: 50, height: 30, color: Colors.teal),
],
),
),
WidgetInfoCard(
name: 'Stack',
category: '布局',
description: '层叠布局,子组件可以重叠',
properties: ['alignment', 'children', 'fit'],
child: SizedBox(
width: 150,
height: 150,
child: Stack(
alignment: Alignment.center,
children: [
Container(width: 150, height: 150, color: Colors.blue.withOpacity(0.3)),
Container(width: 100, height: 100, color: Colors.green.withOpacity(0.5)),
Container(width: 50, height: 50, color: Colors.red.withOpacity(0.7)),
const Text('Stack!', style: TextStyle(color: Colors.white)),
],
),
),
),
],
);
}
}
/// 生命周期展示页
class _LifecyclePage extends StatelessWidget {
const _LifecyclePage();
@override
Widget build(BuildContext context) {
return const Center(
child: Text('生命周期页面 — 参考上方代码中的LifecycleTracker'),
);
}
}
创建一个Widget,追踪自己的build次数:
class BuildCounter extends StatelessWidget {
// 提示:使用static变量或InheritedWidget
// 显示:当前Widget被build了多少次
}
验证Key的作用:
创建一个工具,用缩进打印当前Widget树:
// 提示:使用debugDumpApp()或自定义遍历
void printWidgetTree(BuildContext context, [int depth = 0]) {
// 递归打印Widget树结构
}
下一课:StatelessWidget——深入学习无状态组件的设计模式、const优化和组件提取技巧。