已加mounted检查仍触发Flutter异步BuildContext错误求助
解决Flutter中"Don't use 'BuildContext's across async gaps"错误
问题分析
你的代码里有两处触发该错误的核心问题:
- 错误捕获块未做mounted校验:在
_signIn方法的AuthException和通用catch块中,直接使用Theme.of(context)获取主题颜色,但未做mounted检查;同时你仅创建了SnackBar对象,却未调用ScaffoldMessenger显示它,既触发了context安全问题,又导致错误提示无法正常展示。 - Auth状态监听的异步回调存在context失效风险:
initState中订阅的supabase.auth.onAuthStateChange是异步Stream,回调可能在Widget已经被dispose(比如用户退出页面后)才执行,此时使用Navigator.of(context)会因context已失效触发错误。
解决方案
1. 修复错误捕获块的Context使用
给两个catch块添加mounted检查,并正确调用ScaffoldMessenger.showSnackBar:
on AuthException catch (error) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(error.message), backgroundColor: Theme.of(context).colorScheme.error, ), ); } } catch (error) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('Unexpected error occurred'), backgroundColor: Theme.of(context).colorScheme.error, ), ); } }
2. 修复Auth状态监听的Context安全问题
在Stream回调中先检查mounted,确保Widget仍在树中再使用context进行导航:
@override void initState() { _authStateSubscription = supabase.auth.onAuthStateChange.listen((data) { if (_redirecting) return; final session = data.session; if (session != null && mounted) { // 添加mounted检查 _redirecting = true; Navigator.of(context).pushReplacementNamed('/account'); } }); super.initState(); }
完整修改后的代码
class _LoginPageState extends State<LoginPage> { bool _isLoading = false; bool _redirecting = false; late final TextEditingController _emailController = TextEditingController(); late final StreamSubscription<AuthState> _authStateSubscription; Future<void> _signIn() async { try { setState(() { _isLoading = true; }); await supabase.auth.signInWithOtp( email: _emailController.text.trim(), emailRedirectTo: kIsWeb ? null : 'com.example.app://login-callback/', ); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Check your email for a login link!')), ); _emailController.clear(); } } on AuthException catch (error) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(error.message), backgroundColor: Theme.of(context).colorScheme.error, ), ); } } catch (error) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('Unexpected error occurred'), backgroundColor: Theme.of(context).colorScheme.error, ), ); } } finally { if (mounted) { setState(() { _isLoading = false; }); } } } @override void initState() { _authStateSubscription = supabase.auth.onAuthStateChange.listen((data) { if (_redirecting) return; final session = data.session; if (session != null && mounted) { _redirecting = true; Navigator.of(context).pushReplacementNamed('/account'); } }); super.initState(); } @override void dispose() { _authStateSubscription.cancel(); _emailController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Sign In')), body: ListView( padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 12), children: [ const Text('Sign in via the magic link with your email below'), const SizedBox(height: 18), TextFormField( controller: _emailController, decoration: const InputDecoration(labelText: 'Email'), ), const SizedBox(height: 18), ElevatedButton( onPressed: _isLoading ? null : _signIn, child: Text(_isLoading ? 'Loading' : 'Send Magic Link'), ), ], ), ); } }
额外说明
mounted是State类的内置属性,用于判断当前State对象是否仍关联在Widget树中,所有异步操作(如await、Stream回调)后使用context的代码,都必须包裹在if(mounted)判断中,避免使用已失效的context。
内容的提问来源于stack exchange,提问作者Marcu Vlad
相关产品推荐
相关产品推荐

