Flutter/Dart - How to update Image after Image Picker on other Screens

3.1k views Asked by At

I'm using Flutter's Image Picker plugin in order to allow the user to change their avatar. When they go to their account page they see their regular avatar photo with a camera icon on top. Clicking the camera icon will allow them to either take a photo from their camera or choose a new avatar from their gallery. After choosing the new one, the avatar photo automatically updates. However, when navigating away from their account page, the old avatar is visible throughout the rest of the app. I'm using Provider with a Change Notifier and Consumers for Avatars everywhere else. The problem though is that I can only access the Provider within a build so I don't know where I can call the Provider in my code. Add to this the fact that the Avatar I'm using all around the app comes from an internet url. After choosing with Image Picker, the new avatar photo gets uploaded to a server. The name of the new photo replaces the name of the old photo. Hence my app doesn't even know anything changed. Even reloading the pages doesn't work. However if I hot restart my app, the new avatar photo appears. Any ideas what I can do?

Here's the Image Picker code;

class Picker extends StatefulWidget {
  Picker({Key key, this.title}) : super(key: key);
  final String title;

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

class _PickerState extends State<Picker>
    with TickerProviderStateMixin,ImagePickerListener{

  File _image;
  AnimationController _controller;
  ImagePickerHandler imagePicker;

  @override
  void initState() {
    super.initState();
    _controller =  AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 500),
    );

    imagePicker= ImagePickerHandler(this,_controller);
    imagePicker.init();

  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    var socialProvider = Provider.of<SocialProvider>(context);
    return  Container(
      child:  GestureDetector(
        onTap: () => imagePicker.showDialog(context),
        child:  Center(
            child: Stack(
              children: <Widget>[
                Center(
                      child: _image == null?
                          Consumer<SocialProvider>(
                          builder: (context, socialProvider, child) {
                            return
                              Image.network(socialProvider.currentavatar,
                              width: 200,
                              height: 200,
                            );
                          }) :
                              Container(
                              height: 200.0,
                              width: 200.0,
                              decoration:  BoxDecoration(
                              color: Colors.grey,
                              image:  DecorationImage(
                              image:  FileImage(_image),
                              fit: BoxFit.cover,
                          ),                              
                        ),
                      ),
                ),

                Center(
                  child: ClipRRect(
                    borderRadius: BorderRadius.circular(20.0),
                    child: Container(
                      color: Colors.black26,
                      child: Icon(Icons.camera_alt,
                        color: Colors.white,
                        size: 40,
                      ),
                    ),
                  ),
                ),
               ,
            )
        ),
      ),
    );
  }

  @override
  userImage(File _image) async{
     setState(() {
      this._image = _image;
    });
  }
} 

Currently the Consumers are correctly updating the avatars throughout the app whenever a user obtains a new avatar through logging in via social media. The new avatar is uploaded to the server and the ChangeNotifier is informed. The code for the Provider here is ;

Future<void> postSocialData(String avatar) async {
    final url = "http://example.com/example.php&currentavatar=" + $avatar;
    final response = await http.get(url);

    if (response.statusCode == 200) {
      currentavatar = "http://example.com/user.jpg";      
      var box = await Hive.openBox('currentuser');
      box.put('currentavatar', "http://example.com/user.jpg",);
      notifyListeners();    
    } 
  }

So I tried putting this into my Provider and calling it from an onTap function in the Image Picker build. Here's the onTap function;

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

                  child: roundedButton(
                      "Gallery",
                      EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 0.0),
                      const Color(0xFF167F67),
                      const Color(0xFFFFFFFF)),
                ),

And here's the Provider it calls;

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

But that didn't update the consumers with the new avatar. I guess because the external url for the avatar hasn't changed as the photo has simply been replaced and keeps the same name.

1

There are 1 answers

0
Meggy On BEST ANSWER

Using Hive's listener was a good idea. But it didn't help because it turns out the uploaded image - having the same URL as the replaced image - isn't being refreshed on the server-side. So the caching must be sorted out on the web server.