Flutter Widget 基础
Widget 是 Flutter UI 的基本构建块。
在 Flutter 中,一切皆为 Widget,从简单的文本到复杂的页面布局都是由 Widget 组合而成的。
什么是 Widget?
Widget 是描述 UI 外观和行为的 Dart 类。每个 Flutter 应用本质上就是由 Widget 组成的树形结构,称为 Widget 树。
实例:最简单的 Widget 示例
import 'package:flutter/material.dart';
// 定义一个简单的 Widget
class HelloWidget extends StatelessWidget {
const HelloWidget({super.key});
@override
Widget build(BuildContext context) {
// 返回一个 Text Widget,显示 "Hello World"
return const Text('Hello World');
}
}
// 定义一个简单的 Widget
class HelloWidget extends StatelessWidget {
const HelloWidget({super.key});
@override
Widget build(BuildContext context) {
// 返回一个 Text Widget,显示 "Hello World"
return const Text('Hello World');
}
}
StatelessWidget - 无状态组件
StatelessWidget 是不可变的 Widget,一旦创建,其属性就不能更改。适用于显示静态内容的场景,如文本、图片、图标等。
基本结构
实例:StatelessWidget 示例
import 'package:flutter/material.dart';
// 定义无状态组件
class MyTextWidget extends StatelessWidget {
// 构造函数参数(不可变属性)
final String text;
final Color textColor;
final double fontSize;
// 使用 const 构造函数
const MyTextWidget({
super.key,
required this.text,
this.textColor = Colors.black,
this.fontSize = 16.0,
});
@override
Widget build(BuildContext context) {
// build 方法返回另一个 Widget
return Text(
text,
style: TextStyle(
color: textColor,
fontSize: fontSize,
),
);
}
}
// 使用示例
// MyTextWidget(text: 'Hello', textColor: Colors.red)
// 定义无状态组件
class MyTextWidget extends StatelessWidget {
// 构造函数参数(不可变属性)
final String text;
final Color textColor;
final double fontSize;
// 使用 const 构造函数
const MyTextWidget({
super.key,
required this.text,
this.textColor = Colors.black,
this.fontSize = 16.0,
});
@override
Widget build(BuildContext context) {
// build 方法返回另一个 Widget
return Text(
text,
style: TextStyle(
color: textColor,
fontSize: fontSize,
),
);
}
}
// 使用示例
// MyTextWidget(text: 'Hello', textColor: Colors.red)
适用场景
- 静态文本展示
- 图片显示
- 图标展示
- 不随时间变化的布局
StatefulWidget - 有状态组件
StatefulWidget 是可变的 Widget,可以随着内部数据变化而更新 UI。适用于需要响应用户交互或数据变化的场景。
基本结构
实例:StatefulWidget 示例
import 'package:flutter/material.dart';
// 定义有状态组件
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
// State 类持有可变状态
class _CounterWidgetState extends State<CounterWidget> {
// 计数器状态
int _count = 0;
// 增加计数器的方法
void _increment() {
// 调用 setState 通知框架状态已更改
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 显示当前计数
Text(
'计数器: $_count',
style: const TextStyle(fontSize: 24),
),
const SizedBox(height: 16),
// 增加按钮
ElevatedButton(
onPressed: _increment,
child: const Text('增加'),
),
],
);
}
}
// 定义有状态组件
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
// State 类持有可变状态
class _CounterWidgetState extends State<CounterWidget> {
// 计数器状态
int _count = 0;
// 增加计数器的方法
void _increment() {
// 调用 setState 通知框架状态已更改
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 显示当前计数
Text(
'计数器: $_count',
style: const TextStyle(fontSize: 24),
),
const SizedBox(height: 16),
// 增加按钮
ElevatedButton(
onPressed: _increment,
child: const Text('增加'),
),
],
);
}
}
setState 方法
在 StatefulWidget 中,如果状态发生变化,必须调用 setState 方法来通知 Flutter 重新构建 UI。
如果修改了状态但没有调用 setState,UI 不会更新。这是因为 Flutter 通过 setState 来判断是否需要重新构建 Widget。
适用场景
- 计数器、计时器等动态数据
- 表单输入
- 列表项的增删改
- 用户交互响应
build 方法
每个 Widget 都必须实现 build 方法,该方法返回 Widget 树的一部分。
基本规则
| 规则 | 说明 |
|---|---|
| 返回值 | 必须返回一个 Widget,不能返回 null |
| 参数 | 接收 BuildContext 参数,表示在 Widget 树中的位置 |
| 调用时机 | 首次构建、父 Widget 重建、状态变化时 |
实例:build 方法示例
@override
Widget build(BuildContext context) {
// BuildContext 提供了访问主题、主题等信息的能力
// 例如获取当前主题颜色
final theme = Theme.of(context);
// 返回一个 Widget
return Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: theme.colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Hello Flutter',
style: TextStyle(
color: theme.colorScheme.onPrimaryContainer,
fontSize: 20,
),
),
);
}
Widget build(BuildContext context) {
// BuildContext 提供了访问主题、主题等信息的能力
// 例如获取当前主题颜色
final theme = Theme.of(context);
// 返回一个 Widget
return Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: theme.colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Hello Flutter',
style: TextStyle(
color: theme.colorScheme.onPrimaryContainer,
fontSize: 20,
),
),
);
}
常用基础 Widget
Text - 文本显示
实例:Text Widget
// 基本文本
const Text('Hello World')
// 带样式的文本
Text(
'Hello Flutter',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
)
// 带有可选样式的文本
Text.rich(
TextSpan(
text: 'Hello ',
children: [
TextSpan(
text: 'Flutter',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
)
const Text('Hello World')
// 带样式的文本
Text(
'Hello Flutter',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
)
// 带有可选样式的文本
Text.rich(
TextSpan(
text: 'Hello ',
children: [
TextSpan(
text: 'Flutter',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
)
Icon - 图标
实例:Icon Widget
// 使用 Material Icons
const Icon(Icons.home) // 主页图标
const Icon(Icons.star, color: Colors.yellow) // 带颜色的星星
const Icon(Icons.settings, size: 48) // 大小为 48 的设置图标
const Icon(Icons.home) // 主页图标
const Icon(Icons.star, color: Colors.yellow) // 带颜色的星星
const Icon(Icons.settings, size: 48) // 大小为 48 的设置图标
Image - 图片
实例:Image Widget
// 从网络加载图片
Image.network('https://example.com/image.png')
// 从本地资源加载图片
Image.asset('assets/images/logo.png')
// 从文件加载图片
Image.file(File('path/to/image.png'))
// 带参数的图片
Image.network(
'https://example.com/image.png',
width: 200,
height: 200,
fit: BoxFit.cover,
)
Image.network('https://example.com/image.png')
// 从本地资源加载图片
Image.asset('assets/images/logo.png')
// 从文件加载图片
Image.file(File('path/to/image.png'))
// 带参数的图片
Image.network(
'https://example.com/image.png',
width: 200,
height: 200,
fit: BoxFit.cover,
)
Button - 按钮
实例:按钮 Widget
// 文字按钮
TextButton(
onPressed: () { /* 处理点击 */ },
child: const Text('文字按钮'),
)
// 悬浮按钮
FloatingActionButton(
onPressed: () { /* 处理点击 */ },
child: const Icon(Icons.add),
)
// ElevatedButton - 带阴影的按钮
ElevatedButton(
onPressed: () { /* 处理点击 */ },
child: const Text('提交'),
)
// 带图标的按钮
ElevatedButton.icon(
onPressed: () { /* 处理点击 */ },
icon: const Icon(Icons.save),
label: const Text('保存'),
)
TextButton(
onPressed: () { /* 处理点击 */ },
child: const Text('文字按钮'),
)
// 悬浮按钮
FloatingActionButton(
onPressed: () { /* 处理点击 */ },
child: const Icon(Icons.add),
)
// ElevatedButton - 带阴影的按钮
ElevatedButton(
onPressed: () { /* 处理点击 */ },
child: const Text('提交'),
)
// 带图标的按钮
ElevatedButton.icon(
onPressed: () { /* 处理点击 */ },
icon: const Icon(Icons.save),
label: const Text('保存'),
)
Widget 组合
Flutter 的强大之处在于可以组合 Widget。通过嵌套,一个简单的 Widget 可以构建出复杂的 UI。
实例:组合 Widget
class MyCard extends StatelessWidget {
const MyCard({super.key});
@override
Widget build(BuildContext context) {
return Card(
// Card 是 Material Design 的卡片组件
elevation: 4, // 阴影深度
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题
const Text(
'卡片标题',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
// 描述文本
const Text(
'这是卡片的描述内容,可以包含多行文字。',
),
const SizedBox(height: 16),
// 按钮行
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {},
child: const Text('取消'),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: () {},
child: const Text('确认'),
),
],
),
],
),
),
);
}
}
const MyCard({super.key});
@override
Widget build(BuildContext context) {
return Card(
// Card 是 Material Design 的卡片组件
elevation: 4, // 阴影深度
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题
const Text(
'卡片标题',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
// 描述文本
const Text(
'这是卡片的描述内容,可以包含多行文字。',
),
const SizedBox(height: 16),
// 按钮行
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {},
child: const Text('取消'),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: () {},
child: const Text('确认'),
),
],
),
],
),
),
);
}
}
