Search code examples
flutterdartriverpod

Access the ref in a ConsumerStatefulWidget in flutter app


I have a ConsumerStatefulWidget and a ConsumerState class. I want to access my StateNotifierProvider inside the ConsumerState class but I am getting an error on ref saying The instance member 'ref' can't be accessed in an initializer.

I have been working on this for quite a while and I am learning a lot but I thought I could access ref inside the ConsumerState class.

Here is my code:

// Create the global provider
final globalProvider = StateNotifierProvider<GlobalsNotifier, Globals>(
    (ref) => GlobalsNotifier(Globals(
        currentUid: '',
        currentUEmail: '',
        userDocumentId: '',
        companyId: '',
        mlsId: '',
        currentTrxnId: '',
        currentCompanyName: '',
        currentCompanyState: '',
        currentUserId: '',
        currentUserName: '',
        currentUserState: '',
        selectedCompany: '',
        selectedState: '',
        selectedTrxnState: '',
        selectedUser: '',
        selectedUserState: '',
        targetScreen: '',
        newCompany: true,
        newEvent: true,
        newTrxn: true,
        newUser: true)));

class LoginScreen extends ConsumerStatefulWidget {
  static const String id = 'login_screen';

  @override
  ConsumerState<LoginScreen> createState() => _LoginScreenState();
}

class _LoginScreenState extends ConsumerState<LoginScreen> {
  bool showSpinner = false;
  final _auth = FirebaseAuth.instance;

  final globals = ref.read(globalProvider);  <<<< Access ref here

  late String email;
  late String password;

  getCurrentAgencyName() async {
    final DocumentSnapshot _currentAgentProfile =
        await agentsRef.doc(ref.watch(globalProvider.currentUid)).get();

    if (_currentAgentProfile != null) {
      ref.read(GlobalsProvider.companyId.notifier).state =
          _currentAgentProfile.get('agencyId');
      ref.read(GlobalsProvider.currentUserStateProvider.notifier).state =
          _currentAgentProfile.get('state');
      ref.read(GlobalsProvider.mlsId.notifier).state =
          _currentAgentProfile.get('mlsId');
    }

    final DocumentSnapshot _currentAgencyProfile =
        await agencyRef.doc(ref.watch(GlobalsProvider.companyId)).get();

    if (_currentAgencyProfile != null) {
      ref.read(GlobalsProvider.currentCompanyState.notifier).state =
          _currentAgencyProfile.get('state');
      ref.read(GlobalsProvider.currentCompanyName.notifier).state =
          _currentAgencyProfile.get('name');
    }
  }

