Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laggy/Slow Navigation between BottomNavigation composables - Jetpack Compose

I am using a BottomNavigation with 4 composables. All of them have a LazyColumn with each item in the LazyColumn having an image populated from the network using Coil for Jetpack Compose. Similar to Twitter/YouTube.

When I navigate between these items, the composables get destroyed and recompose only when navigated back to them. Even the coil images are cleared and re-fetched (from memory or local storage) when navigated between these composables. This is of course the expected behavior.

The problem is that this is causing the navigation between them to be too slow. Coil images take about 400ms to 700ms to load the image for every navigation. Apps like YouTube/LinkedIn are literally instant in their BottomBar navigations.

When I was using XML for this, I would make the fragments(used as bottom nav items ) with an appear/disappear logic to avoid this time delay while navigating between them.

How do I achieve the same with Compose ?

I am using the following versions:

//compose navigation
implementation "androidx.navigation:navigation-compose:2.4.0-beta01"
implementation "com.google.accompanist:accompanist-navigation-animation:0.21.0-beta"
like image 272
Anudeep Ananth Avatar asked Sep 06 '25 03:09

Anudeep Ananth


1 Answers

Well I had the same problem using emulator or phone both work well with small simplistic composables. And when I create more complex Composable and try animating the navigation, using the accompanist animation library it gets very laggy. But then I tried building the release APK file, since it is usually optimized and way faster, and that will significantly speed up your app. Here is link on how you can generate signed APK then install it on your phone or emulator and see if that fixes your problem!

You could also check if you accidentally disabled the hardware acceleration from the manifest. Make sure you set android:hardwareAccelerated="true" in you activity tag.

In case that does not help either, then you have to implement your own animation and use Shared ViewModel, for communication and triggering the transition from one Composable to another. The idea is that you can use modifier offset property to show/hide the Composable by placing it outside the screen.

First set your ViewModel, and add mutable state variable, that will trigger the transition from Home to Settings and vice versa.

This is not the best practice, since there is no way to directly pass data from one composable to another as you would normally do with the normal navigation. But you can still share data using the Shared ViewModel. Using this method it will not recompose your Composable, and thus be really fast. So far I have no problem with any out of memory exceptions even on some very old/slow devices with 2GB RAM.

class SharedViewModel : ViewModel() {

    // changing this mutable state will trigger the transition animation
    private val _switchHomeToSetting = mutableStateOf(true)
    val switchHomeToSetting: State<Boolean> = _switchHomeToSetting

    fun switchHomeToSettings() {
        _switchHomeToSetting.value = !_switchHomeToSetting.value
    }
}

Now create your two Composable functions Home and Settings respectively

@Composable
fun HomeScreen(viewModel: SharedViewModel) {
   // draw your subcomponents
}

@Composable
fun SettingsScreen(viewModel: SharedViewModel) {
   // draw your subcomponents
}

And finally initialize the animation in you main activity

class MainActivity : ComponentActivity() {
   
    val viewModel by viewModels<CityWeatherViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
 
        setContent { 
            
            // set your offset animation
            val density = LocalDensity.current
            val width = with(density) { (1.adw).toPx() }
            val animatedOffsetX: Float by animateFloatAsState(
                targetValue = if (!viewModel.switchHomeToSetting.value) 0f else width,
                animationSpec = tween(1200)
            )

            // Home screen
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .offset { IntOffset((-width + animatedOffsetX).toInt(), 0) }
            ) {
                HomeScreen(viewModel = viewModel)
            }

            // Settings screen
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .offset { IntOffset(animatedOffsetX.toInt(), 0) }
            ) {
                SettingsScreen(viewModel = viewModel)
            }
        }
    }
}

Here is the result using the Composable Navigation, together with the Accompanist Animation. As you can see it is very laggy indeed

enter image description here

And now here is the result using our custom animation, where it is very smooth since no Composable is recomposed.

enter image description here

like image 165
slaviboy Avatar answered Sep 07 '25 22:09

slaviboy