I am using providers to manage state in my flutter app. I have a dialogue that should initially show a CircularProgressBar and then later on some content. I am calling notifyListeners() when I need the CircularProgressBar to change to the content, but that doesn't seem to do anything. Here is some of my code, let me know if you need to see anymore.

The method that is called to display the dialogue:

displayDialog() {
  return showDialog(
    context: context,
    barrierDismissible: false,
    builder: (BuildContext context) {
      return ChangeNotifierProvider<CreateAccountProvider>(
        builder: (_) => CreateAccountProvider(),
        child: CreateNewUserDialog(),
      );
    },
  );
}

final createAccountButton = Material(
  elevation: 5.0,
  color: Colors.lightGreen,
  child: MaterialButton(
    minWidth: MediaQuery.of(context).size.width,
    padding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
    onPressed: () {
      createAccountProvider.createAccountClicked(context);
      displayDialog();
    },
    child: Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Icon(
          Icons.play_arrow,
          color: Colors.white,
        ),
        SizedBox(
          width: 5,
        ),
        Text(
          "Create an account",
          textAlign: TextAlign.center,
          style: TextStyle(color: Colors.white),
        ),
      ],
    ),
  ),
);

The relevant code in the provider:

// initial values for loading and loadingMessage
bool loading = false;
String loadingMessage;

void createAccountClicked(BuildContext context) {
  print("Create account clicked");
  validateAllFields();

  if (name != null &&
      email != null &&
      password != null &&
      confirmPassword != null) {

    loading = true;
    loadingMessage = "Creating new user";
    notifyListeners(); // <- nothing happens when this is called :(

    RestEndpoints.createNewUser(name, email, password, confirmPassword, "on")
        .then(createUserSuccess, onError: createUserError);
  }
}

void createUserSuccess(response) {
  print(response.data);
  if (response.data["status"] == "OK") {
    loading = false;
    notifyListeners();
  }
}

void createUserError(error) {
  loading = false;
  print("Error creating new user: ${error.toString()}");
  notifyListeners();
}

Relevant bits of the Dialog:

@override
Widget build(BuildContext context) {
  final createAccountProvider = Provider.of<CreateAccountProvider>(context);

  // ...

  return AlertDialog(
    content: Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        Visibility(
          visible: createAccountProvider.loading, //<-- this is never visible
          child: Loading(createAccountProvider.loadingMessage),
        ),
        Visibility(
          visible: !createAccountProvider.loading, //<-- this is always visible
          child: SingleChildScrollView(
            child: Column(
              children: <Widget>[
                SizedBox(height: 30),
                title,
                SizedBox(height: 30),
                subText,
                SizedBox(height: 30),
                confirmCodeTextField,
                SizedBox(height: 50),
                infoBox,
                newVerificationCode,
                continueButton
              ],
            ),
          ),
        ),
      ],
    ),
  );
}

Question: how do I get my dialogue to update when the notifyListeners() is called?


Solution 1: Konstantin Kozirev

After this line:

notifyListeners(); // <- nothing happens when this is called :(

You have a line that can potentially immediately call loading = false; here:

RestEndpoints.createNewUser(name, email, password, confirmPassword, "on")
        .then(createUserSuccess, onError: createUserError);

Probably you have some kind of error, so createUserError might be invoked immediately, which sets loading = false; here:

void createUserError(error) {
  loading = false;
  print("Error creating new user: ${error.toString()}");
  notifyListeners();
}

Try to run your code without this line:

RestEndpoints.createNewUser(name, email, password, confirmPassword, "on")
        .then(createUserSuccess, onError: createUserError);