coderhelp
coderhelp

Reputation: 81

How to update picture in Storage and refresh Streambuilder to show the new picture?

I am facing the issue of updating profile picture in my Flutter App. I have displayed the current logged in user's details like this :

        body: StreamBuilder<Object>(
            stream: FirebaseFirestore.instance
                .collection('users')
                .doc(loggedInUser)
                .snapshots(),
            builder: (context, snapshot) {
              if (!snapshot.hasData) {
                return Center(
                  child: CircularProgressIndicator(),
                );
              } else if (snapshot.data != null) {
                final userData = snapshot.data as DocumentSnapshot;
                return Column(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Container(
                      decoration: BoxDecoration(
                        color: Colors.grey[200],
                      ),
                      width: MediaQuery.of(context).size.width / 1,
                      height: MediaQuery.of(context).size.width / 2.5,
                      child: CachedNetworkImage(
                        imageUrl: '${userData['image']}',
                        imageBuilder: (context, imageProvider) => Container(
                          width: 120,
                          height: 120,
                          decoration: BoxDecoration(
                            image: DecorationImage(
                              fit: BoxFit.cover,
                              image: imageProvider,
                            ),
                          ),
                        ),
                        placeholder: (context, url) =>
                            Center(child: CircularProgressIndicator()),
                        errorWidget: (context, url, error) => Icon(Icons.error),
                      ),
                    ),
                    Container(
                      margin: EdgeInsets.symmetric(horizontal: 10),
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.start,
                        children: [
                          SizedBox(height: 40),
                          Row(
                            children: [
                              Padding(
                                padding: const EdgeInsets.only(left: 10),
                                child: Text("Name : ",
                                    style: TextStyle(
                                        fontSize: 18,
                                        color: Colors.black54,
                                        fontWeight: FontWeight.w500)),
                              ),
                              Padding(
                                padding: const EdgeInsets.fromLTRB(60, 0, 0, 0),
                                child: Text('${userData['fullName']}',
                                    style: TextStyle(
                                        fontSize: 18,
                                        color: Colors.black54,
                                        fontWeight: FontWeight.w500)),
                              )
                            ],
                          ),
                          SizedBox(height: 20),
                        ],
                      ),
                    ),
                  ],
                );
              } else
                return Text('null');
            })

And for updating profile picture in a new "Edit Profile" page, the code I wrote are:

  postNewProfilePicture(String userid) async {
    final ref =
        FirebaseStorage.instance.ref().child('users').child(userid + '.jpg');

    await ref.putFile(newprofilePic!);
    String url = await ref.getDownloadURL();

    newUploadedProfilePic = url;
  }
                        ElevatedButton(
                            style: ElevatedButton.styleFrom(
                                padding: EdgeInsets.symmetric(
                                    horizontal: 45, vertical: 10),
                                textStyle: TextStyle(
                                    fontWeight: FontWeight.w500,
                                    color: Colors.white,
                                    fontSize: 15)),
                            onPressed: () {
                              postNewProfilePicture(widget.id);
                              if (_formKey.currentState!.validate()) {
                                updateUser(widget.id, name, newUploadedProfilePic);
                              }
                              Navigator.of(context).pop();
                            },
                            child: Text(
                              'Update',
                              style: TextStyle(fontSize: 15),
                            ),
                          ),
  Future<void> updateUser(id, name, newimage) {
    return users
        .doc(id)
        .update({
          'fullName': name,
          'image': newimage
        })
        .then((value) => print('Success'))
        .catchError((error) => print('Error: $error'));
  }

With these codes, the url of the new image uploaded sometimes is successfully inserted in Firestore but then other times, only receives null instead. In the case of a successful new profile picture, my application only shows the old profile image as well. How can I insert a new image successfully and load it for display?

Upvotes: 0

Views: 603

Answers (1)

mario francois
mario francois

Reputation: 1371

"update" it's for one field. Use set with option merge true.

  Future<void> updateUser(id, name, newimage) {
    return users
        .doc(id)
        .set({
      'fullName': name,
      'image': newimage
    }, SetOptions(merge: true))
        .then((value) => print('Success'))
        .catchError((error) => print('Error: $error'));
  }

Try to add the model MyUser

@immutable
class MyUser {
  final String id;
  final String fullName;
  final String image;

  const MyUser({required this.id, required this.fullName, required this.image});

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'fullName': fullName,
      'image': image,
    };
  }

  factory MyUser.fromMap(Map<String, dynamic>? map) {
    if (map == null) {
      return const MyUser(id: "", fullName: "", image: "");
    }

    return MyUser(
      id: map['id'] as String,
      fullName: map['fullName'] as String,
      image: map['image'] as String,
    );
  }
}

Then in your stream builder get your image like this

StreamBuilder<MyUser>(
        stream: FirebaseFirestore.instance
            .collection('users')
            .doc(loggedInUser)
            .snapshots()
            .map((map) => MyUser.fromMap(map.data())),
        builder: (context, snapshot) {
          final myUser = snapshot.data;
          if(myUser == null){
            return const Center(
              child: CircularProgressIndicator(),
            );
          }
          print(myUser.image);
          return Container();
        })

Upvotes: 1

Related Questions