如何将AuthService类重构为StatefulWidget并正确处理异步后的BuildContext与mounted
解决「BuildContexts across async gaps」警告:重构AuthService为StatefulWidget
原AuthService类在异步操作后直接使用BuildContext,触发「BuildContexts across async gaps」警告——这是因为异步操作期间Widget可能已被销毁,此时使用无效上下文会引发错误。以下是重构方案:将Auth逻辑迁移至StatefulWidget的State类中,通过mounted属性验证上下文有效性。
重构后的完整代码
import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; import 'package:provider/provider.dart'; import 'dart:convert'; // 假设User、UserProvider、httpErrorHandle、showSnackBar、HomeScreen、uri已在其他文件定义 class AuthHandler extends StatefulWidget { final Widget child; const AuthHandler({super.key, required this.child}); static AuthHandlerState of(BuildContext context) { return context.findAncestorStateOfType<AuthHandlerState>()!; } @override AuthHandlerState createState() => AuthHandlerState(); } class AuthHandlerState extends State<AuthHandler> { // 注册用户 void signUpUser({ required String email, required String password, required String name, }) async { try { User user = User( id: '', name: name, password: password, email: email, address: '', type: '', token: '', // cart: [], ); http.Response res = await http.post( Uri.parse('$uri/api/signup'), body: user.toJson(), headers: const <String, String>{ 'Content-Type': 'application/json; charset=UTF-8', }, ); // 验证Widget是否仍挂载,避免使用无效上下文 if (!mounted) return; httpErrorHandle( response: res, context: context, onSuccess: () { showSnackBar( context, '账户创建成功!请使用相同凭证登录!', ); }, ); } catch (e) { if (!mounted) return; showSnackBar(context, e.toString()); } } // 登录用户 void signInUser({ required String email, required String password, }) async { try { http.Response res = await http.post( Uri.parse('$uri/api/signin'), body: jsonEncode({ 'email': email, 'password': password, }), headers: const <String, String>{ 'Content-Type': 'application/json; charset=UTF-8', }, ); if (!mounted) return; httpErrorHandle( response: res, context: context, onSuccess: () async { SharedPreferences prefs = await SharedPreferences.getInstance(); if (!mounted) return; Provider.of<UserProvider>(context, listen: false).setUser(res.body); await prefs.setString('x-auth-token', jsonDecode(res.body)['token']); if (!mounted) return; Navigator.pushNamedAndRemoveUntil( context, HomeScreen.routeName, (route) => false, ); }, ); } catch (e) { if (!mounted) return; showSnackBar(context, e.toString()); } } // 获取用户数据 void getUserData() async { try { SharedPreferences prefs = await SharedPreferences.getInstance(); String? token = prefs.getString('x-auth-token'); if (token == null) { prefs.setString('x-auth-token', ''); } var tokenRes = await http.post( Uri.parse('$uri/tokenIsValid'), headers: <String, String>{ 'Content-Type': 'application/json; charset=UTF-8', 'x-auth-token': token! }, ); var response = jsonDecode(tokenRes.body); if (response == true) { http.Response userRes = await http.get( Uri.parse('$uri/'), headers: <String, String>{ 'Content-Type': 'application/json; charset=UTF-8', 'x-auth-token': token }, ); if (!mounted) return; var userProvider = Provider.of<UserProvider>(context, listen: false); userProvider.setUser(userRes.body); } } catch (e) { if (!mounted) return; showSnackBar(context, e.toString()); } } @override Widget build(BuildContext context) { return widget.child; } }
关键改动说明
- 移除上下文传递:不再将BuildContext作为方法参数,直接使用State类的
context属性,避免跨异步传递无效上下文 - 挂载状态检查:所有异步操作(
await后)添加if (!mounted) return;,确保Widget仍处于活跃状态才继续使用上下文 - 全局访问入口:通过
AuthHandler.of(context)静态方法,可在任意子Widget中获取AuthHandlerState实例,调用相关方法 - 结构调整:将原AuthService的静态逻辑迁移至StatefulWidget的State类中,符合Flutter的Widget生命周期管理规范
使用方式
在Widget树的上层(如MaterialApp外层)添加AuthHandler:
void main() { runApp( AuthHandler( child: MaterialApp( // ... 其他配置 ), ), ); }
在需要调用Auth方法的Widget中:
// 调用注册 AuthHandler.of(context).signUpUser( email: 'test@example.com', password: '123456', name: 'Test User', ); // 调用登录 AuthHandler.of(context).signInUser( email: 'test@example.com', password: '123456', ); // 获取用户数据 AuthHandler.of(context).getUserData();
内容的提问来源于stack exchange,提问作者marc
相关产品推荐
相关产品推荐

