📚 目录
- 什么是组件通信
- [1. 父传子(Parent to Child)](#1. 父传子(Parent to Child))
- [2. 子传父(Child to Parent)](#2. 子传父(Child to Parent))
- [3. 兄弟组件通信](#3. 兄弟组件通信)
- [4. 跨层级通信](#4. 跨层级通信)
- [5. 全局状态管理](#5. 全局状态管理)
- 实战案例
- 最佳实践
什么是组件通信?
在 Flutter 中,组件(Widget)之间经常需要传递数据和事件。组件通信就是解决这个问题的方法。
常见的通信场景
父组件
├── 子组件 A
└── 子组件 B
- 父传子:父组件向子组件传递数据
- 子传父:子组件向父组件传递事件或数据
- 兄弟通信:子组件 A 和子组件 B 之间通信
- 跨层级:祖先组件和后代组件之间通信
1. 父传子(Parent to Child)
父传子是最简单的通信方式,通过构造函数参数传递数据。
基本原理
父组件创建子组件时,通过构造函数传递数据给子组件。
案例1:传递简单数据
dart
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: ParentWidget(),
);
}
}
// 父组件
class ParentWidget extends StatelessWidget {
const ParentWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// 父组件的数据
String userName = '张三';
int userAge = 25;
return Scaffold(
appBar: AppBar(title: const Text('父传子 - 基础示例')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'父组件',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
// 通过构造函数传递数据给子组件
ChildWidget(
name: userName,
age: userAge,
),
],
),
),
);
}
}
// 子组件
class ChildWidget extends StatelessWidget {
// 1. 定义接收的参数
final String name;
final int age;
// 2. 在构造函数中接收参数
const ChildWidget({
Key? key,
required this.name, // required 表示必传
required this.age,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.blue[100],
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: [
const Text(
'子组件',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
// 3. 使用接收到的数据
Text('姓名: $name', style: const TextStyle(fontSize: 18)),
Text('年龄: $age', style: const TextStyle(fontSize: 18)),
],
),
);
}
}
关键点:
- 子组件定义
final属性接收数据 - 构造函数中使用
required标记必传参数 - 父组件创建子组件时传递数据
案例2:传递复杂对象
dart
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: UserListPage(),
);
}
}
// 用户数据模型
class User {
final String name;
final int age;
final String avatar;
User({
required this.name,
required this.age,
required this.avatar,
});
}
// 父组件
class UserListPage extends StatelessWidget {
const UserListPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// 父组件的数据
final List<User> users = [
User(name: '张三', age: 25, avatar: '👨'),
User(name: '李四', age: 30, avatar: '👩'),
User(name: '王五', age: 28, avatar: '👨💼'),
];
return Scaffold(
appBar: AppBar(title: const Text('父传子 - 传递对象')),
body: ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
// 传递整个对象给子组件
return UserCard(user: users[index]);
},
),
);
}
}
// 子组件
class UserCard extends StatelessWidget {
final User user; // 接收 User 对象
const UserCard({
Key? key,
required this.user,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.all(10),
child: ListTile(
leading: Text(
user.avatar,
style: const TextStyle(fontSize: 40),
),
title: Text(user.name),
subtitle: Text('年龄: ${user.age}'),
),
);
}
}
案例3:传递可选参数
dart
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: OptionalParamsPage(),
);
}
}
class OptionalParamsPage extends StatelessWidget {
const OptionalParamsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('父传子 - 可选参数')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 传递所有参数
CustomButton(
text: '完整按钮',
color: Colors.blue,
icon: Icons.check,
),
const SizedBox(height: 20),
// 只传递必需参数
CustomButton(text: '简单按钮'),
],
),
),
);
}
}
class CustomButton extends StatelessWidget {
final String text; // 必需参数
final Color? color; // 可选参数
final IconData? icon; // 可选参数
const CustomButton({
Key? key,
required this.text, // 必需
this.color, // 可选
this.icon, // 可选
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ElevatedButton.icon(
onPressed: () {},
icon: Icon(icon ?? Icons.touch_app), // 使用默认值
label: Text(text),
style: ElevatedButton.styleFrom(
backgroundColor: color ?? Colors.grey, // 使用默认值
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
),
);
}
}
2. 子传父(Child to Parent)
子传父通过**回调函数(Callback)**实现,子组件触发事件时调用父组件传递的函数。
基本原理
- 父组件定义一个函数
- 将函数作为参数传递给子组件
- 子组件在需要时调用这个函数
案例1:基础回调
dart
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: CallbackDemo(),
);
}
}
// 父组件
class CallbackDemo extends StatefulWidget {
const CallbackDemo({Key? key}) : super(key: key);
@override
State<CallbackDemo> createState() => _CallbackDemoState();
}
class _CallbackDemoState extends State<CallbackDemo> {
String message = '等待子组件发送消息...';
// 1. 父组件定义回调函数
void handleChildMessage(String msg) {
setState(() {
message = msg;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('子传父 - 基础回调')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.green[100],
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: [
const Text(
'父组件',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Text(
'收到消息: $message',
style: const TextStyle(fontSize: 18),
),
],
),
),
const SizedBox(height: 40),
// 2. 将回调函数传递给子组件
ChildButton(onPressed: handleChildMessage),
],
),
),
);
}
}
// 子组件
class ChildButton extends StatelessWidget {
// 3. 子组件接收回调函数
final Function(String) onPressed;
const ChildButton({
Key? key,
required this.onPressed,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.blue[100],
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: [
const Text(
'子组件',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () {
// 4. 子组件调用回调函数,传递数据给父组件
onPressed('你好,我是子组件!');
},
child: const Text('发送消息给父组件'),
),
],
),
);
}
}
关键点:
- 父组件定义回调函数
handleChildMessage - 通过构造函数传递给子组件
onPressed: handleChildMessage - 子组件接收函数类型参数
final Function(String) onPressed - 子组件调用
onPressed('数据')
案例2:计数器示例(经典案例)
dart
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: CounterPage(),
);
}
}
// 父组件
class CounterPage extends StatefulWidget {
const CounterPage({Key? key}) : super(key: key);
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int count = 0;
// 增加计数
void increment() {
setState(() {
count++;
});
}
// 减少计数
void decrement() {
setState(() {
count--;
});
}
// 重置计数
void reset() {
setState(() {
count = 0;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('子传父 - 计数器')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(30),
decoration: BoxDecoration(
color: Colors.blue[100],
borderRadius: BorderRadius.circular(15),
),
child: Column(
children: [
const Text(
'父组件',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
Text(
'当前计数: $count',
style: const TextStyle(fontSize: 48, fontWeight: FontWeight.bold),
),
],
),
),
const SizedBox(height: 40),
// 传递三个回调函数给子组件
CounterButtons(
onIncrement: increment,
onDecrement: decrement,
onReset: reset,
),
],
),
),
);
}
}
// 子组件
class CounterButtons extends StatelessWidget {
final VoidCallback onIncrement; // 无参数的回调
final VoidCallback onDecrement;
final VoidCallback onReset;
const CounterButtons({
Key? key,
required this.onIncrement,
required this.onDecrement,
required this.onReset,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.green[100],
borderRadius: BorderRadius.circular(15),
),
child: Column(
children: [
const Text(
'子组件',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 15),
Row(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
onPressed: onDecrement, // 调用父组件的函数
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
child: const Icon(Icons.remove),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: onReset,
style: ElevatedButton.styleFrom(backgroundColor: Colors.orange),
child: const Icon(Icons.refresh),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: onIncrement,
style: ElevatedButton.styleFrom(backgroundColor: Colors.green),
child: const Icon(Icons.add),
),
],
),
],
),
);
}
}
VoidCallback 说明:
VoidCallback是 Flutter 内置的类型,表示无参数无返回值的函数- 等同于
void Function()
案例3:表单提交(传递多个参数)
dart
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: FormPage(),
);
}
}
// 父组件
class FormPage extends StatefulWidget {
const FormPage({Key? key}) : super(key: key);
@override
State<FormPage> createState() => _FormPageState();
}
class _FormPageState extends State<FormPage> {
String submittedData = '暂无提交数据';
// 处理表单提交
void handleSubmit(String name, String email) {
setState(() {
submittedData = '姓名: $name\n邮箱: $email';
});
// 显示提示
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('表单提交成功!')),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('子传父 - 表单提交')),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.blue[100],
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: [
const Text(
'父组件 - 接收数据',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Text(
submittedData,
style: const TextStyle(fontSize: 16),
),
],
),
),
const SizedBox(height: 30),
// 传递回调函数给表单组件
UserForm(onSubmit: handleSubmit),
],
),
),
);
}
}
// 子组件 - 表单
class UserForm extends StatefulWidget {
final Function(String name, String email) onSubmit;
const UserForm({
Key? key,
required this.onSubmit,
}) : super(key: key);
@override
State<UserForm> createState() => _UserFormState();
}
class _UserFormState extends State<UserForm> {
final TextEditingController nameController = TextEditingController();
final TextEditingController emailController = TextEditingController();
@override
void dispose() {
nameController.dispose();
emailController.dispose();
super.dispose();
}
void submit() {
String name = nameController.text;
String email = emailController.text;
if (name.isEmpty || email.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('请填写完整信息')),
);
return;
}
// 调用父组件的回调函数,传递多个参数
widget.onSubmit(name, email);
// 清空表单
nameController.clear();
emailController.clear();
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.green[100],
borderRadius: BorderRadius.circular(10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'子组件 - 表单',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 15),
TextField(
controller: nameController,
decoration: const InputDecoration(
labelText: '姓名',
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.white,
),
),
const SizedBox(height: 15),
TextField(
controller: emailController,
decoration: const InputDecoration(
labelText: '邮箱',
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.white,
),
),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: submit,
child: const Text('提交'),
),
),
],
),
);
}
}
3. 兄弟组件通信
兄弟组件之间不能直接通信,需要通过父组件作为中介。
原理
父组件(管理状态)
├── 子组件 A(触发事件)
└── 子组件 B(接收数据)
- 子组件 A 通过回调通知父组件
- 父组件更新状态
- 子组件 B 通过 props 接收新数据
案例:兄弟组件通信
dart
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: SiblingCommunicationPage(),
);
}
}
// 父组件 - 管理状态
class SiblingCommunicationPage extends StatefulWidget {
const SiblingCommunicationPage({Key? key}) : super(key: key);
@override
State<SiblingCommunicationPage> createState() => _SiblingCommunicationPageState();
}
class _SiblingCommunicationPageState extends State<SiblingCommunicationPage> {
String message = '暂无消息';
Color selectedColor = Colors.blue;
// 接收子组件 A 的消息
void updateMessage(String msg) {
setState(() {
message = msg;
});
}
// 接收子组件 A 的颜色选择
void updateColor(Color color) {
setState(() {
selectedColor = color;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('兄弟组件通信')),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(10),
),
child: const Text(
'父组件(中介)',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 30),
// 子组件 A - 发送消息
SenderWidget(
onSendMessage: updateMessage,
onSelectColor: updateColor,
),
const SizedBox(height: 30),
// 子组件 B - 接收消息
ReceiverWidget(
message: message,
color: selectedColor,
),
],
),
),
);
}
}
// 子组件 A - 发送者
class SenderWidget extends StatelessWidget {
final Function(String) onSendMessage;
final Function(Color) onSelectColor;
const SenderWidget({
Key? key,
required this.onSendMessage,
required this.onSelectColor,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.blue[100],
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.blue, width: 2),
),
child: Column(
children: [
const Text(
'子组件 A(发送者)',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 15),
Wrap(
spacing: 10,
children: [
ElevatedButton(
onPressed: () => onSendMessage('你好!'),
child: const Text('发送: 你好'),
),
ElevatedButton(
onPressed: () => onSendMessage('Flutter 很棒!'),
child: const Text('发送: Flutter'),
),
],
),
const SizedBox(height: 10),
const Text('选择颜色:', style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_ColorButton(Colors.red, onSelectColor),
_ColorButton(Colors.green, onSelectColor),
_ColorButton(Colors.blue, onSelectColor),
_ColorButton(Colors.orange, onSelectColor),
],
),
],
),
);
}
}
class _ColorButton extends StatelessWidget {
final Color color;
final Function(Color) onTap;
const _ColorButton(this.color, this.onTap);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => onTap(color),
child: Container(
width: 40,
height: 40,
margin: const EdgeInsets.symmetric(horizontal: 5),
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
),
),
);
}
}
// 子组件 B - 接收者
class ReceiverWidget extends StatelessWidget {
final String message;
final Color color;
const ReceiverWidget({
Key? key,
required this.message,
required this.color,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: color.withOpacity(0.2),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: color, width: 2),
),
child: Column(
children: [
const Text(
'子组件 B(接收者)',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 15),
Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
const Text(
'收到的消息:',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Text(
message,
style: TextStyle(fontSize: 20, color: color),
),
],
),
),
],
),
);
}
}
关键点:
- 父组件管理共享状态(message 和 color)
- 子组件 A 通过回调函数通知父组件
- 父组件更新状态后,子组件 B 自动接收新数据
4. 跨层级通信
当组件层级很深时,一层层传递数据很麻烦。Flutter 提供了 InheritedWidget 和更简单的方案。
方案1:使用 InheritedWidget
dart
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: InheritedWidgetDemo(),
);
}
}
// 1. 创建 InheritedWidget
class UserData extends InheritedWidget {
final String userName;
final String userRole;
const UserData({
Key? key,
required this.userName,
required this.userRole,
required Widget child,
}) : super(key: key, child: child);
// 提供静态方法供子组件获取数据
static UserData? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<UserData>();
}
@override
bool updateShouldNotify(UserData oldWidget) {
return userName != oldWidget.userName || userRole != oldWidget.userRole;
}
}
// 2. 在顶层包裹 InheritedWidget
class InheritedWidgetDemo extends StatelessWidget {
const InheritedWidgetDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return UserData(
userName: '张三',
userRole: '管理员',
child: Scaffold(
appBar: AppBar(title: const Text('跨层级通信 - InheritedWidget')),
body: const Center(
child: LevelOne(),
),
),
);
}
}
// 第一层组件
class LevelOne extends StatelessWidget {
const LevelOne({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.blue[100],
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: const [
Text('第一层组件', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
SizedBox(height: 20),
LevelTwo(),
],
),
);
}
}
// 第二层组件
class LevelTwo extends StatelessWidget {
const LevelTwo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.green[100],
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: const [
Text('第二层组件', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
SizedBox(height: 20),
LevelThree(),
],
),
);
}
}
// 第三层组件 - 直接获取顶层数据
class LevelThree extends StatelessWidget {
const LevelThree({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// 3. 在任意层级获取数据
final userData = UserData.of(context);
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.orange[100],
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'第三层组件',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Text('用户名: ${userData?.userName}'),
Text('角色: ${userData?.userRole}'),
],
),
);
}
}
方案2:使用 Provider(推荐)
Provider 是 Flutter 官方推荐的状态管理方案,更简单易用。
首先在 pubspec.yaml 添加依赖:
yaml
dependencies:
flutter:
sdk: flutter
provider: ^6.0.0
dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// 1. 在顶层提供数据
return ChangeNotifierProvider(
create: (context) => CounterModel(),
child: const MaterialApp(
home: ProviderDemo(),
),
);
}
}
// 2. 创建数据模型
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 通知所有监听者
}
void decrement() {
_count--;
notifyListeners();
}
}
class ProviderDemo extends StatelessWidget {
const ProviderDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('跨层级通信 - Provider')),
body: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DisplayWidget(),
SizedBox(height: 30),
ButtonWidget(),
],
),
),
);
}
}
// 显示组件 - 监听数据变化
class DisplayWidget extends StatelessWidget {
const DisplayWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// 3. 监听数据变化
final counter = context.watch<CounterModel>();
return Container(
padding: const EdgeInsets.all(30),
decoration: BoxDecoration(
color: Colors.blue[100],
borderRadius: BorderRadius.circular(15),
),
child: Column(
children: [
const Text(
'显示组件',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 15),
Text(
'${counter.count}',
style: const TextStyle(fontSize: 48, fontWeight: FontWeight.bold),
),
],
),
);
}
}
// 按钮组件 - 修改数据
class ButtonWidget extends StatelessWidget {
const ButtonWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// 4. 获取数据但不监听变化
final counter = context.read<CounterModel>();
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.green[100],
borderRadius: BorderRadius.circular(15),
),
child: Column(
children: [
const Text(
'按钮组件',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 15),
Row(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
onPressed: counter.decrement,
child: const Icon(Icons.remove),
),
const SizedBox(width: 20),
ElevatedButton(
onPressed: counter.increment,
child: const Icon(Icons.add),
),
],
),
],
),
);
}
}
Provider 的三种获取方式:
context.watch<T>()- 监听变化,数据改变时重建context.read<T>()- 只读取,不监听变化context.select<T, R>()- 只监听特定属性
5. 全局状态管理
对于复杂应用,推荐使用专业的状态管理方案。
常见方案对比
| 方案 | 难度 | 适用场景 |
|---|---|---|
| setState | ⭐ | 单个组件内部状态 |
| InheritedWidget | ⭐⭐ | 简单的跨层级通信 |
| Provider | ⭐⭐ | 中小型应用(推荐) |
| Riverpod | ⭐⭐⭐ | 中大型应用 |
| Bloc | ⭐⭐⭐⭐ | 大型应用,需要严格架构 |
| GetX | ⭐⭐ | 快速开发 |
Provider 完整示例(购物车)
dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => CartModel(),
child: const MaterialApp(
home: ShoppingPage(),
),
);
}
}
// 商品模型
class Product {
final String name;
final double price;
Product(this.name, this.price);
}
// 购物车模型
class CartModel extends ChangeNotifier {
final List<Product> _items = [];
List<Product> get items => _items;
int get itemCount => _items.length;
double get totalPrice {
return _items.fold(0, (sum, item) => sum + item.price);
}
void addItem(Product product) {
_items.add(product);
notifyListeners();
}
void removeItem(int index) {
_items.removeAt(index);
notifyListeners();
}
void clear() {
_items.clear();
notifyListeners();
}
}
// 主页面
class ShoppingPage extends StatelessWidget {
const ShoppingPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('全局状态管理 - 购物车'),
actions: [
// 购物车图标显示数量
Consumer<CartModel>(
builder: (context, cart, child) {
return Stack(
children: [
IconButton(
icon: const Icon(Icons.shopping_cart),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CartPage(),
),
);
},
),
if (cart.itemCount > 0)
Positioned(
right: 8,
top: 8,
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Text(
'${cart.itemCount}',
style: const TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
),
],
);
},
),
],
),
body: const ProductList(),
);
}
}
// 商品列表
class ProductList extends StatelessWidget {
const ProductList({Key? key}) : super(key: key);
final List<Product> products = const [
Product('iPhone 15', 5999),
Product('MacBook Pro', 12999),
Product('iPad Air', 4599),
Product('AirPods Pro', 1899),
Product('Apple Watch', 2999),
];
@override
Widget build(BuildContext context) {
final cart = context.read<CartModel>();
return ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return Card(
margin: const EdgeInsets.all(10),
child: ListTile(
title: Text(product.name),
subtitle: Text('¥${product.price}'),
trailing: ElevatedButton(
onPressed: () {
cart.addItem(product);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('${product.name} 已加入购物车'),
duration: const Duration(seconds: 1),
),
);
},
child: const Text('加入购物车'),
),
),
);
},
);
}
}
// 购物车页面
class CartPage extends StatelessWidget {
const CartPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('购物车'),
actions: [
IconButton(
icon: const Icon(Icons.delete_outline),
onPressed: () {
context.read<CartModel>().clear();
},
),
],
),
body: Consumer<CartModel>(
builder: (context, cart, child) {
if (cart.itemCount == 0) {
return const Center(
child: Text('购物车是空的', style: TextStyle(fontSize: 18)),
);
}
return Column(
children: [
Expanded(
child: ListView.builder(
itemCount: cart.itemCount,
itemBuilder: (context, index) {
final product = cart.items[index];
return ListTile(
title: Text(product.name),
subtitle: Text('¥${product.price}'),
trailing: IconButton(
icon: const Icon(Icons.remove_circle_outline),
onPressed: () {
cart.removeItem(index);
},
),
);
},
),
),
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.grey[200],
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.3),
spreadRadius: 2,
blurRadius: 5,
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'总计: ¥${cart.totalPrice.toStringAsFixed(2)}',
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
ElevatedButton(
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('确认结算'),
content: Text('总金额: ¥${cart.totalPrice.toStringAsFixed(2)}'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
cart.clear();
Navigator.pop(context);
Navigator.pop(context);
},
child: const Text('确认'),
),
],
),
);
},
child: const Text('结算'),
),
],
),
),
],
);
},
),
);
}
}
实战案例
综合案例:Todo 应用(包含所有通信方式)
dart
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: TodoApp(),
);
}
}
// Todo 数据模型
class Todo {
String title;
bool isDone;
Todo({required this.title, this.isDone = false});
}
// 主页面 - 管理所有状态
class TodoApp extends StatefulWidget {
const TodoApp({Key? key}) : super(key: key);
@override
State<TodoApp> createState() => _TodoAppState();
}
class _TodoAppState extends State<TodoApp> {
final List<Todo> todos = [];
// 添加 Todo(子传父)
void addTodo(String title) {
setState(() {
todos.add(Todo(title: title));
});
}
// 切换完成状态(子传父)
void toggleTodo(int index) {
setState(() {
todos[index].isDone = !todos[index].isDone;
});
}
// 删除 Todo(子传父)
void deleteTodo(int index) {
setState(() {
todos.removeAt(index);
});
}
// 编辑 Todo(子传父)
void editTodo(int index, String newTitle) {
setState(() {
todos[index].title = newTitle;
});
}
@override
Widget build(BuildContext context) {
// 统计数据
int totalCount = todos.length;
int doneCount = todos.where((todo) => todo.isDone).length;
int pendingCount = totalCount - doneCount;
return Scaffold(
appBar: AppBar(
title: const Text('Todo 应用 - 综合案例'),
),
body: Column(
children: [
// 统计组件(父传子)
TodoStats(
total: totalCount,
done: doneCount,
pending: pendingCount,
),
const Divider(),
// 输入组件(子传父)
TodoInput(onAdd: addTodo),
const Divider(),
// 列表组件(父传子 + 子传父)
Expanded(
child: TodoList(
todos: todos,
onToggle: toggleTodo,
onDelete: deleteTodo,
onEdit: editTodo,
),
),
],
),
);
}
}
// 统计组件 - 接收数据(父传子)
class TodoStats extends StatelessWidget {
final int total;
final int done;
final int pending;
const TodoStats({
Key? key,
required this.total,
required this.done,
required this.pending,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(15),
color: Colors.blue[50],
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_StatItem('总计', total, Colors.blue),
_StatItem('已完成', done, Colors.green),
_StatItem('待办', pending, Colors.orange),
],
),
);
}
}
class _StatItem extends StatelessWidget {
final String label;
final int count;
final Color color;
const _StatItem(this.label, this.count, this.color);
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
'$count',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: color,
),
),
Text(label, style: TextStyle(color: Colors.grey[600])),
],
);
}
}
// 输入组件 - 发送数据(子传父)
class TodoInput extends StatefulWidget {
final Function(String) onAdd;
const TodoInput({
Key? key,
required this.onAdd,
}) : super(key: key);
@override
State<TodoInput> createState() => _TodoInputState();
}
class _TodoInputState extends State<TodoInput> {
final TextEditingController controller = TextEditingController();
void submit() {
if (controller.text.trim().isEmpty) return;
widget.onAdd(controller.text.trim());
controller.clear();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(15),
child: Row(
children: [
Expanded(
child: TextField(
controller: controller,
decoration: const InputDecoration(
hintText: '输入待办事项...',
border: OutlineInputBorder(),
),
onSubmitted: (_) => submit(),
),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: submit,
child: const Text('添加'),
),
],
),
);
}
}
// 列表组件 - 接收数据和回调(父传子 + 子传父)
class TodoList extends StatelessWidget {
final List<Todo> todos;
final Function(int) onToggle;
final Function(int) onDelete;
final Function(int, String) onEdit;
const TodoList({
Key? key,
required this.todos,
required this.onToggle,
required this.onDelete,
required this.onEdit,
}) : super(key: key);
@override
Widget build(BuildContext context) {
if (todos.isEmpty) {
return const Center(
child: Text(
'暂无待办事项\n点击上方添加按钮开始',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16, color: Colors.grey),
),
);
}
return ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
return TodoItem(
todo: todos[index],
onToggle: () => onToggle(index),
onDelete: () => onDelete(index),
onEdit: (newTitle) => onEdit(index, newTitle),
);
},
);
}
}
// Todo 项组件 - 接收数据和回调
class TodoItem extends StatelessWidget {
final Todo todo;
final VoidCallback onToggle;
final VoidCallback onDelete;
final Function(String) onEdit;
const TodoItem({
Key? key,
required this.todo,
required this.onToggle,
required this.onDelete,
required this.onEdit,
}) : super(key: key);
void showEditDialog(BuildContext context) {
final controller = TextEditingController(text: todo.title);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('编辑待办'),
content: TextField(
controller: controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
autofocus: true,
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
if (controller.text.trim().isNotEmpty) {
onEdit(controller.text.trim());
Navigator.pop(context);
}
},
child: const Text('保存'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
child: ListTile(
leading: Checkbox(
value: todo.isDone,
onChanged: (_) => onToggle(),
),
title: Text(
todo.title,
style: TextStyle(
decoration: todo.isDone ? TextDecoration.lineThrough : null,
color: todo.isDone ? Colors.grey : null,
),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.edit, color: Colors.blue),
onPressed: () => showEditDialog(context),
),
IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
onPressed: onDelete,
),
],
),
),
);
}
}
最佳实践
1. 选择合适的通信方式
dart
// ✅ 简单的父子通信 - 使用构造函数
class ChildWidget extends StatelessWidget {
final String data;
const ChildWidget({Key? key, required this.data}) : super(key: key);
// ...
}
// ✅ 子传父 - 使用回调函数
class ChildWidget extends StatelessWidget {
final VoidCallback onPressed;
const ChildWidget({Key? key, required this.onPressed}) : super(key: key);
// ...
}
// ✅ 跨层级通信 - 使用 Provider
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final data = context.watch<MyModel>();
// ...
}
}
2. 回调函数命名规范
dart
// ✅ 推荐:使用 on 前缀
final VoidCallback onPressed;
final Function(String) onChanged;
final Function(int) onItemSelected;
// ❌ 不推荐
final VoidCallback pressHandler;
final Function(String) changeFunc;
3. 避免过度传递
dart
// ❌ 不推荐:层层传递
Parent -> Child1 -> Child2 -> Child3 (传递数据)
// ✅ 推荐:使用 Provider 或 InheritedWidget
Parent (提供数据)
└── Child3 (直接获取数据)
4. 使用 const 优化性能
dart
// ✅ 推荐:使用 const
const ChildWidget(data: 'hello')
// ❌ 不推荐:不使用 const
ChildWidget(data: 'hello')
5. 回调函数类型定义
dart
// ✅ 推荐:使用 typedef 定义复杂回调
typedef OnItemSelected = void Function(int index, String value);
class MyWidget extends StatelessWidget {
final OnItemSelected onItemSelected;
// ...
}
// ✅ 推荐:使用内置类型
final VoidCallback onPressed; // void Function()
final ValueChanged<String> onChanged; // void Function(String)
6. 状态提升原则
dart
// 状态应该放在需要它的最小公共父组件中
// ❌ 不推荐:状态放在顶层
App (state)
└── PageA
└── ComponentB (使用 state)
// ✅ 推荐:状态放在最近的父组件
App
└── PageA (state)
└── ComponentB (使用 state)
总结
通信方式速查表
| 场景 | 方式 | 示例 |
|---|---|---|
| 父传子 | 构造函数参数 | ChildWidget(data: value) |
| 子传父 | 回调函数 | ChildWidget(onPressed: handlePress) |
| 兄弟通信 | 通过父组件中转 | 父组件管理状态 |
| 跨层级 | InheritedWidget | MyData.of(context) |
| 跨层级 | Provider | context.watch<MyModel>() |
| 全局状态 | Provider/Riverpod/Bloc | 状态管理库 |
关键要点
-
父传子:通过构造函数传递数据
dartChildWidget(name: 'value') -
子传父:通过回调函数传递事件
dartChildWidget(onPressed: () => handleEvent()) -
兄弟通信:通过父组件作为中介
dart父组件管理状态 → 子组件A触发 → 父组件更新 → 子组件B接收 -
跨层级:使用 InheritedWidget 或 Provider
dartcontext.watch<MyModel>() -
全局状态:使用专业的状态管理方案
- 小项目:Provider
- 中大型:Riverpod 或 Bloc
选择建议
- 简单应用:setState + 回调函数
- 中型应用:Provider
- 大型应用:Riverpod 或 Bloc
- 快速开发:GetX
掌握这些通信方式,你就能轻松处理 Flutter 中的数据流动!🎉