Pawandeep Singh
Pawandeep Singh

Reputation: 269

Circular Progress Bar in JetPack

I have a function that fetches data from an API and displays that data. I want to show a circular progress bar on the screen until that page is ready.

fun Preview(Number: String) {
    var selected by remember { mutableStateOf<Train?>(null) }
    //var isLoading = remember { mutableStateOf(true) }
    LaunchedEffect(key1 = Unit) {
    //Simulate an API delay
         delay(1000) 
        selected = data from API
    }
 
    if (selected != null) {
       
     
        HorizontalPager(
            state = pagerState,
            modifier = Modifier.fillMaxSize(),
            beyondBoundsPageCount = 2
        )
        { index ->
            selected?.get(index)?.let {
                PagerContent(it)
            }
        }
    } else {
        Column(
            Modifier.fillMaxSize(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            CircularProgressIndicator(color = Color.Blue)
            Spacer(modifier = Modifier.height(10.dp))
            Text(text = "Loading Please Wait...")
        }
    }
}

This code shows a progress bar then it kind of hangs. I think wait for PagerContent() to get loaded. I want it to keep showing the progress bar until PagerContent() is loaded/data is displayed on the page.

Upvotes: 0

Views: 277

Answers (2)

tyg
tyg

Reputation: 15589

The UI runs on a single thread, the main thread. You can launch coroutines, but in general these can't call composable functions. That also means that the UI cannot be constructed asynchronously. Therefore you can either display a frame of an animation or construct some other part of the UI, not both at the same time. When constructing some UI element takes too long the next animation frame cannot be executed fast enough so the animation seems to "hang".

The real question that you must ask yourself is why PagerContent even takes so long to execute that it is noticable.

Compose functions are intended to execute very fast because they are executed everytime a recomposition takes place. Recompositions in general can occur as often as every frame, which will be usualy at least 60 times a second. "Can occur" means that's the worst case, and usually you will have far less recompositions. Nevertheless you will want your composable functions to still execute as fast as possible to prevent the kind of jank you experienced.

What you need to do is make PagerContent faster. Since you didn't share the code of PagerContent we can only speculate why it takes so long to execute. Possible reasons are:

  • Displaying large amounts of data even when most of it is off-screen. An example would be a long list of items that is displayed in a scrollable Column. Every item would need to be created, even if most of them would be off-screen. In this case the solution would be to use a LazyColumn that only creates those items that are actually visible.
  • Performing long-running operations. That may be complex calculations that consume a lot of CPU time or lots of fast operations, like iterating/mapping a very long list. Both should be moved off the main thread into the view model and executed on the Default dispatcher. Another source of long-running operations would be IO operations like database access or even a network request. These should be moved to the IO dispatcher. In all of these cases, however, the UI should only get involved after the result is made available by the view model so it doesn't have to wait and won't block the main thread.

This list is not exhaustive but you probably get the gist: Move everything that isn't directly related to creating UI elements (which must be executed on the main thread) out of PagerContent and into the view model while also executing it on another, more appropriate dispatcher.

When PagerContent is fast enough that its execution time isn't noticeable anymore the problem you described in your question will also be gone.

Upvotes: 0

Pravin Nagmal
Pravin Nagmal

Reputation: 1

I think you should try seperating IF and ELSE Condition statements and check.

            if (selected != null) {
                   
                 
                    HorizontalPager(
                        state = pagerState,
                        modifier = Modifier.fillMaxSize(),
                        beyondBoundsPageCount = 2
                    )
                    { index ->
                        selected?.get(index)?.let {
                            PagerContent(it)
                        }
                    }
            }

             if (selected == null) {
                    Column(
                        Modifier.fillMaxSize(),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        CircularProgressIndicator(color = Color.Blue)
                        Spacer(modifier = Modifier.height(10.dp))
                        Text(text = "Loading Please Wait...")
                    }
            }

Upvotes: -1

Related Questions