如何使用 JetPack Compose 导航到单击 LazyColumn 项目的详细视图?

How to navigate to Detail View clicking in LazyColumn item with JetPack Compose?

我正在尝试使用 JetPack Compose 创建一个应用程序(第一次),但我在导航方面遇到了一些问题。该应用程序有一个带有 3 个项目的 bottomBar,可导航到所选屏幕。

这行得通,问题是当我尝试访问其中一个屏幕中的 LazyColumn 的一项时。我想导航到显示所选项目数据的另一个屏幕(配置文件),但我找不到方法。无论我如何尝试,我总是得到这个“@Composable 调用只能在@Composable 函数的上下文中发生”。

有人可以帮我解释一下怎么做吗?我想要的是学习如何以及为什么,而不仅仅是复制。

谢谢

MainActivity

class MainActivity : ComponentActivity() {

    @ExperimentalFoundationApi
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)


        setContent {
            val systemUiController = rememberSystemUiController()

            SideEffect {
                systemUiController.setStatusBarColor(color = PrimaryDark)
            }
            AppTheme() {


                MainScreen()

            }

        }
    }

    @ExperimentalFoundationApi
    @Composable
    fun MainScreen() {
        val navController = rememberNavController()
        val navigationItems = listOf(Obras, Talleres, Ajustes)
        Scaffold(bottomBar = {
            BottomNavigationBar(
                navController = navController,
                items = navigationItems
            )
        }) {

            NavigationHost(navController)
        }
    }
} 

NavigationHost.kt

@ExperimentalFoundationApi
@Composable
fun NavigationHost(navController: NavHostController) {


    NavHost(navController = navController, startDestination = Obras.route) {
        composable(Obras.route) {
            Pantalla1(navigateToProfile = { authorId ->
                navController.navigate("${Profile.route}/$authorId")
            })

        }
        composable(Talleres.route) {
            Pantalla2()
        }

        composable(Ajustes.route) {
            Pantalla3()
        }

        composable(
            Profile.route + "/{authorId}",
            arguments = listOf(navArgument("authorId") { type = NavType.StringType })
        ) { backStackEntry ->
            val authorId = backStackEntry.arguments!!.getString("authorId")!!
            Profile(authorId)
        }

    }
}

Pantalla1.kt

typealias AuthorId = String

@ExperimentalCoroutinesApi
@Composable
fun Pantalla1(navigateToProfile: (AuthorId) -> Unit) {


    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(
                paddingValues = PaddingValues(

                    bottom = 50.dp
                )
            ),
    ) {

        AutoresInfo(navigateToProfile)
    }

}

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun AutoresInfo(navigateToProfile: (AuthorId) -> Unit) {
    var autoresList by remember {
        mutableStateOf<List<Autor>?>(null)
    }

    JetFirestore(path = { collection("autores") },
        queryOnCollection = { orderBy("nombre", Query.Direction.ASCENDING) },

        onRealtimeCollectionFetch = { value, exception ->
            autoresList = value.getListOfObjects()
        }) {
        autoresList?.let {
            val grouped = it.groupBy { it.nombre[0] }
            LazyColumn(

                modifier = Modifier.fillMaxSize()

            ) {


                grouped.forEach { (initial, autoresForInitial) ->
                    stickyHeader {
                        StickyHeaderAutores(initial = initial.toString())
                    }

                    items(autoresForInitial, key = { autor -> autor.nombre }) { autor ->
                        Surface(modifier = Modifier.clickable { navigateToProfile(autor.nombre) }) {
                            @OptIn(coil.annotation.ExperimentalCoilApi::class)
                            AutorCard(autor)
                        }


                    }
                }


            }
        } ?: Column(
            modifier = Modifier.fillMaxSize(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            CircularProgressIndicator(
                color = Color.Red,
                modifier = Modifier

                    .size(50.dp)

            )
        }

    }


}

第 1 步。将参数添加到您的个人资料导航路线。查看 documentation 关于使用参数导航

composable(
    Profile.route + "/{authorId}",
    arguments = listOf(navArgument("authorId") { type = NavType.StringType })
) { backStackEntry ->
    val authorId = backStackEntry.arguments!!.getString("authorId")!!
    Profile(authorId)
}

第 2 步。您需要从 NavigationHost 传递 navigateToProfile 函数。如果不是 String:

,您可以将 AuthorId 替换为 Int(或其他类型)
typealias AuthorId = String

@Composable
fun NavigationHost(navController: NavHostController){
    NavHost(navController = navController, startDestination = Obras.route) {
        composable(Obras.route) {
            Pantalla1(navigateToProfile = { authorId ->
                navController.navigate("${Profile.route}/$authorId")
            })
        }
        ... 
}

@Composable
fun Pantalla1(navigateToProfile: (AuthorId) -> Unit) {
    ...
        AutoresInfo(navigateToProfile)
    ...
}

@Composable
fun AutoresInfo(navigateToProfile: (AuthorId) -> Unit) {
    ...
    items(autoresForInitial, key = { autor -> autor.nombre }) { autor ->
        Surface(modifier = Modifier.clickable {
            navigateToProfile(author.id)
        }) {
            AutorCard(autor)
        }
    }
    ...
}

第 3 步。在您的个人资料可组合项中,您需要通过 ID 获取作者。不确定 JetFirestore 是什么,您可能应该使用它。

@Composable
fun Profile(id: AuthorId) {
    JetFirestore(
        // fetch author by id
    )
}

创建一个密封的class,其中包含如下所示的路由

sealed class Screen(val route:String){
    object MyListScreen:Screen("my_list_screen")
    object MyDetailScreen:Screen("my_detail_screen")
}

创建带有接收列表和点击事件的行的列表项,如下所示

@Composable
fun MyListItem(
    yourList: YourList,
    onItemClick:(yourList) -> Unit
) {
    Row(
       modifier = Modifier
           .fillMaxWidth()
           .clickable {
               onItemClick(yourList)
           }
    ){
   //something here
}

在 LazyColoumn 的 listScreen 中,您可以这样做

 LazyColumn(modifier = Modifier.fillMaxSize()){
            items (state.yourfreshlist){mylist->
               MyListItem(mylist = mylist, onItemClick = {
                 navController.navigate(Screen.MyDetailScreen.route+"/${mylist.something}")
               }
               )
            }
    }

你的主人会是这样的

NavHost(
            navController = navController,
            startDestination = Screen.MyListScreen.route
        ) {
            composable(
                route = Screen.MyListScreen.route
            ) {
                MyListScreen(navController)
            }
            composable(
                route = Screen.MyDetailScreen.route + "/{anything here id for example}"
            ) {
                MyDetailScreen()
            }
        }

更多信息,您可以浏览docs