重新组合后保持 LazyColumn 滚动位置

Maintain LazyColumn scroll position after re-composition

我正在构建一个可组合的屏幕,比如 PostScreen,其中 GridView 中显示多个 post,当用户点击其中任何一个时,我将导航至 DetailScreen 其中 post 显示在带有多个关联按钮(如评论)的较大框中。 我的逻辑是,当用户单击 PostScreen 中的任何 post 时,使用 PostScreen 中的索引滚动到 DetailScreen 中的该索引。问题是,当用户点击任何 post(并到达 DetailScreen),然后向上(或向下)移动然后点击操作(例如,像 post),协同程序操作已启动,但索引正在重置并且 DetailScreen 滚动到原始索引而不是停留在喜欢的 post。我将如何解决这个问题? (我知道 rememberLazyListState()

@Composable
fun DetailScreen(
    viewModel: MyViewModel,
    index: Int? // index is coming from navGraph
) {
    val postIndex by remember { mutableStateOf(index) }
    val scope = rememberCoroutineScope()
    val posts = remember(viewModel) { viewModel.posts }.collectAsLazyPagingItems()

    Scaffold(
        topBar = { MyTopBar() }
    ) { innerPadding ->
        JustNestedScreen(
            modifier = Modifier.padding(innerPadding),
            posts = posts,
            onLike = { post ->
                // This is causing index to reset, maybe due to re-composition
                scope.launch {
                    viewModel.toggleLike(
                        postId = post.postId,
                        isLiked = post.isLiked
                    )
                }
            },
            indexToScroll = postIndex
        )
    }
}

@Composable
fun JustNestedScreen(
    modifier: Modifier = Modifier,
    posts: LazyPagingItems<ExplorePost>,
    onLike: (Post) -> Unit,
    indexToScroll: Int? = null
) {
    val scope = rememberCoroutineScope()
    val listState = rememberLazyListState()

    LazyColumn(
        modifier = modifier
            .fillMaxSize()
            .background(MaterialTheme.colors.background),
        state = listState
    ) {
        items(posts) { post ->
            post?.let {
                // Display image in box and some buttons
                FeedPostItem(
                    post = it,
                    onLike = onLike,
                )
            }
        }

        indexToScroll?.let { index ->
                scope.launch {
                    listState.scrollToItem(index = index)
                }
        }
    }
}

使用LaunchedEffectLaunchedEffect 的块只是 运行 第一次,然后每次更改密钥时。如果你只想 运行 它一次,使用 UnitlistState 作为键:

LaunchedEffect(listState) {
    indexToScroll?.let { index ->
        listState.scrollToItem(index = index)
    }
}