以下是基于 Flutter 最新版本的 PopScope
使用示例及说明,结合官方文档和社区实践:
基础用法示例
dart
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool _canPop = true; // 控制是否允许返回
final TextEditingController _textController = TextEditingController();
@override
void dispose() {
_textController.dispose();
super.dispose();
}
Future<bool> _handlePop() async {
if (_textController.text.isEmpty) return true; // 允许直接返回
// 弹出二次确认对话框
final confirm = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('确认退出?'),
content: const Text('输入内容未保存,确定要离开吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('取消'),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
child: const Text('确定'),
),
],
),
);
return confirm ?? false; // 用户点击确定时返回true
}
@override
Widget build(BuildContext context) {
return PopScope(
canPop: _canPop, // 动态控制返回手势是否生效 [[6]][[7]]
onPopInvoked: (didPop) async {
if (didPop) return; // 如果已经弹出则直接返回
final allowed = await _handlePop();
if (allowed && mounted) {
Navigator.pop(context); // 手动触发返回操作 [[2]][[4]]
}
},
child: Scaffold(
appBar: AppBar(title: const Text('PopScope 示例')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _textController,
onChanged: (value) {
// 输入内容变化时更新canPop状态
setState(() => _canPop = value.isEmpty);
},
),
],
),
),
),
);
}
}
关键点说明
-
替代
WillPopScope
PopScope
在 Flutter 3.16+ 完全替代WillPopScope
,通过canPop
和onPopInvoked
实现返回拦截 [[3]][[6]]。 -
canPop
动态控制canPop: true
时,系统返回手势(如 Android 物理按键/滑动返回)直接生效 [[7]]。canPop: false
时,需通过onPopInvoked
手动处理返回逻辑 [[8]]。
-
异步确认弹窗
在
onPopInvoked
中可通过showDialog
弹出确认框,结合async/await
实现阻塞式交互 [[1]][[4]]。 -
与导航器的配合
PopScope
需包裹在Navigator
的上层组件中(如MaterialApp
的子组件),才能正确拦截返回事件 [[2]][[3]]。
根据最新 Flutter 版本(v3.22+)的变更,onPopInvoked
已被弃用,需改用 onPopInvokedWithResult
。以下是修改后的代码示例及关键点说明:
修改后的代码
scss
class _HomePageState extends State<HomePage> {
// ... 其他代码保持不变
@override
Widget build(BuildContext context) {
return PopScope(
canPop: _canPop,
onPopInvokedWithResult: (didPop, result) async { // 修改为 onPopInvokedWithResult [[1]][[2]]
if (didPop) return; // 已处理弹出则直接返回
final allowed = await _handlePop();
if (allowed && mounted) {
Navigator.pop(context); // 手动触发返回
}
},
child: Scaffold(
// ... 原有 Scaffold 内容
),
);
}
}
关键修改点
-
替换回调名称
将
onPopInvoked
改为onPopInvokedWithResult
,这是 Flutter 3.22+ 的强制要求 [[1]][[2]]。 -
参数兼容性处理
-
新回调的参数
(didPop, result)
中:didPop
表示路由是否已弹出(与旧版行为一致)[[1]]。result
是导航返回时传递的可选数据(如Navigator.pop(context, result)
)[[6]]。
-
若无需处理返回值,可直接忽略
result
参数。
-
-
保持原有逻辑
_handlePop()
方法的异步确认逻辑无需修改,只需确保在allowed
为true
时手动调用Navigator.pop
[[4]][[6]]。
迁移建议
- 最低版本要求
确保pubspec.yaml
中 Flutter SDK 版本不低于3.22.0-12.0.pre
,否则会因 API 不兼容导致编译失败 [[1]]。 - 处理返回结果
若需要通过Navigator.pop(context, result)
传递数据,可通过onPopInvokedWithResult
的result
参数接收 [[6]]。 - 与旧版兼容
如需支持 Flutter 3.12 至 3.22 之间的版本,可通过条件导入或版本检测实现兼容逻辑,但会增加代码复杂度。
通过上述修改,代码将符合最新 Flutter API 规范,同时保持原有的返回拦截功能(如未保存内容的确认弹窗)。