获取联系人时触发@Composable调用错误,该如何解决?
解决Jetpack Compose中@Composable只能在@Composable上下文调用的错误
你遇到的错误提示:
@Composable invocations can only happen from the context of a @Composable function.
错误原因
你在rememberLauncherForActivityResult的回调函数中直接调用了LazyColumn(@Composable组件),但这个回调本身并没有被@Composable注解标记,不属于Composable上下文,违反了Jetpack Compose的核心规则——所有@Composable函数/组件必须在其他@Composable函数或带@Composable注解的Lambda中执行。
解决方案
通过状态变量分离业务逻辑与UI渲染:回调仅负责更新状态,UI渲染逻辑放在Composable的主流程中,依靠Compose的状态自动重组机制更新界面。
修改后的完整代码:
data class Contact(val name: String, val phoneNumber: String) @Composable fun ContactList(){ val context = LocalContext.current // 状态变量:存储获取到的联系人列表 val contactsState = remember { mutableStateOf<List<Contact>>(emptyList()) } // 状态变量:标记是否已获取联系人权限 val hasPermissionState = remember { mutableStateOf(false) } val launcher = rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> if (isGranted) { // 权限通过,仅更新状态变量 val contacts = fetchContacts(context) contactsState.value = contacts hasPermissionState.value = true } else { // 权限被拒绝,更新状态 hasPermissionState.value = false } } // 首次启动时申请权限,避免重组重复发起 LaunchedEffect(Unit) { launcher.launch(Manifest.permission.READ_CONTACTS) } // 根据状态渲染对应UI when { !hasPermissionState.value -> { Text(text = "需要联系人读取权限才能显示联系人") // 可添加重新申请权限的按钮 } contactsState.value.isEmpty() -> { Text(text = "暂无联系人数据") } else -> { LazyColumn { items(contactsState.value) { contact -> Text(text = "Name: ${contact.name}, Phone: ${contact.phoneNumber}") Spacer(modifier = Modifier.padding(10.dp)) } } } } } private fun fetchContacts(context: Context): List<Contact> { val contacts = mutableListOf<Contact>() val cursor = context.contentResolver.query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null ) cursor?.use { while (it.moveToNext()) { val name = it.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)) val phoneNumber = it.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)) contacts.add(Contact(name, phoneNumber)) } } return contacts }
关键修改点
- 新增
contactsState和hasPermissionState两个状态变量,用remember { mutableStateOf(...) }包裹,确保Composable重组时状态不丢失 - 将原回调中的UI渲染逻辑移到Composable主流程,通过
when表达式根据状态切换界面 - 用
LaunchedEffect(Unit)包裹权限申请逻辑,避免组件重组时重复发起权限请求 - 回调仅负责更新状态,不再直接调用@Composable组件
内容的提问来源于stack exchange,提问作者indiebloog
相关产品推荐
相关产品推荐

