You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Compose 1.7.0及以上自定义输入法适配:替代弃用的LocalTextInputService

Compose 1.7.0 自定义输入服务适配方案

问题背景

Compose 1.7.0 中LocalTextInputService和TextInputService已被弃用,原有依赖这些API实现的自定义TextField无法再通过CoreTextField获取LocalTextInputService.current实例。官方标注需用PlatformTextInputModifierNode替代,但缺少明确落地指引。


一、过渡兼容方案(基于旧API适配)

如果暂时不想大规模重构,可通过官方遗留适配器兼容原有逻辑,修改你的MyKeyboardContainer实现如下:

@Composable
fun MyKeyboardContainer(
    modifier: Modifier = Modifier,
    closeKeyboardClickOutside: Boolean = true,
    content: @Composable ColumnScope.() -> Unit,
) = Column(modifier = modifier) {
    val focusManager = LocalFocusManager.current
    val clickableModifier = if (closeKeyboardClickOutside) {
        Modifier.clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) { focusManager.clearFocus(force = true) }
    } else {
        Modifier
    }

    // 替换原有MyLocalTextInputService的获取逻辑
    val legacyAdapter = remember { createLegacyPlatformTextInputServiceAdapter() }
    val myPlatformService = remember { MyPlatformTextInputService() }
    val textInputService = remember {
        TextInputService(object : PlatformTextInputService by myPlatformService {
            // 委托原有状态管理逻辑,同时通过适配器兼容遗留API
            override fun startInput(
                value: TextFieldValue,
                imeOptions: ImeOptions,
                onEditCommand: (List<EditCommand>) -> Unit,
                onImeActionPerformed: (ImeAction) -> Unit,
            ) {
                myPlatformService.startInput(value, imeOptions, onEditCommand, onImeActionPerformed)
                legacyAdapter.startInput(value, imeOptions, onEditCommand, onImeActionPerformed)
            }

            override fun stopInput() {
                myPlatformService.stopInput()
                legacyAdapter.stopInput()
            }

            override fun updateState(oldValue: TextFieldValue?, newValue: TextFieldValue) {
                myPlatformService.updateState(oldValue, newValue)
                legacyAdapter.updateState(oldValue, newValue)
            }

            override fun showSoftwareKeyboard() {
                myPlatformService.showSoftwareKeyboard()
                legacyAdapter.showSoftwareKeyboard()
            }

            override fun hideSoftwareKeyboard() {
                myPlatformService.hideSoftwareKeyboard()
                legacyAdapter.hideSoftwareKeyboard()
            }
        })
    }

    CompositionLocalProvider(LocalTextInputService provides textInputService) {
        Column(
            modifier = Modifier
                .weight(1f)
                .fillMaxWidth()
                .then(clickableModifier),
            content = content,
        )

        MyKeyboard(windowSize)
    }
}

该方案完整保留你原有MyPlatformTextInputService的状态管理逻辑,同时通过委托机制满足CoreTextField的API调用要求。


二、正式替代方案(基于PlatformTextInputModifierNode)

官方推荐的PlatformTextInputModifierNode采用Modifier节点架构,需将输入服务逻辑从CompositionLocal迁移至Modifier体系,具体步骤如下:

1. 重构输入状态管理类

剥离原有PlatformTextInputService继承关系,保留核心状态管理逻辑:

internal class MyInputStateManager {
    val softKeyboardState = MutableStateFlow(MyPlatformTextInputService.KeyboardState.HIDDEN)
    val inputState = MutableStateFlow<MyPlatformTextInputService.InputState>(MyPlatformTextInputService.InputState.Idle)

    fun hideSoftwareKeyboard() {
        softKeyboardState.value = MyPlatformTextInputService.KeyboardState.HIDDEN
    }

    fun showSoftwareKeyboard() {
        softKeyboardState.value = MyPlatformTextInputService.KeyboardState.SHOWN
    }

