Home > Software engineering >  How to add a CircularProgressIndicator on top of existing LazyColumn in Jetpack Compose?
How to add a CircularProgressIndicator on top of existing LazyColumn in Jetpack Compose?

Time:10-03

I have the following screen:

fun ItemsScreen(
    viewModel: ItemsViewModel = hiltViewModel()
) {
    val showProgressBarState = remember { mutableStateOf(false) }
    if (showProgressBarState.value) { ShowProgressBar() }

    when(val resource = viewModel.state.value) {
        is Loading -> ShowProgressBar() //Works perfectly
        is Success -> LazyColumn {
            items(
                items = resource.data
            ) { item ->
                ItemCard(
                    item = item
                )
            }
            when(val r = viewModel.s.value) {
                is Loading -> showProgressBarState.value = true
                is Success -> showProgressBarState.value = false
                is Failure -> Log.d(TAG, "Failure")
            }
        }
        is Failure -> Text(
            text = r.message,
            modifier = Modifier.padding(16.dp)
        )
    }
}

@Composable
fun ShowProgressBar() {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        CircularProgressIndicator()
    }
}

The second "when" is for a delete state. I want when a item is deleted to start the progress bar. It starts but behind the LazyColumn. How to add it in front?

This is what I have in the activity class:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
        Scaffold(
            //
        ) {
            ItemsScreen()
        }
    }
}

CodePudding user response:

Wrap your all content into Box and also place your progressbar to top of your content.

Box(){
when(val resource = viewModel.state.value) {

    is Success -> LazyColumn {
    }
    is Loading -> ShowProgressBar() //move to top layer 
}
}

CodePudding user response:

You're updating your state value inside the view builder. It'll work in this case, but generally it's a bad practice which may lead to redundant recompositions which may slow your app. I'm talking about this part:

when(val r = viewModel.s.value) {
    is Loading -> showProgressBarState.value = true
    is Success -> showProgressBarState.value = false
    is Failure -> Log.d(TAG, "Failure")
}

You should change state using side effects. Here you could've use LaunchedEffect, so content would be called only when specified key is changed since the last recomposition.:

LaunchedEffect(viewModel.s.value) {
    when (val r = viewModel.s.value) {
        is Loading -> showProgressBarState.value = true
        is Success -> showProgressBarState.value = false
        is Failure -> Log.d(TAG, "Failure")
    }
}

But actually that was an off topic for the future, in this case you don't need showProgressBarState at all.

When you use Box, items are displayed on the screen on on top of each other. As you need to display the progress bar on top of LazyColumn, you need to wrap it with a Box and place ShowProgressBar after LazyColumn.

Also you can specify contentAlignment = Alignment.Center instead of wrapping CircularProgressIndicator with a Column:

is Resource.Success -> {
    Box(contentAlignment = Alignment.Center) {
        LazyColumn {
            items(
                items = resource.data
            ) { item ->
                ItemCard(
                    item = item
                )
            }
        }
        when (val r = viewModel.s.value) {
            is Resource.Loading -> CircularProgressIndicator()
            is Resource.Success -> Unit
            is Resource.Failure -> Log.d(TAG, "Failure")
        }
    }
}
  • Related