Widget 基础
Widget 是 Flutter 应用的构建块。理解 Widget 的概念和工作原理是掌握 Flutter 的关键。
核心概念:在 Flutter 中,"一切皆 Widget"。从按钮到布局,从文本到动画,所有内容都是 Widget。
1. 什么是 Widget?
Widget 是 Flutter 中的核心概念,它们是:
- UI 元素:按钮、文本、图片等
- 布局组件:行、列、堆栈等
- 功能组件:手势、主题、导航等
- 不可变的:一旦创建就不能修改
2. Widget 类型
StatelessWidget(无状态 Widget)
无状态 Widget 是不可变的,一旦创建就不会改变。适用于静态内容。
import 'package:flutter/material.dart';
class MyTextWidget extends StatelessWidget {
final String text;
const MyTextWidget({super.key, required this.text});
@override
Widget build(BuildContext context) {
return Text(
text,
style: const TextStyle(
fontSize: 18,
color: Colors.blue,
),
);
}
}
// 使用
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: MyTextWidget(text: 'Hello, Widget!'),
),
),
);
}
}
StatefulWidget(有状态 Widget)
有状态 Widget 可以在其生命周期内改变状态。适用于需要动态更新的内容。
import 'package:flutter/material.dart';
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'计数: $_count',
style: const TextStyle(fontSize: 24),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _increment,
child: const Text('增加'),
),
],
);
}
}
3. 常用基础 Widget
Container
Container 是一个多功能组合 Widget,可以包含子 Widget、设置内边距、外边距、装饰等。
Container(
width: 200,
height: 200,
padding: const EdgeInsets.all(20),
margin: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 10,
spreadRadius: 5,
),
],
),
child: const Center(
child: Text(
'Container',
style: TextStyle(color: Colors.white, fontSize: 18),
),
),
)
Text
Text Widget 用于显示文本内容。
const Text(
'Hello, Flutter!',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blue,
letterSpacing: 1.5,
wordSpacing: 2.0,
),
textAlign: TextAlign.center,
)
Image
Image Widget 用于显示图片。
// 从网络加载图片
Image.network(
'https://example.com/image.jpg',
width: 200,
height: 200,
fit: BoxFit.cover,
)
// 从资源加载图片
Image.asset('assets/images/logo.png')
// 从文件加载
Image.file(File('/path/to/image.jpg'))
Icon
Icon Widget 显示图标。
const Icon(
Icons.favorite,
size: 48,
color: Colors.red,
)
// 自定义颜色和大小
Icon(
Icons.star,
color: Colors.yellow,
size: 32,
)
4. 布局 Widget
Row(行布局)
Row Widget 在水平方向上排列子 Widget。
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Icon(Icons.star, color: Colors.yellow),
Icon(Icons.star, color: Colors.yellow),
Icon(Icons.star, color: Colors.yellow),
Icon(Icons.star_half, color: Colors.yellow),
Icon(Icons.star_border, color: Colors.yellow),
],
)
Column(列布局)
Column Widget 在垂直方向上排列子 Widget。
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'用户信息',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
const TextField(
decoration: InputDecoration(
hintText: '用户名',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 10),
const TextField(
decoration: InputDecoration(
hintText: '密码',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {},
child: const Text('登录'),
),
],
)
Stack(堆叠布局)
Stack Widget 允许子 Widget 重叠。
Stack(
alignment: Alignment.center,
children: [
// 背景图片
Container(
width: 200,
height: 200,
decoration: const BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
// 文字在中心
const Text(
'中心',
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
// 右下角图标
Positioned(
bottom: 10,
right: 10,
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: const Icon(Icons.edit, size: 20),
),
),
],
)
5. 交互 Widget
按钮
Flutter 提供了多种按钮类型。
// ElevatedButton(Material 风格凸起按钮)
ElevatedButton(
onPressed: () {
print('ElevatedButton 被点击');
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 15,
),
),
child: const Text('点击我'),
)
// TextButton(文本按钮)
TextButton(
onPressed: () {},
child: const Text('文本按钮'),
)
// OutlinedButton(轮廓按钮)
OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.blue),
),
child: const Text('轮廓按钮'),
)
// IconButton(图标按钮)
IconButton(
onPressed: () {},
icon: const Icon(Icons.favorite),
iconSize: 32,
color: Colors.red,
)
TextField
TextField Widget 用于接收用户输入。
TextField(
decoration: InputDecoration(
labelText: '用户名',
hintText: '请输入用户名',
prefixIcon: const Icon(Icons.person),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
filled: true,
fillColor: Colors.grey[100],
),
onChanged: (value) {
print('输入: $value');
},
onSubmitted: (value) {
print('提交: $value');
},
)
6. Widget 树
Flutter 应用的 UI 由 Widget 树组成。理解 Widget 树对于构建复杂 UI 至关重要。
7. 生命周期
理解 StatefulWidget 的生命周期对于管理状态和资源很重要。
class LifecycleWidget extends StatefulWidget {
const LifecycleWidget({super.key});
@override
State createState() => _LifecycleWidgetState();
}
class _LifecycleWidgetState extends State {
@override
void initState() {
super.initState();
print('initState: Widget 初始化');
// 初始化操作,如加载数据
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('didChangeDependencies: 依赖变化');
// 依赖项(如 Theme)变化时调用
}
@override
void didUpdateWidget(LifecycleWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print('didUpdateWidget: Widget 更新');
// 父 Widget 重建时调用
}
@override
void setState(VoidCallback fn) {
super.setState(fn);
print('setState: 状态更新');
}
@override
void deactivate() {
super.deactivate();
print('deactivate: Widget 即将被移除');
}
@override
void dispose() {
super.dispose();
print('dispose: Widget 被销毁');
// 清理资源,如关闭流、取消订阅
}
@override
Widget build(BuildContext context) {
print('build: Widget 构建');
return Container(
child: const Text('生命周期示例'),
);
}
}
8. 最佳实践
- 合理拆分 Widget:将大 Widget 拆分成小的、可重用的组件
- 使用 const:对于不变的 Widget 使用 const 构造函数
- 避免过度嵌套:使用私有方法或独立 Widget 减少嵌套层级
- 选择合适的状态管理:根据应用复杂度选择 setState、Provider、Bloc 等
- 使用 Keys:在需要控制 Widget 身份时使用 Key
提示:使用 Flutter DevTools 可以帮助可视化 Widget 树和调试 Widget 性能。
注意:频繁调用 setState 可能会影响性能。在构建复杂 Widget 树时,考虑将可变部分提取为独立的 Widget。