steveny909
steveny909

Reputation: 105

Provider NotifyListeners not updating consumers

I'm pretty new to flutter and really, really want to use provider to pass data around, but I just can't get it to work. I think I have everything set right, but for some reason when I notify listeners, my app doesn't refresh. All I'm trying to do is update a profile pic. I start with a pic that is in firebase (works fine). Then I give the user the option to update their pic with camera or photos. When they choose a new pic, the consumer doesn't update like I would expect it to. Can someone please tell me if I'm doing something wrong or how I may be able to accomplish this?

First, here is my main:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<Storage>(create: (context) => Storage()),
        StreamProvider<Uid>.value(value: AuthService().user),   
      ],
      child: MaterialApp(   ....

Then here is the class and method that updates the photo URL which I call from an image picker class. I'm passing context so I can pop that image picker view and go back to the profile page after it's done.

class Storage with ChangeNotifier {
  String userPhotoUrl;
  void uploadImage(PickedFile _imageFile, BuildContext context) async {

      ....     //some other code here

      try {
        var snapshot =
            await _storage.ref().child('/profileImages/$_userId').putFile(file);

        await snapshot.ref.getDownloadURL().then((value) {
          userPhotoUrl = value;
          notifyListeners();
          print(userPhotoUrl); //I know the code is working because it prints here
          Navigator.pop(context); 
        });
      } catch (e) {
        print(e);
     
      }
   }

}

And here is the profile page that I'm trying to update. It's just not getting the updated value.

class Profile extends StatefulWidget {
  Profile({Key key}) : super(key: key);

  @override
  _ProfileState createState() => _ProfileState();
}

/// This is the private State class that goes with MyStatefulWidget.
class _ProfileState extends State<Profile> with TickerProviderStateMixin {


     String imageUrl;  //firebase pic to start - works fine

@override
  Widget build(BuildContext context) {

                       ....//some more widget tree

                 Consumer<Storage>(   
                  builder: (context, storage, widget) =>
                     CircularProfileAvatar(            //never updates. userPhotoURL always null
                        storage.userPhotoUrl ?? imageUrl, //always uses imageUrl,
                        elevation: 20,
                        borderColor: Colors.white70,
                        borderWidth: 1,
                        radius: 50,
                         ),
                     ),

Any idea as to why it's not working? I would think this should be easy but provider is still alluding me...

Upvotes: 4

Views: 3128

Answers (1)

Toheeb
Toheeb

Reputation: 1713

You are using the wrong approach to solve the problem you need to do some like this

Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => ChangeNotifierProvider<Storage>(
            create: (context) => Storage(),
            child: ImageGrabberScreen(),
    ),
  ),
);

When navigating to the screen you want to use the Consumer() widget

By using Provider or MultipleProvider as parent you're telling flutter you want to use StreamBuilder()widget but from your work I see you're using the Consumer() widget

Also try to do trigger your function doing the below

Widget build(BuildContext context) {
 var storageNotifier = Provider.of<Storage>(context);
  ///In your button you can then do storageNotifier.uploadImage();
}

This type of problem is best solved with FutureBuilder if you don't have any form of button that is meant to trigger this event

Future<String> uploadImage() async {

  ....     //some other code here

    var snapshot =
        await _storage.ref().child('/profileImages/$_userId').putFile(file);

  return  await snapshot.ref.getDownloadURL();
  
}

Then somewhere in the widget tree do this

FutureBuilder(
  future: uploadImage(),
  builder: (_,snapshot){
    if(snapshot.hasData){
       return CircularProfileAvatar(snapshot.data);
    }else{
     return CircularProfileAvatar(imageUrl);
    }
  })
 ...widget tree continues

Upvotes: 1

Related Questions