    fun startInput(
        value: TextFieldValue,
        imeOptions: ImeOptions,
        onEditCommand: (List<EditCommand>) -> Unit,
        onImeActionPerformed: (ImeAction) -> Unit,
    ) {
        inputState.value = MyPlatformTextInputService.InputState.Active(
            textFieldValue = value,
            onEditCommand = onEditCommand,
            onImeActionPerformed = onImeActionPerformed,
            imeOptions = imeOptions,
        )
        softKeyboardState.value = MyPlatformTextInputService.KeyboardState.SHOWN
    }

    fun stopInput() {
        inputState.value = MyPlatformTextInputService.InputState.Idle
    }

    fun updateState(oldValue: TextFieldValue?, newValue: TextFieldValue) {
        (inputState.value as? MyPlatformTextInputService.InputState.Active)?.let { activeInputState ->
            inputState.value = activeInputState.copy(textFieldValue = newValue)
        }
    }
}

2. 实现自定义PlatformTextInputModifierNode

创建自定义Modifier节点,关联状态管理类并实现官方接口:

internal class MyTextInputNode(
    private val inputStateManager: MyInputStateManager
) : ModifierNode, PlatformTextInputModifierNode {

    private var textInputSession: PlatformTextInputSession? = null

    override val platformTextInputSession: PlatformTextInputSession?
        get() = textInputSession

    override fun onDetach() {
        textInputSession?.close()
        textInputSession = null
    }

    override fun requestSoftwareKeyboardShow() {
        inputStateManager.showSoftwareKeyboard()
    }

    override fun requestSoftwareKeyboardHide() {
        inputStateManager.hideSoftwareKeyboard()
    }

    override fun startInput(
        value: TextFieldValue,
        imeOptions: ImeOptions,
        onEditCommand: (List<EditCommand>) -> Unit,
        onImeActionPerformed: (ImeAction) -> Unit
    ) {
        inputStateManager.startInput(value, imeOptions, onEditCommand, onImeActionPerformed)
        // 创建输入会话,处理系统IME交互(按需实现)
        textInputSession = object : PlatformTextInputSession {
            override fun updateState(newValue: TextFieldValue) {
                inputStateManager.updateState(null, newValue)
            }

            override fun close() {
                inputStateManager.stopInput()
            }
        }
    }

    override fun stopInput() {
        inputStateManager.stopInput()
        textInputSession?.close()
        textInputSession = null
    }

    override fun updateInputState(oldValue: TextFieldValue?, newValue: TextFieldValue) {
        inputStateManager.updateState(oldValue, newValue)
    }
}

// 扩展Modifier方法,简化使用
fun Modifier.myTextInputService(inputStateManager: MyInputStateManager): Modifier =
    this then modifierNodeOf { MyTextInputNode(inputStateManager) }

3. 修改MyKeyboardContainer使用新架构

移除CompositionLocal依赖,改为通过Modifier注入输入服务:

@Composable
fun MyKeyboardContainer(
    modifier: Modifier = Modifier,
    closeKeyboardClickOutside: Boolean = true,
    content: @Composable ColumnScope.(MyInputStateManager) -> Unit,
) = Column(modifier = modifier) {
    val focusManager = LocalFocusManager.current
    val clickableModifier = if (closeKeyboardClickOutside) {
        Modifier.clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) { focusManager.clearFocus(force = true) }
    } else {
        Modifier
    }

    val inputStateManager = remember { MyInputStateManager() }

    Column(
        modifier = Modifier
            .weight(1f)
            .fillMaxWidth()
            .then(clickableModifier)
            .myTextInputService(inputStateManager),
    ) {
        content(inputStateManager)
    }

    MyKeyboard(windowSize)
}

4. 自定义TextField适配

在你的自定义TextField组件中,直接应用该Modifier即可让CoreTextField通过节点获取输入服务。


方案说明

  • 过渡方案可快速兼容现有代码,但属于临时妥协,后续版本可能移除遗留API
  • 正式方案符合Compose最新架构设计,基于Modifier节点的实现更贴合组件化状态管理理念,推荐逐步迁移

内容的提问来源于stack exchange,提问作者MisterFlyBear

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 14:54:51