Search code examples
firebaseflutterdartdart-null-safety

Declaring a variable with no initialiser in Flutter, with null safety


I'm trying to implement Firebase authentication into my app with Flutter and my coding worked fine initially, although since I have migrated the project to null safety the authentication no longer works. I think I have 'isolated' where that is.

For example, if we take my password reset TextField, this is what the TextField looks like in terms of the code:

  TextField _buildEmailTextField() {
    return TextField(
      controller: _emailController,
      focusNode: _emailFocusNode,
      decoration: InputDecoration(
        labelText: 'Email',
        hintText: 'email@address.com',
        errorText: model.emailErrorText,
        enabled: model.isLoading == false,
      ),
      autocorrect: false,
      keyboardType: TextInputType.emailAddress,
      textInputAction: TextInputAction.done,
      onChanged: model.updateEmail,
      onEditingComplete: _submit,
    );
  }

Of course, to handle changes to the TextField, you can see that I have added an updateEmail function onChanged: model.updateEmail. The function itself looks like this:

void updateEmail(String email) => updateWith(email: email);

And the updateWith function looks like this:

  void updateWith({
    String email = '',
    bool isLoading = false,
    bool submitted = false,
  }) {
    this.email = email;
    this.isLoading = isLoading;
    this.submitted = submitted;
    notifyListeners();
  }

Prior to null safety, I didn't need to initialise the variables in updateWith with a value, so it would have looked like this:

  void updateWith({
    String email,
    bool isLoading,
    bool submitted,
  }) {
    this.email = email;
    this.isLoading = isLoading;
    this.submitted = submitted;
    notifyListeners();
  }

The error from Firebase is:

[firebase_auth/missing-email] An email address must be provided

And I think this is caused by the fact that I am now having to initialise the email variable in updateWith, and I am using an empty string. Therefore, even though I am typing in an email into the TextField, when I submit, it submits the empty string, so that Firebase tells me I haven't submitted an email. I have tried it instead using email as a nullable value, like so:

  void updateWith({
    String? email,
    bool isLoading = false,
    bool submitted = false,
  }) {
    this.email = email!;
    this.isLoading = isLoading;
    this.submitted = submitted;
    notifyListeners();
  }

But then that gives me the error:

Null check operator used on a null value

How do I make my updateWith function work now that I have migrated to null safety? As I say, it worked fine prior to null safety, so I am confident with the logic. It's just that having to initialise email with a variable within the function now means that initial variable is submitted to firebase, rather than the typed-in email address. Is there a way to make this work, or a way I can rewrite my updateEmail and updateWith functions, so that they don't have to be initialised with a value?

If you need me to provide any additional information, I would be happy to. Thanks for the advice!


Solution

  • The method is very unintuitive for Dart/Flutter developers, unless you do it using the common pattern. The point of those optional parameters in methods like this normally is to pass only a few of them, leaving the rest untouched. So for example, you could call updateWith(email: 'test@example.com') and only the email will be updated but isLoading and submitted will remain unchanged. You can only achieve this if the parameters are nullable, so you have a marker/value for "wasn't set at all" that you can check for.

    For example:

    void updateWith({
        String? email,
        bool? isLoading,
        bool? submitted,
      }) {
        this.email = email ?? this.email;
        this.isLoading = isLoading ?? this.isLoading;
        this.submitted = submitted ?? this.submitted;
    
        notifyListeners();
      }
    

    If email is a required parameter and the other two will be set to defaults anyway, you don't need named parameters at all. Just make it a normal method with normal positional parameters.