0

After the user changes the data, the UI doesn't update the data with Provider. The API calls are always statusCode: 200 but the UI is not updating unless I refresh/build the page again. I assume that the setting state method is not set up correctly by me but I'm not sure what I did wrong. The code:

Method for editing profile info:

 Map<String, dynamic> userData;
@override
  Widget build(BuildContext context) {
UserData user = UserData.fromJson(userData);

return TextButton(
        onPressed: () async {
             changeUserInfo(user) // I pass the user object to the method
               .whenComplete(() => Navigator.of(context).pop());
          },
            child: Text(
             'SAVE',
             ),
       );
}

Change user method:

 Future<void> changeUserInfo(UserData user) async {
    final userRes = await patchUserInfo(user); // API CALL WHICH ALWAYS GOES THROUGH
    if (userRes != null) {
 // HERE IS WHERE I WANT TO SET THE CURRENT STATE OF THE USER THAT HAS BEEN CHANGED
     userStore.setUserInfo(user);  
      return;
    }
  }

The User Store:

class UserStore extends ChangeNotifier {

  Map<String, UserData> _userInfo = {};
  Map<String, UserData> get userInfo => _userInfo ;

 void setUserInfo(UserData user) {
    _userInfo[user.id] = user; // I assume this is where I do something wrong?!
    notifyListeners();
  }

UserData  getUserInfoByUserId(String userId) => _userInfo[userId];
}

The is the JSON from the API returned after patch:

{
    "statusCode": 200,
    "message": "Success",
    "data": {
        "id": "1",
        "email": "johndoe@johndoe.com",
        "firstName": "John",
        "lastName": "Doe",
    }
}

And my User Model:

class UserData {
  String id, firstName,
      lastName,
      email;

  UserData({
    this.id,
    this.firstName,
    this.lastName,
    this.email,
  });

  UserData.fromJson(Map<String, dynamic> json) {
    id = json['id'];
    firstName = json['firstName'];
    lastName = json['lastName'];
    email = json['email'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = Map<String, dynamic>();
    data['id'] = this.id;
    data['firstName'] = this.firstName;
    data['lastName'] = this.lastName;
    data['email'] = this.email;

    return data;
  }
}

Let's say the screen where I should see the changes (I tried even with Consumer, so it is the same):

class SomeClassExample extends StatelessWidget {
final userStore = Provider.of<UserStore>(context, listen: false);
    UserData user = userStore.getUserInfo(userId);
...
return Column(
children: [
    Text(user.firstName),
    Text(user.lastName),
   ],
);

}

The way it is set now (or at least I think so), I pass the user object to the changeUserInfo method, where the object is passed to the API call and if the returned API call is not null, set the current state of the user info with the passed object. Then in the store I pass that object into my UserData map, notify the listeners, update it where it is listened and that's it. I'm not sure what I did wrong.

Thanks in advance for you help.

GrandMagus
  • 600
  • 3
  • 12
  • 37
  • Hello. Could you please paste your whole widget which should Consume your ChangeNotifier ? You removed too much code, and in it's current state, there is no way your SomeClassExample widget can listen to any changes. – FDuhen Mar 01 '22 at 19:56
  • 1
    At the line `final userStore = Provider.of(context, listen: false);`. Use `listen: true` instead. That should make it update. – Swanav Mar 02 '22 at 03:23
  • @Swanav So, I tried literally everything, and last night I realized I wasn't listening to the changes, but got mad before that and started a bounty... And this morning, you have the same answer. :D Please write this as an answer so I can give you the `+50 rep` bounty! Thank you for your help! – GrandMagus Mar 02 '22 at 08:20
  • @GrandMagus Ahaa! We've all been there! – Swanav Mar 02 '22 at 08:35

3 Answers3

1

At this line:

final userStore = Provider.of<UserStore>(context, listen: false);

Use listen: true instead.

That should make it update.

As per the docs, the listen: false flag allows you to read a value from the Provider Store without observing it for changes. It is commonly used to optimise the application to avoid unnecessary rebuilding.

Swanav
  • 387
  • 1
  • 2
  • 12
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 02 '22 at 09:20
0

Why don't you wrap your Column inside a Consumer widget that listens to the UserStore.

Did you do it like this:

// remove fetching the provider using Provider.of;
// the Consumer widget is taking care of that for you.

return Consumer<UserStore>(
    builder: (context, userStore, child) {
        UserData user = userStore.getUserInfo(userId);
        
        return Column(
          children: [
             Text(user.firstName),
             Text(user.lastName),
          ],
        );
    }
);

every time you call notifyListeners on your UserStore it should cause this widget to rebuild itself, displaying the changes. Now, you're not showing where you're getting the userId either, which I'm wondering now as well.

Roman Jaquez
  • 2,499
  • 1
  • 14
  • 7
  • I just shown the minimal code required to reproduce the flow and the output problem. Like I mentioned in the question, I have also tried with Consumer and that is not working also. Thanks for the help tho. – GrandMagus Feb 18 '22 at 08:46
0

The line you highlighted

Your JSON and your UserData object use a UserData.id member but the line highlighted _userInfo[user.userId] = user; uses userId. If the class definition provided is correct then it should be user.id.

Other Provider stuff, maybe

You can use context.watch instead of Provider.of, but if Consumer doesn't work then it's not likely to change anything. (Provider as written in the code block won't trigger a re-render)

final userStore = context.watch<UserStore>();
    UserData user = userStore.getUserInfo(userId);
...
return Column(
children: [
    Text(user.firstName),
    Text(user.lastName),
   ],
);

Remember to insert a Provider into the Widget Tree

Perhaps make sure that Provider is properly inserted into the widget tree - the code samples above don't include that exact code. If it's missing then an approximation of what that should look like is as follows:

ChangeNotifierProvider(create: (context) => UserStore(), SomeParentWidget())
Friskod
  • 51
  • 3
  • The id is a typo which I changed it. I tried with `watch` but like you said yourself, if Consumer isn't working, this changes nothing.. And yes, I have inserted the Provider into the Widget Tree. Thanks for the help. – GrandMagus Feb 18 '22 at 08:48