Search code examples
flutterdartprovider

Unhandled Exception: No ScaffoldMessenger widget found


I'm attempting to enable auto-login by obtaining a token saved in the local mobile instance. I'm encountering this error. I'm using Provider for state management. In my main file , I want to retrieve my token to check whether user has been logged in or not. What i'm, doing wrong ?

My Error

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 file

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');
      ;
    }
  }
}

get user function

  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());
    }
  }

Solution

  • This happened because the context that you used in showSnackBar, doesn't belongs to any Scaffold, you need to separate the MaterialApp's home to new StatefulWidget class like this:

    class ScreenManager extends StatefulWidget {
      const ScreenManager({Key? key}) : super(key: key);
    
      @override
      State<ScreenManager> createState() => _ScreenManagerState();
    }
    
    class _ScreenManagerState extends State<ScreenManager> {
      @override
      void initState() {
        // TODO: implement initState
        authService.getUserData(context);
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Provider.of<UserProvider>(context).user.token.isNotEmpty
              ? const HomeScreen()
              : const AuthScreen(),
        );
      }
    
      void getUserData(BuildContext context) async {
       
    
        try {
    
          ...
    
        } catch (e) {
          WidgetsBinding.instance.addPostFrameCallback((_) {
             showSnackBar(context, e.toString());
          });
        }
      }
    }
    

    then use it like this:

    home: ScreenManager();
    

    Note: don't forget to remove authService.getUserData(context) from _MyAppState's initState.