Search code examples
android-jetpack-composeandroid-roomdao

Jetpack compose onSuccess() with insert into Room DB


Jetpack compose with room does not perform onSuccess() action, however the data was inserted

This is my viewModel call from the screen fun:

DIRECTOR_ID = UUID.randomUUID()
AGENCY_ID = UUID.randomUUID()
viewModel.addDirectorAndAgency(
    director = Director(
        directorId = DIRECTOR_ID,
        directorName = directorNameValue,
        directorPhone = phoneValue,
        directorPassword = directorPasswordValue,
        agencyId = AGENCY_ID
    ),
    agency = Agency(
        agencyId = AGENCY_ID,
        agencyName = agencyNameValue
    )
) {
  
navController.navigate(Graph.MAIN) //This action is not completed 

}

This is my viewmodel code:

fun addDirectorAndAgency(agency: Agency, director: Director, onSuccess: () -> Unit) {
    viewModelScope.launch(Dispatchers.IO) {
        REPOSITORY.insertDirectorAndAgency(agency = agency, director = director ) {
            viewModelScope.launch(Dispatchers.Main) {
                onSuccess()
            }
        }
    }
}

Database repository:

suspend fun insertDirectorAndAgency(director: Director, agency: Agency, onSuccess: ()-> Unit)

RoomRepository

override suspend fun insertDirectorAndAgency(
    director: Director,
    agency: Agency,
    onSuccess: () -> Unit
) {
    agencyDao.insertDirectorAndAgency(agency = agency, director = director )
}

RoomDao:

@Transaction
suspend fun insertDirectorAndAgency(director: Director, agency: Agency) {
    insertDirector(director)
    insertAgency(agency)
}

Solution

  • You shouldn't write side effects in Compose, instead of waiting for a result in the Ui side with onSuccess you can create a SharedFlow (hot flow) for navigation events in the ViewModel, along with a custom sealed class. Then you need to listen these events in the Compose screen.

    in the ViewModel:

    private val _navigationEvents = MutableSharedFlow<NavigationEvent>()
    val navigationEvents = _navigationEvents.asSharedFlow()
    

    Sealed class:

    sealed class NavigationEvent {
        object Main : NavigationEvent()
        data class Detail(val id: String) : NavigationEvent() // sample
    }
    

    in the Compose screen:

    @Composable
    fun Screen(
        navController: NavHostController,
        viewModel: MainViewModel = hiltViewModel()
    ) {
        LaunchedEffect(Unit) {
            viewModel.navigationEvents.collect { event ->
                when (event) {
                    Main -> navController.navigate(Graph.MAIN)
                    Detail -> // navigate with event.id
                }
            }
        }
    }
    

    You can send the Main navigation event to this flow on success in the viewModel, then your navigation should happen.