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

如何在StatelessWidget中修复「Do not use BuildContexts across async gaps」错误?

问题

在FireAuth登录流程中,我需要将用户实例存储到ChangeNotifier类型的appState中,执行代码如下:

Provider.of<StateModel>(context, listen: false).signedIn = shokazeUser;

但shokazeUser需要通过await从远程数据库获取,这会导致context可能失效,触发错误:

Do not use BuildContexts across async gaps.

当前使用的是StatelessWidget,无法像StatefulWidget那样通过判断mounted状态处理,请问该如何解决?

我的代码如下:

Future<String?> _authUser(BuildContext context, LoginData data) {
    debugPrint('Name: ${data.name}, Password: ${data.password}');

    return auth
        .signInWithEmailAndPassword(email: data.name, password: data.password)
        .then((credential) async {
      try {
        final user = credential.user;

        if (user == null) {
          return 'Password does not match';
        } else {
          // == Tell the app we just signed in

          try {
            final shokazeUser = await shokaze.User.getFromId(user.uid);
            if (shokazeUser == null) {
              return "Unable to fetch user from database";
            }
            Provider.of<StateModel>(context, listen: false).signedIn =
                shokazeUser;
          } catch (e) {
            return "Error while fetching user from database: $e";
          }
          return null;
        }
      } catch (e) {
        return 'User not exists';
      }
    }).catchError((e) {
      if (e.code == 'weak-password') {
        debugPrint('The password provided is too weak.');
      } else if (e.code == 'email-already-in-use') {
        debugPrint('The account already exists for that email.');
      } else {
        debugPrint(e.toString());
      }

      return Future<String?>.error(e);
    });

}
解决方案

方法1:使用全局Provider容器获取实例(不依赖BuildContext)

如果StateModel在顶层MultiProvider中注册,可通过全局保存的ProviderContainer直接获取实例,彻底避开BuildContext。

实现步骤:

  1. 在主入口文件保存容器引用:
void main() {
  final container = ProviderContainer();
  runApp(
    UncontrolledProviderScope(
      container: container,
      child: const MyApp(),
    ),
  );
  // 将容器存到全局类中
  GlobalProviderContainer.instance = container;
}

class GlobalProviderContainer {
  static late ProviderContainer instance;
}
  1. 在登录方法中替换原Provider.of代码:
// 无需context,直接更新状态
GlobalProviderContainer.instance.read<StateModel>().signedIn = shokazeUser;

方法2:将登录逻辑抽离到独立服务类

把FireAuth和用户数据获取逻辑迁移到不依赖Widget的服务类中,通过构造函数注入StateModel,避免使用BuildContext。

示例代码:

class AuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;
  final StateModel _stateModel;

  AuthService(this._stateModel);

  Future<String?> signIn(LoginData data) async {
    debugPrint('Name: ${data.name}, Password: ${data.password}');
    try {
      final credential = await _auth.signInWithEmailAndPassword(
        email: data.name,
        password: data.password,
      );
      final user = credential.user;
      if (user == null) {
        return 'Password does not match';
      }
      try {
        final shokazeUser = await shokaze.User.getFromId(user.uid);
        if (shokazeUser == null) {
          return "Unable to fetch user from database";
        }
        _stateModel.signedIn = shokazeUser;
        return null;
      } catch (e) {
        return "Error while fetching user from database: $e";
      }
    } catch (e) {
      if (e is FirebaseAuthException) {
        if (e.code == 'weak-password') {
          debugPrint('The password provided is too weak.');
        } else if (e.code == 'email-already-in-use') {
          debugPrint('The account already exists for that email.');
        }
      }
      debugPrint(e.toString());
      return e.toString();
    }
  }
}

在Widget中调用服务:

final authService = Provider.of<AuthService>(context, listen: false);
final error = await authService.signIn(data);

方法3:使用context.mounted(Dart 3.0+)

如果项目使用Dart 3.0及以上版本,StatelessWidget的BuildContext也支持mounted属性,可直接判断Widget是否仍挂载:

final shokazeUser = await shokaze.User.getFromId(user.uid);
if (shokazeUser == null) {
  return "Unable to fetch user from database";
}
// 仅在Widget挂载时更新状态
if (context.mounted) {
  Provider.of<StateModel>(context, listen: false).signedIn = shokazeUser;
}

内容的提问来源于stack exchange,提问作者Stéphane de Luca

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 12:40:41