skip to Main Content

When i click picture it updates in firebase storage, but doesn’t updates in my ui. But if i click this picture again, it will update in the ui.

Code:

var user by remember {
    mutableStateOf(
        if(signInWithGoogle) {
            User(
                profilePictureUrl = userData?.profilePictureUrl,
                userName = userData?.userName
            )
        } else {
            User(
                profilePictureUrl = signInEmailVM.getSignedInUser()?.photoUrl.toString(),
                userName = signInEmailVM.getSignedInUser()?.displayName
            )
        }
    )
}

//Photo picker
val singlePhotoPickerLauncher = rememberLauncherForActivityResult(
    contract = ActivityResultContracts.PickVisualMedia(),
    //Upload image to firebase and change profile picture
    onResult = { uri ->
        if(uri != null) {
            scope.launch {
                if(signInEmailVM.updateUserProfile(image = uri, name = "BRBX")) {
                    Toast.makeText(
                        context,
                        "Изменения сохранены",
                        Toast.LENGTH_SHORT
                    ).show()
                    user = User(
                        profilePictureUrl = signInEmailVM.getSignedInUser()?.photoUrl.toString(),
                        userName = signInEmailVM.getSignedInUser()?.displayName
                    )
                } else {
                    Toast.makeText(
                        context,
                        "Что-то пошло не так",
                        Toast.LENGTH_SHORT
                    ).show()
                }
            }
        }
    }
)

AsyncImage(
    model = user.profilePictureUrl,
    contentDescription = "Profile picture",
    modifier = Modifier.fillMaxSize(),
    contentScale = ContentScale.Crop
)

I tried to move the creation of a new instance of the user object, but result was stil the same. I tried to run top part of code, not in a coroutine, didn’t help. I think problem is compose and his recompositions. But if the picture was already updated, next time i click it, it will showed from the first try. If you need, here is code for updating user picture:

suspend fun updateUserProfile(image: Uri, name: String): Boolean {
    val storageRef = FirebaseStorage.getInstance().reference.child("Users/${firebaseAuth.currentUser?.uid}/${image.lastPathSegment}")
    val upload = storageRef.putFile(image)

    val result = CompletableDeferred<Boolean>()
    upload.addOnCompleteListener {
        result.complete(it.isSuccessful)
    }

    storageRef.downloadUrl.addOnSuccessListener { uri ->
        val profileUpdates = userProfileChangeRequest {
            displayName = name
            photoUri = Uri.parse(uri.toString())
        }
        firebaseAuth.currentUser?.updateProfile(profileUpdates)?.addOnCompleteListener {
            result.complete(it.isSuccessful)
        }
    }

    return result.await()
}

2

Answers


  1. Chosen as BEST ANSWER

    Finally after three hours i found solution. Problem wasn't in recompositions, it was in logic

    suspend fun updateUserProfile(image: Uri, name: String): Boolean {
        val storageRef = FirebaseStorage.getInstance().reference.child("Users/${firebaseAuth.currentUser?.uid}/${image.lastPathSegment}")
        val upload = storageRef.putFile(image)
    
        val result = CompletableDeferred<Boolean>()
        upload.addOnSuccessListener {
            storageRef.downloadUrl.addOnSuccessListener { uri ->
                val profileUpdates = userProfileChangeRequest {
                    displayName = name
                    photoUri = Uri.parse(uri.toString())
                }
                firebaseAuth.currentUser?.updateProfile(profileUpdates)?.addOnSuccessListener {
                    result.complete(true)
                }
            }
        }
    
        return result.await()
    }
    

    This worked for me


  2. It seems like the issue might be related to how the user state is updated after selecting a new profile picture. Since you’re updating the user state within a coroutine, and the UI might not be recomposed immediately after the state change, Compose might not reflect the changes promptly.

    To ensure that Compose recomposes immediately after updating the user state, you can try using a MutableStateFlow instead of mutableStateOf. MutableStateFlow is a part of Kotlin Flows and is designed to be used in asynchronous scenarios like coroutines.

    Here’s how you can modify your code to use MutableStateFlow:

    val userStateFlow = remember { mutableStateOf(
        if(signInWithGoogle) {
            User(
                profilePictureUrl = userData?.profilePictureUrl,
                userName = userData?.userName
            )
        } else {
            User(
                profilePictureUrl = signInEmailVM.getSignedInUser()?.photoUrl.toString(),
                userName = signInEmailVM.getSignedInUser()?.displayName
            )
        }
    ) }
    
    var user by userStateFlow
    
    //Photo picker
    val singlePhotoPickerLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.PickVisualMedia(),
        //Upload image to firebase and change profile picture
        onResult = { uri ->
            if(uri != null) {
                scope.launch {
                    if(signInEmailVM.updateUserProfile(image = uri, name = "BRBX")) {
                        Toast.makeText(
                            context,
                            "Изменения сохранены",
                            Toast.LENGTH_SHORT
                        ).show()
                        user = User(
                            profilePictureUrl = signInEmailVM.getSignedInUser()?.photoUrl.toString(),
                            userName = signInEmailVM.getSignedInUser()?.displayName
                        )
                    } else {
                        Toast.makeText(
                            context,
                            "Что-то пошло не так",
                            Toast.LENGTH_SHORT
                        ).show()
                    }
                }
            }
        }
    )
    
    AsyncImage(
        model = user.profilePictureUrl,
        contentDescription = "Profile picture",
        modifier = Modifier.fillMaxSize(),
        contentScale = ContentScale.Crop
    )
    

    This way, Compose will be immediately notified of changes to the user state, ensuring that the UI updates accordingly after selecting a new profile picture.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search