Pratik Fagadiya
Pratik Fagadiya

Reputation: 1472

Composable Keeps recomposing - Loading Dialog Keeps Updating Multiple Times

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

enter image description here

Upvotes: 0

Views: 828

Answers (3)

F.Mysir
F.Mysir

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

Mostafa Arian Nejad
Mostafa Arian Nejad

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

Atul Sharma
Atul Sharma

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

Related Questions