PopScope 是什么?
PopScope 是 Flutter 提供的一个 路由返回(pop)拦截和监听组件。当用户尝试返回页面(比如按下返回键、手势返回、调用 Navigator.pop()),PopScope 可以:
- 控制:是否允许返回
- 监听:返回事件是否真正发生
参数
const PopScope({
super.key,
required this.child,
this.canPop = true,
this.onPopInvokedWithResult,
@Deprecated(
'Use onPopInvokedWithResult instead. '
'This feature was deprecated after v3.22.0-12.0.pre.',
)
this.onPopInvoked,
}) : assert(
onPopInvokedWithResult == null || onPopInvoked == null,
'onPopInvoked is deprecated, use onPopInvokedWithResult',
);
child类型:
Widget说明:这是
PopScope包裹的子组件,也就是你真正要展示的页面内容。
canPop类型:
bool说明:是否允许 自动返回。
canPop = true:用户按返回键 / 手势返回 / 调用Navigator.pop()→ 页面会 直接返回。这时onPopInvokedWithResult(didPop: true, result: xxx)会被触发,告诉你页面已经返回了。canPop = false:用户按返回键 / 手势返回 / 调用Navigator.pop()→ 不会自动退出。而是先触发onPopInvokedWithResult(didPop: false, result: null)。在这个回调里可以:- 弹出确认框(是否真的要退出)
- 保存数据后再退出
- 或者干脆什么都不做,阻止退出
如果你决定真的退出 → 需要手动调用:
Navigator.of(context).pop("自定义的返回值");手动退出后,
onPopInvokedWithResult(didPop: true, result: "自定义的返回值")会再次触发。
onPopInvokedWithResult:- 类型:
void Function(bool didPop, Object? result) - 说明:当页面尝试返回时调用(推荐使用这个)。
didPop→ 表示是否真的返回了true:已经返回成功false:返回被拦截了
result→ 如果返回时Navigator.pop(result)带了值,这里可以接收到。
- 类型:
onPopInvoked(已过时)类型:
void Function(bool didPop)说明:和
onPopInvokedWithResult类似,但 没有result参数,因此无法知道Navigator.pop(result)传出的值。
入门案例
代码
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: FirstPage());
}
}
class FirstPage extends StatelessWidget {
const FirstPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("First Page")),
body: Center(
child: ElevatedButton(
onPressed: () async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (_) => const SecondPage()),
);
debugPrint("从 SecondPage 返回的结果: $result");
},
child: const Text("跳转到 SecondPage"),
),
),
);
}
}
class SecondPage extends StatelessWidget {
const SecondPage({super.key});
@override
Widget build(BuildContext context) {
return PopScope(
canPop: false, // 禁止自动返回
onPopInvokedWithResult: (didPop, result) {
if (didPop) {
debugPrint("页面已返回,返回值: $result");
} else {
debugPrint("返回被拦截,可以做处理逻辑");
// 手动退出并带数据
Navigator.of(context).pop("从 SecondPage 自定义的返回值");
}
},
child: Scaffold(
appBar: AppBar(title: const Text("Second Page")),
body: const Center(child: Text("按返回键或手势,触发 PopScope")),
),
);
}
}
日志如下:
I/flutter ( 8164): 返回被拦截,可以做处理逻辑
I/flutter ( 8164): 页面已返回,返回值: 从 SecondPage 自定义的返回值
I/flutter ( 8164): 从 SecondPage 返回的结果: 从 SecondPage 自定义的返回值
解释
情况一:canPop = false
- 用户点击返回键 / 手势返回
- 系统发现
canPop = false→ 不会自动退出 - 触发
onPopInvokedWithResult(didPop: false, result: null)- 这里的
didPop = false表示 页面没有退出
- 这里的
- 如果你在这里调用了
Navigator.of(context).pop("返回值")- 页面真正退出
- 再次触发
onPopInvokedWithResult(didPop: true, result: "返回值")
👉 所以 会触发两次回调:
- 第一次:拦截(
didPop = false) - 第二次:真正退出(
didPop = true)
情况二:canPop = true
- 用户点击返回键 / 手势返回
- 系统允许返回,页面立即退出
- 触发
onPopInvokedWithResult(didPop: true, result: null)- 这里
didPop = true,因为页面已经退出 - 如果
Navigator.pop("值"),那么result就是这个值
- 这里
👉 所以 只会触发一次回调(直接返回成功)。