  @override
  Widget build(BuildContext context) {
    bool loginFail = false;
    String errorMessage = "";

    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: const Row(
          mainAxisAlignment: MainAxisAlignment.center,
          // children: [
          //   Image.asset('assets/images/logo.png',
          //       fit: BoxFit.cover, height: 56),
          // ],
        ),
      ),
      resizeToAvoidBottomInset: false, // This fixes the keyboard white space
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.only(left: 30, right: 30),
          child: Column(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              const Text(
                'Login',
                style: TextStyle(
                  color: Colors.black,
                  fontWeight: FontWeight.w900,
                  fontSize: 40.0,
                ),
                textAlign: TextAlign.center,
              ),
              const SizedBox(
                height: 48.0,
              ),
              TextField(
                autofocus: true,
                keyboardType: TextInputType.emailAddress,
                textAlign: TextAlign.center,
                onChanged: (value) {
                  email = value;
                },
                decoration: InputDecoration(
                  labelText: 'Email',
                  errorText: loginFail ? 'incorrect email' : null,
                ),
              ),
              const SizedBox(
                height: 8.0,
              ),
              TextField(
                obscureText: true,
                textAlign: TextAlign.center,
                onChanged: (value) {
                  password = value;
                },
                decoration: InputDecoration(
                  labelText: 'Password',
                  errorText: loginFail ? 'incorrect passwowrd' : null,
                ),
              ),
              TextButton(
                child: const Text(
                  'Login',
                  style: TextStyle(fontSize: 15),
                ),
                onPressed: () => Navigator.of(context).push(MaterialPageRoute(
                    builder: (context) => ResetPasswordScreen())),
              ),
              TextButton(
                  child: const Text(
                    'Forgot Password',
                    style: TextStyle(fontSize: 15),
                  ),
                  onPressed: () async {
                    setState(() {
                      showSpinner = true;
                    });
                    try {
                      UserCredential userCredential =
                          await _auth.signInWithEmailAndPassword(
                              email: email, password: password);

                      if (userCredential != null) {
                        // Set the global state
                        ref.read(GlobalsProvider.currentUid.notifier).state =
                            _auth.currentUser!.uid;
                        ref.read(GlobalsProvider.currentUserId.notifier).state =
                            _auth.currentUser!.uid;
                        ref.read(GlobalsProvider.currentUEmail.notifier).state =
                            _auth.currentUser!.email;
                        ref.read(GlobalsProvider.targetScreen.notifier).state =
                            0;
                        await getCurrentAgencyName();
                        Navigator.push(
                          context,
                          MaterialPageRoute(builder: (context) => MainScreen()),
                        );
                      } else {
                        setState(() {
                          loginFail = true;
                        });
                      }
                      setState(() {
                        showSpinner = false;
                      });
                    } on FirebaseAuthException catch (error) {
                      switch (error.code) {
                        case "ERROR_INVALID_EMAIL":
                        case "invalid-email":
                          errorMessage =
                              "Your email address appears to be malformed.";
                          break;
                        case "email-already-in-use":
                          errorMessage = "Email is already in use.";
                          break;
                        case "ERROR_WRONG_PASSWORD":
                        case "wrong-password":
                          errorMessage = "Your password is wrong.";
                          break;
                        case "ERROR_USER_NOT_FOUND":
                        case "user-not-found":
                          errorMessage = "User with this email doesn't exist.";
                          break;
                        case "ERROR_USER_DISABLED":
                        case "user-disabled":
                          errorMessage =
                              "User with this email has been disabled.";
                          break;
                        case "ERROR_TOO_MANY_REQUESTS":
                        case "too-many-requests":
                          errorMessage = "Too many requests. Try again later.";
                          break;
                        case "ERROR_OPERATION_NOT_ALLOWED":
                        case "operation-not-allowed":
                          errorMessage =
                              "Signing in with Email and Password is not enabled.";
                          break;
                        default:
                          errorMessage =
                              "An undefined Error happened. Please try again.";
                      }

                      if (errorMessage != null && errorMessage != "") {
                        ScaffoldMessenger.of(context).showSnackBar(
                            (SnackBar(content: Text(errorMessage))));
                      }
                    }
                  }),
              RoundedButton(
                  title: 'Log In',
                  colour: Colors.lightBlueAccent,
                  onPressed: () async {
                    setState(() {
                      showSpinner = true;
                    });
                    try {
                      UserCredential userCredential =
                          await _auth.signInWithEmailAndPassword(
                              email: email, password: password);

                      if (userCredential != null) {
                        ref.read(GlobalsProvider.currentUid.notifier).state =
                            _auth.currentUser!.uid;
                        ref.read(GlobalsProvider.currentUserId.notifier).state =
                            _auth.currentUser!.uid;
                        ref.read(GlobalsProvider.currentUEmail.notifier).state =
                            _auth.currentUser!.email;
                        ref.read(GlobalsProvider.targetScreen.notifier).state =
                            0;
                        await getCurrentAgencyName();
                        Navigator.push(
                          context,
                          MaterialPageRoute(builder: (context) => MainScreen()),
                        );
                      } else {
                        setState(() {
                          loginFail = true;
                        });
                      }
                      setState(() {
                        showSpinner = false;
                      });
                    } on FirebaseAuthException catch (error) {
                      switch (error.code) {
                        case "ERROR_INVALID_EMAIL":
                        case "invalid-email":
                          errorMessage =
                              "Your email address appears to be malformed.";
                          break;
                        case "email-already-in-use":
                          errorMessage = "Email is already in use.";
                          break;
                        case "ERROR_WRONG_PASSWORD":
                        case "wrong-password":
                          errorMessage = "Your password is wrong.";
                          break;
                        case "ERROR_USER_NOT_FOUND":
                        case "user-not-found":
                          errorMessage = "User with this email doesn't exist.";
                          break;
                        case "ERROR_USER_DISABLED":
                        case "user-disabled":
                          errorMessage =
                              "User with this email has been disabled.";
                          break;
                        case "ERROR_TOO_MANY_REQUESTS":
                        case "too-many-requests":
                          errorMessage = "Too many requests. Try again later.";
                          break;
                        case "ERROR_OPERATION_NOT_ALLOWED":
                        case "operation-not-allowed":
                          errorMessage =
                              "Signing in with Email and Password is not enabled.";
                          break;
                        default:
                          errorMessage =
                              "An undefined Error happened. Please try again.";
                      }

                      if (errorMessage != null && errorMessage != "") {
                        ScaffoldMessenger.of(context).showSnackBar(
                            (SnackBar(content: Text(errorMessage))));
                      }
                    }
                  }),
              const SizedBox(
                height: 100.0,
              ),
              TextButton(
                child: const Text(
                  'New User?  Create Account',
                  style: TextStyle(fontSize: 15, color: Colors.blue),
                ),
                onPressed: () => Navigator.of(context).push(MaterialPageRoute(
                    builder: (context) => UserRegisterScreen())),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Am I using the correct class or should I be using something else in place of the StatefulWidget class?

I am obviously not referencing ref in the correct place so where should I access it so I can use it throughout the code?

Thanks


Solution

  • ref is a property of ConsumerState (and its subclasses). As a property, you cannot use it in the initializer of another property, similar to the way that:

    int f = 5;
    int g = f + 2; // not allowed
    

    is not allowed. All initializers are presumed to execute "in a silo", not seeing any neighboring values.

    The proper response is to declare the property as late, and initialize it in an overridden initState, where the remaining properties will be available to use.