组件通信
基础通信:父子组件之间
- 父传子(构造函数传参) :父组件在实例化子组件时,通过构造函数将数据作为属性传递给子组件。子组件通常使用
final关键字接收这些属性,以保证数据的不可变性。 - 子传父(回调函数) :父组件将一个回调函数(Callback)作为参数传递给子组件。当子组件内部发生特定事件(如按钮点击、表单提交)时,调用该函数并将数据回传给父组件,父组件再根据接收到的数据更新自身状态。
进阶通信:跨层级组件之间
当组件嵌套层级过深时,如果依然使用构造函数层层传参,会导致代码极其臃肿(即"prop drilling"问题)。此时需要借助跨层通信机制:
- InheritedWidget: Flutter 内置的底层机制,允许数据在 Widget 树中高效地自上而下传递。
- Notification: 一种自下而上的事件冒泡机制。子 Widget 可以分发(dispatch)一个 Notification,任何上层的 NotificationListener 都可以监听到这个通知并进行处理,无需显式传递回调函数
状态管理:任意组件之间通信
跨组件,跨页面之间的数据共享和通信。
- Provider 官方推荐的状态管理库,本质上是对InheritedWidget的封装。
- BLoC / Riverpod:适用于复杂业务逻辑的状态管理方案。BLoC 基于事件(Events)和状态(States)分离的原则;Riverpod 则是 Provider 的下一代演进,提供了编译时安全和更灵活的依赖注入
- GetX: 一个轻量级的微型框架,集成了状态管理、路由管理和依赖注入,以语法简洁和高性能著称
- EventBus: 基于"发布/订阅"模式的事件总线。发布者和订阅者之间无需任何父子关系,非常适合全局事件通信(如用户登录状态变更、全局消息通知等)
架构层间通信 MVVM
在大型应用中,还需要考虑 View、ViewModel、Repository 和 Service 等架构层之间的通信规范。通常遵循依赖注入原则,各层通过构造函数接收依赖的对象(例如 ViewModel 通过构造函数接收 Repository),从而保持各层职责清晰、低耦合。
其它
- 原生平台通信,通常使用MethodChannl进行一步方法调用,实现Flutter与iOS/Android通信。
- Isolate通信,Dart并发模型,不同的Ioslate之间通过SendPort和ReceivePort进行消息传递。
- 网络通信,使用Dio等网络请求插件。
父传子和子传父
scala
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.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('CustomScrollView 全方位知识点')),
body: Container(
color: Colors.green,
alignment: Alignment.center,
child: Column(
children: [
Text("这是父组件"),
ShowPage(message: "这是第一个无状态子组件"),
ShowPage(message: "这是第二个无状态子组件"),
ChildPage(message: "这是第一个有状态子组件"),
ChildPage(message: "这是第二个有状态子组件"),
CallBackFather(),
],
),
),
);
}
}
class ShowPage extends StatelessWidget {
//定义final属性
final String message;
const ShowPage({Key? key, required this.message}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(child: Text(message));
}
}
class ChildPage extends StatefulWidget {
final String message;
ChildPage({Key? key, required this.message}) : super(key: key);
@override
_ChildPageState createState() => _ChildPageState();
}
class _ChildPageState extends State<ChildPage> {
@override
Widget build(BuildContext context) {
return Container(child: Text("${widget.message}"));
}
}
class CallBackFather extends StatefulWidget {
CallBackFather({Key? key}) : super(key: key);
@override
_CallBackFatherState createState() => _CallBackFatherState();
}
class _CallBackFatherState extends State<CallBackFather> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
height: 60,
child: Row(
mainAxisAlignment: .center,
children: [
Text("这是父组件 $_count"),
CallBackChild(
callback: () {
setState(() {
_count += 1;
});
},
),
],
),
);
}
}
class CallBackChild extends StatelessWidget {
final Function() callback;
const CallBackChild({Key? key, required this.callback}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
child: GestureDetector(
child: Text("callback + 1",style: TextStyle(color: Colors.amber,backgroundColor: Colors.black),),
onTap: () {
callback();
},
),
);
}
}
跨层级的通信
less
没有 InheritedWidget 时参数要层层 drilling// 没有 InheritedWidget → 层层传参
PageA(userId: "123", userName: "张三")
└─ PageB(userId: "123", userName: "张三") ← 被迫接收
└─ PageC(userId: "123", userName: "张三") ← 被迫接收
└─ PageD(userId: "123", userName: "张三") ← 被迫接收
└─ PageE(userId: "123", userName: "张三") ← 真正需要
// B/C/D 根本不需要 userId 和 userName
// 但为了传给 E,不得不声明这些参数
// Widget 嵌套越深越痛苦
需要定义一个组件继承InheritedWidget。然后这个组件的所有子孙组件都可以获取。
less
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(const MaterialApp(home: SelectionArea(child: HomePage())));
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('InheritedWidget 组件通信')),
body: ListView(
padding: const EdgeInsets.all(16),
children: const [
_Section1_What(),
_Section2_Problem(),
_Section3_HowToUse(),
_Section4_FlutterOwn(),
_Section5_DeepUpdate(),
_Section6_Limitations(),
_Section7_Summary(),
],
),
);
}
}
// ────────────────────────────────────────────────────────────────
// ① InheritedWidget 是什么
// ────────────────────────────────────────────────────────────────
class _Section1_What extends StatelessWidget {
const _Section1_What();
@override
Widget build(BuildContext context) {
return _Card(
title: '① InheritedWidget 是什么',
subtitle: '跨层级组件通信的"数据总线"',
children: const [
Text(
'// 问题:Widget 树层级深时,数据一层层传太麻烦\n'
'A → B → C → D → E\n'
'A 有数据要给 E,必须 B/C/D 都带着这个参数\n'
'→ 叫"参数 drilling(参数钻探)"\n\n'
'// InheritedWidget 解决什么?\n'
'把数据存在树上层的 InheritedWidget 里\n'
'下层任意 Widget 都可以通过 context 直接找到它\n'
'不用中间层一层层传\n\n'
'// Flutter 框架里大量用了 InheritedWidget:\n'
'Theme.of(context) ← ThemeData 存在 InheritedWidget\n'
'MediaQuery.of(context) ← MediaQueryData 存在 InheritedWidget\n'
'Scaffold.of(context) ← ScaffoldState 存在 InheritedWidget\n'
'Navigator.of(context) ← Navigator 存在 InheritedWidget',
style: TextStyle(fontSize: 11, fontFamily: 'monospace'),
),
],
);
}
}
// ────────────────────────────────────────────────────────────────
// ② 没有 InheritedWidget 时的痛点
// ────────────────────────────────────────────────────────────────
class _Section2_Problem extends StatelessWidget {
const _Section2_Problem();
@override
Widget build(BuildContext context) {
return _Card(
title: '② 痛点:一层层传参',
subtitle: '没有 InheritedWidget 时参数要层层 drilling',
children: const [
Text(
'// 没有 InheritedWidget → 层层传参\n\n'
'PageA(userId: "123", userName: "张三")\n'
' └─ PageB(userId: "123", userName: "张三") ← 被迫接收\n'
' └─ PageC(userId: "123", userName: "张三") ← 被迫接收\n'
' └─ PageD(userId: "123", userName: "张三") ← 被迫接收\n'
' └─ PageE(userId: "123", userName: "张三") ← 真正需要\n\n'
'// B/C/D 根本不需要 userId 和 userName\n'
'// 但为了传给 E,不得不声明这些参数\n'
'// Widget 嵌套越深越痛苦\n\n'
'// 有了 InheritedWidget → 直接取\n\n'
'class PageE extends StatelessWidget {\n'
' @override\n'
' Widget build(BuildContext context) {\n'
' final data = UserData.of(context); // 直接取!\n'
' return Text(data.userName);\n'
' }\n'
'}',
style: TextStyle(fontSize: 11, fontFamily: 'monospace'),
),
],
);
}
}
// ────────────────────────────────────────────────────────────────
// ③ 怎么用 ------ 完整示例
// ────────────────────────────────────────────────────────────────
class _Section3_HowToUse extends StatelessWidget {
const _Section3_HowToUse();
@override
Widget build(BuildContext context) {
return _Card(
title: '③ 怎么用 ------ 完整三步',
subtitle: '定义 → 挂载 → 取用',
children: [
// 实际演示
const _Demo(),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.all(8),
color: Colors.amber.shade50,
child: const Text(
'// 步骤 1:定义 InheritedWidget\n\n'
'class CounterInherited extends InheritedWidget {\n'
' final int count;\n'
' final VoidCallback increment;\n'
'\n'
' const CounterInherited({\n'
' super.key,\n'
' required this.count,\n'
' required this.increment,\n'
' required super.child,\n'
' });\n'
'\n'
' // 步骤 3 的配套:提供 of(context) 静态方法\n'
' static CounterInherited of(BuildContext context) {\n'
' return context.dependOnInheritedWidgetOfExactType<CounterInherited>();\n'
' }\n'
'\n'
' // ✅ 必须重写!决定什么时候通知下游更新\n'
' @override\n'
' bool updateShouldNotify(CounterInherited oldWidget) {\n'
' return count != oldWidget.count; // 数据变了才通知\n'
' }\n'
'}\n\n'
'// 步骤 2:在树上层挂载(通常放 MaterialApp 或页面根)\n'
'class _DemoState extends State<_Demo> {\n'
' int _count = 0;\n'
'\n'
' @override\n'
' Widget build(BuildContext context) {\n'
' return CounterInherited(\n'
' count: _count,\n'
' increment: () => setState(() => _count++),\n'
' child: const Column(\n'
' children: [_PageA(), _PageB()],\n'
' ),\n'
' );\n'
' }\n'
'}\n\n'
'// 步骤 3:任意子 Widget 通过 of(context) 取数据\n'
'class _PageA extends StatelessWidget {\n'
' @override\n'
' Widget build(BuildContext context) {\n'
' final inherited = CounterInherited.of(context);\n'
' return Text("count: \\\${inherited.count}");\n'
' }\n'
'}',
style: TextStyle(fontSize: 10.5, fontFamily: 'monospace'),
),
),
],
);
}
}
// ───── 演示组件 ─────
class _Demo extends StatefulWidget {
const _Demo();
@override
State<_Demo> createState() => _DemoState();
}
class _DemoState extends State<_Demo> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.all(8),
child: CounterInherited(
count: _count,
increment: () => setState(() => _count++),
reset: () => setState(() => _count = 0),
child: const Column(
children: [
_DemoHeader(),
SizedBox(height: 6),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [_DemoButton(), _DemoButton()],
),
SizedBox(height: 6),
_DemoFooter(),
],
),
),
);
}
}
// ───── 定义 InheritedWidget ─────
class CounterInherited extends InheritedWidget {
final int count;
final VoidCallback increment;
final VoidCallback reset;
const CounterInherited({
super.key,
required this.count,
required this.increment,
required this.reset,
required super.child,
});
static CounterInherited of(BuildContext context) {
final result = context
.dependOnInheritedWidgetOfExactType<CounterInherited>();
assert(result != null, 'CounterInherited 未找到');
return result!;
}
@override
bool updateShouldNotify(CounterInherited oldWidget) {
return count != oldWidget.count;
}
}
// ───── 三个子组件,分别取数据 ─────
class _DemoHeader extends StatelessWidget {
const _DemoHeader();
@override
Widget build(BuildContext context) {
final inherited = CounterInherited.of(context);
return Text(
'Header: count = ${inherited.count}',
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
);
}
}
class _DemoButton extends StatelessWidget {
const _DemoButton();
@override
Widget build(BuildContext context) {
final inherited = CounterInherited.of(context);
return ElevatedButton(
onPressed: inherited.increment,
child: const Text('+1', style: TextStyle(fontSize: 12)),
);
}
}
class _DemoFooter extends StatelessWidget {
const _DemoFooter();
@override
Widget build(BuildContext context) {
final inherited = CounterInherited.of(context);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Footer: ${inherited.count}',
style: const TextStyle(fontSize: 11),
),
const SizedBox(width: 8),
TextButton(
onPressed: inherited.reset,
child: const Text('reset', style: TextStyle(fontSize: 11)),
),
],
);
}
}
// ────────────────────────────────────────────────────────────────
// ④ Flutter 自己怎么用 ------ Theme.of 源码分析
// ────────────────────────────────────────────────────────────────
class _Section4_FlutterOwn extends StatelessWidget {
const _Section4_FlutterOwn();
@override
Widget build(BuildContext context) {
return _Card(
title: '④ Flutter 框架里的 InheritedWidget',
subtitle: 'Theme / MediaQuery 都是它',
children: const [
Text(
'// Theme.of(context) 源码简化:\n\n'
'class Theme extends InheritedWidget {\n'
' final ThemeData data;\n'
'\n'
' static ThemeData of(BuildContext context) {\n'
' final widget = context.dependOnInheritedWidgetOfExactType<Theme>();\n'
' return widget!.data;\n'
' }\n'
'\n'
' @override\n'
' bool updateShouldNotify(Theme oldWidget) {\n'
' return data != oldWidget.data;\n'
' }\n'
'}\n\n'
'// MaterialApp 内部已经帮你挂载了 Theme\n'
'// 所以你可以在任意页面直接 Theme.of(context)\n'
'// 这就是 InheritedWidget 的威力\n\n'
'// 同样的还有:\n'
'MediaQuery.of(context) → MediaQuery\n'
'Navigator.of(context) → Navigator\n'
'Scaffold.of(context) → ScaffoldMessenger\n'
'Localizations.of(context) → Localizations',
style: TextStyle(fontSize: 11, fontFamily: 'monospace'),
),
],
);
}
}
// ────────────────────────────────────────────────────────────────
// ⑤ updateShouldNotify ------ 什么时候重新构建子组件
// ────────────────────────────────────────────────────────────────
class _Section5_DeepUpdate extends StatelessWidget {
const _Section5_DeepUpdate();
@override
Widget build(BuildContext context) {
return _Card(
title: '⑤ updateShouldNotify 深解',
subtitle: '不是简单的==,要考虑子组件是否依赖这个数据',
children: const [
Text(
'// updateShouldNotify 是 InheritedWidget 的灵魂\n'
'// 它决定:当 InheritedWidget 数据变了,\n'
'// 哪些子组件需要 rebuild',
style: TextStyle(fontSize: 11),
),
SizedBox(height: 6),
Text(
'三种写法对比:',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 11),
),
SizedBox(height: 4),
Text(
'// 写法 1:无脑返回 true(最简单但性能差)\n'
'@override\n'
'bool updateShouldNotify(covariant InheritedWidget old) => true;\n'
'// 任何变化都通知所有下游\n\n'
'// 写法 2:用 == 比较(推荐,足够用)\n'
'@override\n'
'bool updateShouldNotify(CounterInherited old) {\n'
' return count != old.count; // 只在 count 变了才通知\n'
'}\n\n'
'// 写法 3:精细控制哪个属性变了通知谁(高级)\n'
'// 场景:InheritedWidget 有 10 个字段,A 只关心 name,B 只关心 age\n'
'// 这时可以拆分多个 InheritedWidget\n'
'// 或者用 ValueNotifier + Consumer 模式(类似 Provider)',
style: TextStyle(fontSize: 11, fontFamily: 'monospace'),
),
],
);
}
}
// ────────────────────────────────────────────────────────────────
// ⑥ 局限性 & 与 Provider 的关系
// ────────────────────────────────────────────────────────────────
class _Section6_Limitations extends StatelessWidget {
const _Section6_Limitations();
@override
Widget build(BuildContext context) {
return _Card(
title: '⑥ 局限性 & 与 Provider 的关系',
subtitle: '为什么有了 InheritedWidget 还要 Provider/Riverpod?',
children: const [
Text(
'InheritedWidget 的问题:',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 11,
color: Colors.red,
),
),
Text(
'1. 必须手动改 state → setState → 更新 InheritedWidget\n'
' 没有状态管理,只有数据传递\n\n'
'2. updateShouldNotify 要自己写,容易写错\n\n'
'3. 多个 InheritedWidget 嵌套太啰嗦\n\n'
'4. 不支持"只监听某个属性"\n'
' context.dependOnInheritedWidgetOfExactType\n'
' 是全量监听',
style: TextStyle(fontSize: 11),
),
SizedBox(height: 6),
Text(
'Provider / Riverpod 是 InheritedWidget 的封装升级:',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 11,
color: Colors.green,
),
),
Text(
'Provider<T>(create: (_) => MyModel(), child: ...)\n'
'// 底层就是 InheritedWidget + ChangeNotifier\n'
'// 帮你自动处理 updateShouldNotify\n'
'// 帮你自动 setState\n\n'
'Consumer<T>(builder: (_, model, __) {\n'
' return Text(model.name); // 只在 model 变化时 rebuild\n'
'})\n\n'
'// 结论:\n'
'// • 简单场景 → 直接写 InheritedWidget\n'
'// • 复杂状态管理 → 用 Riverpod(当前推荐)\n'
'// • 面试要答出:Provider 底层就是 InheritedWidget',
style: TextStyle(fontSize: 11, fontFamily: 'monospace'),
),
],
);
}
}
// ────────────────────────────────────────────────────────────────
// ⑦ 总结
// ────────────────────────────────────────────────────────────────
class _Section7_Summary extends StatelessWidget {
const _Section7_Summary();
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.brown.shade50,
borderRadius: BorderRadius.circular(8),
),
child: const Text(
'📌 InheritedWidget 完整总结\n\n'
'1. 核心作用:跨层级共享数据,避免参数 drilling\n\n'
'2. 三步用法:\n'
' ① 继承 InheritedWidget,重写 updateShouldNotify\n'
' ② 在树上层挂载(通常根节点)\n'
' ③ 子组件通过 InheritedClass.of(context) 取用\n\n'
'3. 两个获取方法:\n'
' dependOnInheritedWidgetOfExactType\n'
' → 注册依赖,数据变了自动 rebuild ✅\n'
' getInheritedWidgetOfExactType\n'
' → 只取值,不注册依赖,数据变了不 rebuild\n\n'
'4. updateShouldNotify:\n'
' 返回 true → 通知所有 dependOn 的子组件 rebuild\n'
' 返回 false → 不通知\n'
' 推荐用属性 == 比较\n\n'
'5. 与 Provider 的关系:\n'
' Provider 底层 = InheritedWidget + ChangeNotifier\n'
' InheritedWidget 是数据传递\n'
' Provider 是完整的状态管理\n\n'
'6. Flutter 框架里到处是 InheritedWidget:\n'
' Theme / MediaQuery / Navigator / Scaffold',
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,
],
),
);
}
}