flutter – Flutterfire Provider Management With Other Screens

When I try to use Provider to look at whether the user is signed in or not, I’m getting an error. If the user is signed in, I want to go to the profile page. If the user isn’t signed in, I want to go to the login page.

I created a class AuthenticationWrapper that returns to a route. I then navigate through that route.

Here is the error:


    The following ProviderNotFoundException was thrown building AuthenticationWrapper(dirty):
    Error: Could not find the correct Provider<User> above this AuthenticationWrapper Widget
    
    This happens because you used a `BuildContext` that does not include the provider
    of your choice. There are a few common scenarios:
    
    - You added a new provider in your `main.dart` and performed a hot-reload.
      To fix, perform a hot-restart.
    
    - The provider you are trying to read is in a different route.
    
      Providers are "scoped". So if you insert of provider inside a route, then
      other routes will not be able to access that provider.
    
    - You used a `BuildContext` that is an ancestor of the provider you are trying to read.
    
      Make sure that AuthenticationWrapper is under your MultiProvider/Provider<User>.
      This usually happens when you are creating a provider and trying to read it immediately.
    
      For example, instead of:
    
      ```
      Widget build(BuildContext context) {
        return Provider<Example>(
          create: (_) => Example(),
          // Will throw a ProviderNotFoundError, because `context` is associated
          // to the widget that is the parent of `Provider<Example>`
          child: Text(context.watch<Example>().toString()),
        );
      }
      ```
    
      consider using `builder` like so:
    
      ```
      Widget build(BuildContext context) {
        return Provider<Example>(
          create: (_) => Example(),
          // we use `builder` to obtain a new `BuildContext` that has access to the provider
          builder: (context, child) {
            // No longer throws
            return Text(context.watch<Example>().toString());
          }
        );
      }
      ```
    
    If none of these solutions work, consider asking for help on StackOverflow:
    https://stackoverflow.com/questions/tagged/flutter
    
    
    The relevant error-causing widget was
    AuthenticationWrapper

