Flutter问题:拖拽关闭BottomSheetModal时PopScope失效
解决Flutter模态框拖拽关闭时不触发确认对话框的问题
要实现表单有输入内容时,拖拽关闭模态框也弹出放弃确认的逻辑,需要从两个方面调整代码:拦截模态框的所有Pop动作(包括拖拽)、跟踪表单输入状态。
关键修改点
- 添加输入状态跟踪:用
TextEditingController监听表单输入,判断是否有内容。 - 全局拦截Pop动作:将
PopScope作为模态框的根组件,覆盖整个模态框区域,这样拖拽、点击返回键等触发的Pop都会被拦截。 - 条件触发确认对话框:在
onPopInvoked中根据输入状态决定直接关闭还是弹出确认框。
修改后的完整代码
import 'package:flutter/material.dart'; void main() => runApp(const NavigatorPopHandlerApp()); class NavigatorPopHandlerApp extends StatelessWidget { const NavigatorPopHandlerApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( initialRoute: '/home', routes: <String, WidgetBuilder>{ '/home': (BuildContext context) => const _HomePage(), }, ); } } class _HomePage extends StatefulWidget { const _HomePage(); @override State<_HomePage> createState() => _HomePageState(); } class _HomePageState extends State<_HomePage> { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text('Page One'), TextButton( onPressed: () { showModalBottomSheet<void>( context: context, enableDrag: true, builder: (BuildContext context) { return const _FormModal(); }, ); }, child: const Text('Show Bottom Sheet'), ), ], ), ), ); } } class _FormModal extends StatefulWidget { const _FormModal(); @override State<_FormModal> createState() => _FormModalState(); } class _FormModalState extends State<_FormModal> { final TextEditingController _inputController = TextEditingController(); void _showDiscardDialog() { showDialog<void>( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('确定要离开吗?'), content: const Text( '离开后输入的内容将会丢失,确定要放弃吗?', ), actions: <Widget>[ TextButton( style: TextButton.styleFrom( textStyle: Theme.of(context).textTheme.labelLarge, ), child: const Text('取消'), onPressed: () { Navigator.pop(context); }, ), TextButton( style: TextButton.styleFrom( textStyle: Theme.of(context).textTheme.labelLarge, ), child: const Text('离开'), onPressed: () { Navigator.pop(context); Navigator.pop(context); }, ), ], ); }, ); } @override void dispose() { _inputController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return PopScope( canPop: false, onPopInvoked: (bool didPop) { if (didPop) return; if (_inputController.text.isNotEmpty) { _showDiscardDialog(); } else { Navigator.pop(context); } }, child: Container( padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ Container(color: Colors.black54, height: 4, width: 48), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('用户信息表单'), IconButton( onPressed: () { if (_inputController.text.isNotEmpty) { _showDiscardDialog(); } else { Navigator.pop(context); } }, icon: const Icon(Icons.close), ), ], ), const SizedBox(height: 16), TextField( controller: _inputController, decoration: const InputDecoration( hintText: '请输入内容', border: OutlineInputBorder(), ), ), ], ), ), ); } }
代码解释
- TextEditingController:监听输入框内容变化,作为是否触发确认对话框的判断依据。
- PopScope根组件:覆盖整个模态框区域,确保拖拽、返回键、代码调用
Navigator.pop等所有Pop动作都会进入回调处理。 - 条件判断逻辑:仅当输入框有内容时弹出确认对话框,无内容时直接关闭,避免不必要的交互干扰。
内容的提问来源于stack exchange,提问作者Engin Yagmur
相关产品推荐
相关产品推荐

