You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

已加mounted检查仍触发Flutter异步BuildContext错误求助

解决Flutter中"Don't use 'BuildContext's across async gaps"错误

问题分析

你的代码里有两处触发该错误的核心问题:

  1. 错误捕获块未做mounted校验:在_signIn方法的AuthException和通用catch块中,直接使用Theme.of(context)获取主题颜色,但未做mounted检查;同时你仅创建了SnackBar对象,却未调用ScaffoldMessenger显示它,既触发了context安全问题,又导致错误提示无法正常展示。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 13:18:12