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

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 已经终止了函数执行,这部分属于冗余代码。

具体修复步骤:

  1. 延迟调用用户数据获取方法
    使用 WidgetsBinding.instance.addPostFrameCallback,确保 MaterialApp 构建完成后再执行逻辑:

    @override
    void initState() {
      super.initState();
      WidgetsBinding.instance.addPostFrameCallback((_) {
        authService.getUserData(context);
      });
    }
    
  2. 替换全局 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())。
  3. 移除无效代码
    删除 build 方法中 return MaterialApp 之后的 if-else 打印逻辑。

  4. 优化 Provider 监听逻辑(可选)
    使用 Consumer 组件判断登录状态,避免不必要的组件重建:

    home: Consumer<UserProvider>(
      builder: (context, userProvider, child) {
        return userProvider.user.token.isNotEmpty 
            ? const HomeScreen() 
            : const AuthScreen();
      },
    )
    

内容的提问来源于stack exchange,提问作者Rex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 02:35:48