Flutter获取本地Token实现自动登录时遇No ScaffoldMessenger错误排查
问题描述
尝试通过获取移动设备本地存储的Token实现自动登录,使用Provider进行状态管理,在main文件中获取Token以判断用户是否已登录,但遇到No ScaffoldMessenger widget found错误。
错误信息
E/flutter ( 6547): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: No ScaffoldMessenger widget found. E/flutter ( 6547): MyApp widgets require a ScaffoldMessenger widget ancestor. E/flutter ( 6547): The specific widget that could not find a ScaffoldMessenger ancestor was: E/flutter ( 6547): MyApp E/flutter ( 6547): The ancestors of this widget were: E/flutter ( 6547): _InheritedProviderScope<UserProvider?> E/flutter ( 6547): ChangeNotifierProvider<UserProvider> E/flutter ( 6547): _NestedHook E/flutter ( 6547): MultiProvider E/flutter ( 6547): [root] E/flutter ( 6547): Typically, the ScaffoldMessenger widget is introduced by the MaterialApp at the top of your application widget tree.
相关代码
Main.dart 文件
void main() { runApp(MultiProvider( providers: [ChangeNotifierProvider(create: (context) => UserProvider())], child: const MyApp())); } class MyApp extends StatefulWidget { const MyApp({Key? key}):super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { final AuthService authService = AuthService(); @override void initState() { // TODO: implement initState authService.getUserData(context); super.initState(); } // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'ShopOne', theme: ThemeData( scaffoldBackgroundColor: GlobalVariables.backgroundColor, textTheme: Theme.of(context) .textTheme .apply(bodyColor: Colors.white, displayColor: Colors.white), colorScheme: const ColorScheme.light(primary: GlobalVariables.secondaryColor), appBarTheme: const AppBarTheme( elevation: 0, iconTheme: IconThemeData(color: Colors.white)), // primarySwatch: Colors.blue, ), debugShowCheckedModeBanner: false, onGenerateRoute: ((settings) => generateRoute(settings)), home: Provider.of<UserProvider>(context).user.token.isNotEmpty ? const HomeScreen() : const AuthScreen() ); if (Provider.of<UserProvider>(context).user.token.isNotEmpty) { print('true'); } else { print('false'); ; } } }
获取用户数据函数
void getUserData( BuildContext context, ) async { try { SharedPreferences prefs = await SharedPreferences.getInstance(); String? token = prefs.getString("auth-token"); if (token == null) { prefs.setString('auth-token', ''); } var tokenRes = await http.post(Uri.parse('$uri//tokenisvalid'), headers: <String, String>{ 'Content-Type': 'application/json;charset=UTF-8', 'auth-token': token! }); print('tokenres working 1'); 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', 'auth-token': token }); print('tokenres working 2'); var userProvider = Provider.of<UserProvider>(context, listen: false); userProvider.setUser(userRes.body); } } catch (e) { showSnackBar(context, e.toString()); } }
问题原因与解决方法
核心问题1:ScaffoldMessenger 不存在
initState 阶段调用 authService.getUserData(context) 时,MyApp 内的 MaterialApp 还未完成构建,传入的 context 中没有 ScaffoldMessenger 实例,而异常捕获里的 showSnackBar 依赖该组件,因此抛出错误。
核心问题2:build 方法存在无效代码
return MaterialApp 语句之后的 if-else 判断永远不会执行,因为 return 已经终止了函数执行,这部分属于冗余代码。
具体修复步骤:
延迟调用用户数据获取方法
使用WidgetsBinding.instance.addPostFrameCallback,确保MaterialApp构建完成后再执行逻辑:@override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { authService.getUserData(context); }); }替换全局 SnackBar 调用方式
通过navigatorKey实现不依赖当前context的 SnackBar 显示:- 在
MyApp中定义全局导航键:final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); - 在
MaterialApp中配置该键:MaterialApp( navigatorKey: navigatorKey, // 其他原有配置... ) - 封装全局 SnackBar 方法并替换原调用:
然后在void showGlobalSnackBar(String message) { ScaffoldMessenger.of(navigatorKey.currentContext!).showSnackBar( SnackBar(content: Text(message)), ); }getUserData的 catch 块中调用showGlobalSnackBar(e.toString())。
- 在
移除无效代码
删除build方法中return MaterialApp之后的if-else打印逻辑。优化 Provider 监听逻辑(可选)
使用Consumer组件判断登录状态,避免不必要的组件重建:home: Consumer<UserProvider>( builder: (context, userProvider, child) { return userProvider.user.token.isNotEmpty ? const HomeScreen() : const AuthScreen(); }, )
内容的提问来源于stack exchange,提问作者Rex
相关产品推荐
相关产品推荐