Here is my main.dart:


    import 'package:flutter/material.dart'; 
    
    import 'package:firebase_auth/firebase_auth.dart';
    import 'package:firebase_core/firebase_core.dart';
    import 'firebase_options.dart';
    import 'package:provider/provider.dart';
    
    import 'Screens/login.dart';
    import 'Screens/profile.dart';
    import 'Screens/authentication.dart';
    
    void main () async{
      WidgetsFlutterBinding.ensureInitialized();
      await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform,);
      
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
      @override
    
      Widget build(BuildContext context) {
        return MultiProvider(
          providers: [
            Provider<AuthenticationService>(create: (_) => AuthenticationService(FirebaseAuth.instance),),
            StreamProvider(initialData: 5,create: (context) => context.read<AuthenticationService>().authStateChanges,)],
          child: MaterialApp(
            debugShowCheckedModeBanner: false,
            initialRoute: "https://stackoverflow.com/",
            routes: {
              "https://stackoverflow.com/":(context) => const HomeScreen(),
              '/login': (context) => const LoginScreen(),
              '/profile': (context) => const ProfileScreen(),
              '/userSignIn': (context) => const AuthenticationWrapper()
            },
          )
        );
      }
    }
    
    // Home Screen - First Run
    class HomeScreen extends MyApp {
      const HomeScreen({Key? key}) : super(key: key);
      @override
    
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            elevation: 0,
            bottomOpacity: 0.0,
            flexibleSpace: Container(
            decoration: const BoxDecoration(gradient: LinearGradient(begin: Alignment.topCenter, end:Alignment.bottomCenter, colors: [ Color(0xFFFE6E08), Colors.white]))
            ),
            leading: IconButton(iconSize: 40.0, icon: const Icon(Icons.menu, color: Colors.black), tooltip: "Menu",
              onPressed: () {
                  
              },
            ),
            title: Image.asset('assets/topImage.png',scale: 8),
            centerTitle: true,
            actions: <Widget>[
              IconButton(iconSize: 40.0, icon: const Icon(Icons.account_circle, color: Colors.black,), tooltip: "Click Here to Login or View Profile",
                onPressed: () {
                  Navigator.pushNamed(context, '/userSignIn');
                }, // onPressed
              ),
            ],
          ),
        );
      }
    }
    
    class AuthenticationWrapper extends StatelessWidget {
      const AuthenticationWrapper({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        final firebaseUser = context.watch<User>();
    
        if (firebaseUser != null) {
          return const ProfileScreen();
        }
        return const LoginScreen();
      }
    }

Here is my login.dart: “` import ‘package:flutter/material.dart’; import ‘package:provider/provider.dart’;

import 'authentication.dart';

class LoginScreen extends StatelessWidget {
  const LoginScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return WillPopScope(onWillPop: () async => false, child: 
      Scaffold(
        appBar: AppBar(
          elevation: 0,
          bottomOpacity: 0.0,
          flexibleSpace: Container(
          decoration: const BoxDecoration(
            gradient: LinearGradient(begin: Alignment.topCenter, end:Alignment.bottomCenter, colors: [ Color(0xFFFE6E08), Colors.white]),
          ),
          ),
          leading: IconButton(iconSize: 40.0, icon: const Icon(Icons.menu, color: Colors.black), tooltip: "Menu",
            onPressed: () {
              
            },
          ),
          title: IconButton(icon:Image.asset('assets/topImage.png'), iconSize: 65, 
            onPressed: () {
              Navigator.pop(context);
            },
          ),
          centerTitle: true,
        ),
        body: const Inputs()
      )
    );
  }
}

class Inputs extends StatefulWidget {
  const Inputs({Key? key}) : super(key: key);

  @override
  State<Inputs> createState() => _InputsState();
}

class _InputsState extends State<Inputs> {
  final emailController = TextEditingController();
  final passwordController = TextEditingController();

  @override
  void dispose() {
    emailController.dispose();
    passwordController.dispose();
    super.dispose();
  }

  bool userauth(email, password) {
    return false;
  } 

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Column(
          children: <Widget>[
            // Top Image
            Padding(
              padding: const EdgeInsets.only(top: 60.0),
              child: Center(
                child: SizedBox(
                  width: 200,
                  height: 150,
                  child: Image.asset('assets/topImage.png')
                ),
              )
            ),
            // Email Input
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 15),
              child: TextField(
                controller: emailController,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: 'Email',
                  hintText: 'Enter valid email id as abcgamil.com'
                )
              ),
            ),
            // Password Input
            Padding(
              padding: const EdgeInsets.only(
                left: 15.0, right: 15.0, top: 15, bottom: 0
              ),
              child: TextField(
                controller: passwordController,
                obscureText: true,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: 'Password',
                  hintText: 'Enter Secure Password'
                ),
              )
            ),
              // Forgot Password Text
              TextButton(
                onPressed: (){
                  // Add Forgot Password Thing Here
                },
                child: const Text('Forgot Password', style: TextStyle(color: Colors.black, fontSize: 15),),
              ),
              // Submit Box
              InkWell(
                onTap: () {
                  print(emailController.text);
                  print(passwordController.text);
                  emailController.text = "";
                  passwordController.text = "";

                  context.read<AuthenticationService>().signIn(
                    email: emailController.text.trim(),
                    password: passwordController.text.trim(),
                  );
                },
                child: Ink(
                  height: 50, 
                  width: 250, 
                  decoration: BoxDecoration(
                    color: Colors.black, 
                    borderRadius: BorderRadius.circular(20)
                  ),
                  child: const Center(child: Text("Sign In", style: TextStyle(color: Colors.white)))
                )
              ),
              // Box for spacing
              const SizedBox(height: 100),
              // Text for Sign Up
              const Text('New User? Create Account')
          ,]
        ),
      )
    );
  }
}





Here is my authentication.dart:





import 'package:firebase_auth/firebase_auth.dart';

class AuthenticationService {
  final FirebaseAuth _firebaseAuth;

  AuthenticationService(this._firebaseAuth);

  /// Changed to idTokenChanges as it updates depending on more cases.
  Stream<User?> get authStateChanges => _firebaseAuth.idTokenChanges();

  /// This won't pop routes so you could do something like
  /// Navigator.of(context).pushNamedAndRemoveUntil("https://stackoverflow.com/", (Route<dynamic> route) => false);
  /// after you called this method if you want to pop all routes.
  Future<void> signOut() async {
    await _firebaseAuth.signOut();
  }

  /// There are a lot of different ways on how you can do exception handling.
  /// This is to make it as easy as possible but a better way would be to
  /// use your own custom class that would take the exception and return better
  /// error messages. That way you can throw, return or whatever you prefer with that instead.
  Future<String?> signIn({required String email, required String password}) async {
    try {
      await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
      return "Signed in";
    } on FirebaseAuthException catch (e) {
      return e.message;
    }
  }

  /// There are a lot of different ways on how you can do exception handling.
  /// This is to make it as easy as possible but a better way would be to
  /// use your own custom class that would take the exception and return better
  /// error messages. That way you can throw, return or whatever you prefer with that instead.
  Future<String?> signUp({required String email, required String password}) async {
    try {
      await _firebaseAuth.createUserWithEmailAndPassword(email: email, password: password);
      return "Signed up";
    } on FirebaseAuthException catch (e) {
      return e.message;
    }
  }
}
```

Thanks for the help 🙂

Leave a Comment