I am developing an application in which I show a list of potatoes, retrieving the data from Firestore.
I have added a swipe action to refresh the data. With the code that I show below, the data is updating fine, the call is made to Firestore and it is updated showing new values in case they exist, or stopping showing values that no longer exist.
The problem is that when I swipe the potato list screen remains blank, empty, and when the call to Firestore ends, they are shown again. That is, there are a couple of seconds that the screen goes blank.
Is there a possibility that this will not happen? This effect is somewhat ugly
ViewModel:
@HiltViewModel
class PotatoesViewModel @Inject constructor(
private val getPotatoesDataUseCase: GetPotatoesData
) : ViewModel() {
private val _state = mutableStateOf(PotatoesState())
val state: State<PotatoesState> = _state
private val _isRefreshing = MutableStateFlow(false)
val isRefreshing: StateFlow<Boolean>
get() = _isRefreshing.asStateFlow()
init {
getPotatoes()
}
private fun getPotatoes() {
getPotatoesDataUseCase().onEach { result ->
when (result) {
is Resource.Success -> {
_state.value = PotatoesState(potatoes = result.data?.potatoes ?: emptyList())
}
is Resource.Error -> {
_state.value = PotatoesState(
error = result.message ?: "An unexpected error occurred"
)
}
is Resource.Loading -> {
_state.value = PotatoesState(isLoading = true)
}
}
}.launchIn(viewModelScope)
}
fun refresh() {
viewModelScope.launch {
_isRefreshing.emit(true)
getIncidents()
_isRefreshing.emit(false)
}
}
}
Screen:
@Composable
fun PotatoesDataScreen(
navController: NavController,
viewModel: PotatoesViewModel = hiltViewModel()
) {
val state = viewModel.state.value
val isRefreshing by viewModel.isRefreshing.collectAsState()
Scaffold(
topBar = {
TopAppBar(
title = {
Text(
stringResource(R.string.app_name),
fontWeight = FontWeight.Bold
)
},
backgroundColor = Primary,
contentColor = Color.White
)
},
content = {
Box(modifier = Modifier.fillMaxSize()) {
SwipeRefresh(
state = rememberSwipeRefreshState(isRefreshing),
onRefresh = { viewModel.refresh() }
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 8.dp)
) {
items(state.potatoes) { potato ->
PotatoCard(
potato = potato
)
}
}
}
}
}
)
}
PotatoState:
data class PotatoesState(
val isLoading: Boolean = false,
val potatoes: List<Potato> = emptyList(),
val error: String = ""
)
CodePudding user response:
When the list screen is blank, this is the time when the Api call is made.
When your call is made and response is still not received, this is also when the list is blank.
You pass a new Object of PotatoesState
to the mutableState
every time you:
- receive a response,
- get an error, (with
Potatoes = emptyList()
) - or state is loading. (with
Potatoes = emptyList()
)
UI is updated according to the MutableState
you named _state
.
If you want to keep the same data until you get a new response, then you need to update the current state.value: MutableState<PotatoesState>
object only when you get a new response (AKA, is Resource.success
).
Alternatively, you can implement a Loading Spinner, and show it when you start your Api Request, until isLoading
is false
.