0

I'm using Provider with ChangeNotifier to alert a Consumer once a new photo is uploaded to a server replacing an old photo. The problem is that the URL stays the same as the photo is merely overwritten and keeps the same name. Hence the Consumer doesn't recognize that anything has changed and doesn't refresh the old photo with the new one.

How can I trick the ChangeNotifier into refreshing the URL? Heres' the Consumer in my build;

  Consumer<SocialProvider>(
             builder: (context, socialProvider, child) {
               return Image.network(socialProvider.currentavatar,
            );
       }),

Here's where the image is chosen in the Gallery and uploaded to overwrite the old image on the server.

    GestureDetector(
              onTap: () async {
                   await socialProvider.loadCurrentUserId();
                   await _listener.openGallery(socialProvider.currentuserid);
                   String updatedavatar = "http://example.com/same_photo.jpg";
                   socialProvider.updateAvatar(updatedavatar);
                  },

And here's the code in the Provider with ChangeNotifier;

Future<void> updateAvatar(String avatar) async {
     var box = await Hive.openBox('currentuser');
      box.put('currentavatar', avatar);
      currentavatar = avatar;
      notifyListeners();
     }

Any ideas how to trick Consumer into believing the url has changed so that it is refreshed?

Meggy
  • 1,491
  • 3
  • 28
  • 63

1 Answers1

0

I believe the Consumer will rebuild when someone call notifyListeners(). Your issue may be in Image.network(socialProvider.currentavatar,) that flutter reuse same render object when everything is not change. You can try to add key:UniqueLey() to force rebuild the widget every time it build.


Update with some assumption. Here is the simple code I try to rebuild your environment:

class SimpleProvider with ChangeNotifier {
  Future<void> reload() async => notifyListeners();
}

class TestConsumer extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ChangeNotifierProvider(
        create: (context) => SimpleProvider(),
        child: Consumer<SimpleProvider>(
          builder: (_, simpleProvider, child) {
            print('Consumer build');
            return child; // Here is your Image?
          },
          child: Builder(
            builder: (context) {
              print('Builder build');
              return Scaffold(
                floatingActionButton: FloatingActionButton(
                  onPressed: () {
                    context.read<SimpleProvider>().reload();
                  },
                ),
                body: Container(),
              );
            },
          ),
        ),
      ),
    );
  }
}

Every time I click the button, SimpleProvider will call the notifyListeners() (Future is not necessary) and the builder function in Consumer will be called. Here come the questions:

  1. Is the builder really called when you use notifyListeners?

I assume the builder function is called. You can still double check this part. Otherwise you must provide how you call it.

  1. If builder is called, why the Image Widget inside it is not rebuilt?

It is part of flutter design that it reuse as much as it could, for better performance. Sometimes it confuse people that the rebuild is not happen. That's where I guess your problem is here. You can check the basic mechanism from video here: https://www.youtube.com/watch?v=996ZgFRENMs&ab_channel=Flutter

yellowgray
  • 4,006
  • 6
  • 28
  • I tried that along with changing the Consumer to a Hive listener but it still doesn't work. is this what you meant? Image.network('${box.get('currentavatar', defaultValue: 'https://example.com/default,png')}', key:UniqueKey(),; – Meggy Oct 13 '20 at 11:54
  • It does not work with Consumer either- Consumer( builder: (context, socialProvider, child) { return Image.network(snapshot.data[0], key:UniqueKey(), ); } ), – Meggy Oct 13 '20 at 12:29
  • I am a little confuse on your comment that you use `socialProvider.currentavatar` in your question but not in your comment. Your problem is that you want to rebuild the `builder` function in `Consumer` right? Have you check the `builder` is called or not after `notifyListeners()` is called? – yellowgray Oct 13 '20 at 12:49
  • Sorry for the confusion. I've been experimenting with different things including Hive listener, the social provider and a future. I'm back with the original socialProvider.currentavatar. – Meggy Oct 13 '20 at 12:56
  • Here's something interesting I found in an article at medium.com - "Hence unique keys must be used only in stateless widgets where the widgets aren’t depended on internal data change.". So Im guessing thats why the key:UniqueKey() didnt work. – Meggy Oct 13 '20 at 16:38
  • You are correct. `Image.network` is a `Stateless Widget`. The only way to change the value is rebuild it. I edit some content above to explain more detail from my perspective. – yellowgray Oct 14 '20 at 02:35
  • I tried using your code but the reload command doesn't seem to refresh the NetworkImage url. - @override userImage(File _image, String avatar) async { context.read().reload(); print('should have reloaded'); } – Meggy Oct 15 '20 at 16:22
  • Please provide runnable pseudo code to describe the problem. It is really hard to imaging what happen with these pieces of code. – yellowgray Oct 17 '20 at 05:11