将可组合值参数设置为挂起函数的结果

Set Composable value parameter to result of suspend function

我是 Compose 和 Kotlin 的新手。我有一个使用 Room 数据库的应用程序。在前端,有一个包含图标可组合项的可组合项。我希望根据在挂起函数中执行的数据库操作的结果来设置图标资源。

我的可组合项看起来像这样:

@Composable
fun MoviePreview(movie : ApiMoviePreview, viewModel: ApiMovieViewModel) {

    Card(
        modifier = ...
    ) {
        Row(
            modifier = ...
        ) {

            IconButton(
                onClick = {
                //...
            }) {
                Icon(
                    imageVector =
                        // This code does not work, as isMovieOnWatchList() is a suspend function and cannot be called directly
                        if (viewModel.isMovieOnWatchlist(movie.id)) {
                            Icons.Outlined.BookmarkAdded
                        } else {
                            Icons.Filled.Add
                        }
                    ,
                    contentDescription = stringResource(id = R.string.addToWatchlist)
                )
            }
        }
    }
}

我需要调用的函数是一个挂起函数,因为 Room 要求它的数据库操作发生在一个单独的线程上。函数 isMovieOnWatchlist() 如下所示:

suspend fun isMovieOnWatchlist(id: Long) {
    return movieRepository.isMovieOnWatchlist(id)
}

实现所需行为的适当方法是什么?我已经偶然发现了协程,但问题是似乎没有办法 return 从协程函数中提取一个值。

更好的方法是准备数据,以便您需要的一切都在 data/value class 中,而不是按 row/item 执行实时查找,这不是很有效。我假设您有 2 tables 并且您可能想要一个 LEFT JOIN 但是所有这些细节都不包括在内。

对于 Room,它甚至包括使用 Flow api 的实现,这意味着它将在 table 和 re-runs 原始查询中的信息发生变化时观察数据为您提供新的更改数据集。

然而,这超出了您最初问题的范围,但如果您想探索这个问题,那么这是一个好的开始:https://developer.android.com/codelabs/basic-android-kotlin-training-intro-room-flow#0

针对你原来的问题。这可能可以通过组合项中的 LaunchedEffect 和一些观察到的 MutableState<ImageVector?> 对象来实现,例如:

@Composable
fun MoviePreview(
    movie: ApiMoviePreview,
    viewModel: ApiMovieViewModel
) {
    var icon by remember { mutableStateOf<ImageVector?>(value = null) } // null or default icon until update by result below

    Card {
        Row {
            IconButton(onClick = {}) {
                icon?.run {
                    Icon(
                        imageVector = this,
                        contentDescription = stringResource(id = R.string.addToWatchlist))
                }
            }
        }
    }

    LaunchedEffect(Unit) {
        // execute suspending function in provided scope closure and update icon state value once complete
        icon = if (viewModel.isMovieOnWatchlist(movie.id)) {
            Icons.Outlined.BookmarkAdded
        } else Icons.Filled.Add
    }
}