Flutter登录异步流程弹窗行为异常的代码问题排查
问题描述
我希望在登录流程中展示弹窗以优化用户体验,因为应用在跳转到首页前需要保存部分信息。但弹窗行为存在异常:有时在跳转首页前显示,有时在跳转后显示且无法关闭。相关代码如下:
PrimaryButton( text: 'Sign In', press: () async { if (_formKey.currentState!.validate()) { final message = await AuthService().login( email: _emailController.text.trim(), password: _passwordController.text.trim(), ); if (message!.contains('Success')) { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('Signing you in...'), content: Container( height: 50.0, width: 50.0, color: Colors.red, child: const Center( child: CircularProgressIndicator(), ), ), ); }, ); final currUserID = FirebaseAuth.instance.currentUser!.uid; await saveUserModelToHive(currUserID); await saveSomeInfo(); Navigator.of(context).pop(); if (!mounted) return; Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (context) => const LandingPage(), ), ); } else { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), duration: const Duration(seconds: 5), ), ); } } }, ),
问题原因分析
- 上下文(Context)混乱:调用
showDialog时使用的是当前页面的context,后续的Navigator.pushReplacement会替换当前页面的导航栈。如果saveUserModelToHive和saveSomeInfo执行较慢,页面已被替换,弹窗的上下文会失效,导致弹窗无法关闭;如果这两个操作执行过快,弹窗刚显示就被Navigator.of(context).pop()关闭,用户几乎看不到弹窗。 - 弹窗关闭逻辑错误:
Navigator.of(context).pop()使用的是页面的context,而非弹窗自身的context。当页面跳转后,这个context指向已被销毁的页面,无法正确关闭弹窗。 - 未同步弹窗生命周期:弹窗的显示和关闭没有与后续异步操作(保存信息、页面跳转)做好同步,导致执行顺序不可控。
修复方案
调整代码逻辑,确保弹窗生命周期与异步操作同步,同时使用弹窗自身的上下文关闭它:
PrimaryButton( text: 'Sign In', press: () async { if (_formKey.currentState!.validate()) { final message = await AuthService().login( email: _emailController.text.trim(), password: _passwordController.text.trim(), ); if (message!.contains('Success')) { // 显示弹窗并绑定自身上下文 await showDialog( context: context, barrierDismissible: false, // 禁止用户手动关闭弹窗 builder: (BuildContext dialogContext) { // 确保弹窗渲染完成后再执行保存逻辑 WidgetsBinding.instance.addPostFrameCallback((_) async { final currUserID = FirebaseAuth.instance.currentUser!.uid; await saveUserModelToHive(currUserID); await saveSomeInfo(); // 使用弹窗自身上下文关闭弹窗 Navigator.of(dialogContext).pop(); // 弹窗关闭后再执行页面跳转 if (!mounted) return; Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (context) => const LandingPage(), ), ); }); return AlertDialog( title: const Text('Signing you in...'), content: Container( height: 50.0, width: 50.0, color: Colors.red, child: const Center( child: CircularProgressIndicator(), ), ), ); }, ); } else { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), duration: const Duration(seconds: 5), ), ); } } }, ),
关键优化点
- 绑定弹窗上下文:在弹窗的
builder中使用dialogContext关闭弹窗,确保操作的是弹窗自身的导航栈。 - 同步异步操作:通过
WidgetsBinding.instance.addPostFrameCallback确保弹窗渲染完成后再执行保存逻辑,避免弹窗未显示就执行后续操作。 - 禁止手动关闭:设置
barrierDismissible: false,防止用户在保存过程中手动关闭弹窗,保证流程完整性。 - 顺序控制:等待弹窗关闭后再执行页面跳转,确保弹窗始终在旧页面显示,不会出现在新页面中。
内容的提问来源于stack exchange,提问作者ron
相关产品推荐
相关产品推荐

