Я хочу вызвать функцию приостановки внутри обратного вызова составной функции.

suspend fun getLocation(): Location? { /* ... */ }

@Composable
fun F() {

    val (location, setLocation) = remember { mutableStateOf<Location?>(null) }

    val getLocationOnClick: () -> Unit = {
        /* setLocation __MAGIC__ getLocation */
    }

    Button(onClick = getLocationOnClick) {
        Text("detectLocation")
    }

}

Если бы я использовал Rx, я бы просто subscribe.

Я мог бы сделать invokeOnCompletion, а затем getCompleted, но этот API экспериментальный.

Я не могу использовать launchInComposition в getLocationOnClick, потому что launchInComposition равно @Composable, а getLocationOnClick не может быть @Composable.

Как лучше всего получить результат приостановки функции внутри обычной функции, внутри функции @Composable?

10
Nycta 29 Сен 2020 в 12:09

2 ответа

Лучший ответ

Создайте область сопрограмм, привязанную к жизненному циклу вашего составного объекта, и используйте эту область для вызова функции приостановки.

suspend fun getLocation(): Location? { /* ... */ }

@Composable
fun F() {
    // Returns a scope that's cancelled when F is removed from composition
    val coroutineScope = rememberCoroutineScope()

    val (location, setLocation) = remember { mutableStateOf<Location?>(null) }

    val getLocationOnClick: () -> Unit = {
        coroutineScope.launch {
            val location = getLocation()
        }
    }

    Button(onClick = getLocationOnClick) {
        Text("detectLocation")
    }
}
3
heyheyhey 14 Ноя 2020 в 16:16

Вы можете использовать viewModelScope ViewModel или любую другую область сопрограммы.

Пример удаления действия для элемента из LazyColumnFor, который требует приостановки вызова, обрабатываемого ViewModel.

     class ItemsViewModel : ViewModel() {

        private val _itemList = MutableLiveData<List<Any>>()
        val itemList: LiveData<List<Any>>
            get() = _itemList

        fun deleteItem(item: Any) {
            viewModelScope.launch(Dispatchers.IO) {
                TODO("Fill Coroutine Scope with your suspend call")       
            }
        }
    }

    @Composable
    fun Example() {
        val itemsVM: ItemsViewModel = viewModel()
        val list: State<List<Any>?> = itemsVM.itemList.observeAsState()
        list.value.let { it: List<Any>? ->
            if (it != null) {
                LazyColumnFor(items = it) { item: Any ->
                    ListItem(
                        item = item,
                        onDeleteSelf = {
                            itemsVM.deleteItem(item)
                        }
                    )
                }
            } // else EmptyDialog()
        }
    }

    @Composable
    private fun ListItem(item: Any, onDeleteSelf: () -> Unit) {
        Row {
            Text(item.toString())
            IconButton(
                onClick = onDeleteSelf,
                icon = { Icons.Filled.Delete }
            )
        }
    }
2
2jan222 30 Сен 2020 в 10:21