Reputation: 1472
VisionBoardNameViewModel
I'm experiencing an issue where a ProgressDialog that displays loading information is persistently shown due to frequent recomposition of our Composable UI. This behavior is likely caused by how I manage and update data in our ViewModel and UseCase layers.
How can I solve this? Here is my code.
@HiltViewModel
class VisionBoardNameViewModel @Inject constructor(
val getLastBoardDataUseCase: GetLastBoardDataUseCase
) : ViewModel() {
private val _boardDataUiState = MutableStateFlow(BoardDataUiState())
val boardDataUiState: Flow<BoardDataUiState> = _boardDataUiState.asStateFlow()
fun getLastBoardData() = viewModelScope.launch {
getLastBoardDataUseCase().collectLatest { response ->
when (response) {
is Resource.Success -> {
_boardDataUiState.update { currentState ->
currentState.copy(boardData = response.data, isLoading = false)
}
}
is Resource.Failure -> {
_boardDataUiState.update { currentState ->
currentState.copy(error = response.message, isLoading = false)
}
}
is Resource.Loading -> {
_boardDataUiState.update { currentState ->
currentState.copy(isLoading = true)
}
}
}
}
}
}
GetLastBoardDataUseCase
class GetLastBoardDataUseCase @Inject constructor(
private val boardDataRepository: BoardDataRepository
) {
operator fun invoke(): Flow<Resource<BoardData>> = flow {
emit(Resource.Loading())
try {
emit(Resource.Success(boardDataRepository.getBoardList().first()))
} catch (e: Exception) {
emit(Resource.Failure(e.message.toString()))
}
}
}
VisionBoardNavigation
@Composable
fun VisionBoardNavigation(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = Screens.HomeScreen.route
) {
composable(Screens.HomeScreen.route) { navBackStack ->
val boardName = navBackStack.arguments?.getString("boardName")
val boardUId = navBackStack.arguments?.getInt("boardUId")
boardUId?.let { homeViewModel.getBoardSectionList(it) }
val homeUiState by homeViewModel.sectionList.collectAsState()
HomeScreen(boardName.toString(), homeUiState)
}
}
}
@Composable
fun HomeScreen(boardName: String = "Hey There", homeUiState: HomeScreenUIState) {
var showLoading by remember {
mutableStateOf(false)
}
LaunchedEffect(key1 = homeUiState.loading, block = {
showLoading = homeUiState.loading
})
if (showLoading) {
Dialog(
onDismissRequest = { },
DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false)
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.size(100.dp)
.background(White, shape = RoundedCornerShape(16.dp))
) {
CircularProgressIndicator()
}
}
}
} {
}
OUTPUT
Upvotes: 0
Views: 828
Reputation: 4206
val showLoading by remember(homeUiState) {
derivedStateOf { homeUiState.loading }
}
Try it like this and see if it works. If you want to learn more about the concept check this great article.
Upvotes: 0
Reputation: 1665
Try omitting the showLoading
remember and also the LaunchedEffect
block and instead directly check homeUiState.loading
to change visibility of the CircularProgressIndicator
.
if (homeUiState.loading) {
// circular progress composable
}
Upvotes: 0
Reputation: 1112
You can check my solution which explains multiple recomposition and use of LaunchedEffect
in jetpack compose. https://stackoverflow.com/a/76969294/14972910
In this case showLoading
variable is remebered
and set to true
in HomeScreen
composable and so that whenever the HomeScreen
recomposes then it will also recompose the dialog as the showLoading
is true
.
So you have to restrict HomeScreen
for recomposition and you can use LaunchedEffect
for that in the NavHost
.
Upvotes: 0