Restore focus when navigating back

71 Views Asked by At

I'm writing a TV app using Jetpack Compose (1.0.0-alpha06). The main screen of my app is a pretty basic list of shelves/rows implemented using a TvLazyColumn with several TvLazyRows. When I navigate to child/detail screen and pop back to the main screen, focus doesn't seem to be on any item in particular. If I push one of the d-pad buttons it'll set focus to an item and I can resume as normal. However, this obviously isn't the best experience for my users.

I tried saving the list state via rememberTvLazyListState variables to the column and rows but it didn't make a difference. I also tried explicitly setting unique keys on the column/rows items call but it also didn't fix it.

I recreated it in a simple app:

enter image description here

Here's the code for the app above:

package com.example.myapplication

import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.fragment.app.FragmentActivity
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.Card
import androidx.tv.material3.CardDefaults
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text

class MainActivity : FragmentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            NavGraph()
        }
    }
}

@Composable
fun NavGraph(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController()
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = "main"
    ) {
        composable("main") {
            Main(navController)
        }
        composable("detail/{item}") {
            Detail(
                item = it.arguments?.getString("item") ?: "<null>",
                navController = navController
            )
        }
    }
}

@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun Main(navController: NavController = rememberNavController()) {
    TvLazyColumn(
        modifier = Modifier
            .fillMaxSize(),
        contentPadding = PaddingValues(horizontal = 20.dp),
        verticalArrangement = Arrangement.spacedBy(10.dp)
    ) {
        items((1..10).toList()) { row ->
            Text(
                "Row $row",
                color = Color.White
            )

            Spacer(modifier = Modifier.height(5.dp))

            TvLazyRow(
                horizontalArrangement = Arrangement.spacedBy(10.dp)
            ) {
                items((1..10).toList()) { col ->
                    val item = "Item $row.$col"
                    Card(
                        onClick = { navController.navigate("detail/$item") },
                        colors = CardDefaults.colors(containerColor = Color.Red),
                        modifier = Modifier
                            .size(100.dp)
                    ) {
                        Box(modifier = Modifier.fillMaxSize()) {
                            Text(item,
                                color = Color.White,
                                modifier = Modifier
                                    .align(Alignment.Center)
                            )
                        }
                    }
                }
            }
        }
    }
}

@Preview(device = "id:tv_1080p")
@Composable
fun MainPreview() {
    Main()
}

@Composable
fun Detail(item: String,
           navController: NavController = rememberNavController()
) {
    Box(modifier = Modifier.fillMaxSize()) {
        Text("You clicked item $item",
            color = Color.White,
            fontSize = 30.sp,
            modifier = Modifier.align(Alignment.Center)
        )
    }
}

@Preview(device = "id:tv_1080p")
@Composable
fun DetailPreview() {
    Detail("Item 1.1")
}
0

There are 0 best solutions below