0

I'm making a calculator to learn Compose, so I placed my own number buttons on screen and I wanted to prevent the soft keyboard from appearing.

Here is my repo: https://github.com/vitor-ramos/CalculadorCompose

I noticed in TextFieldImpl.kt there is a modifier to show the keyboard, so I tried to clone the code and remove the line: keyboardController.value?.showSoftwareKeyboard() I know it's not a good idea to duplicate code like that, but I wanted to give it a try, and it didn't work. As you can see in the original code below there's a TODO saying it should be handled by BaseTextField, but I looked in it's code and didn't find where it shows or hides the keyboard.

val textFieldModifier = modifier
    .focusRequester(focusRequester)
    .focusObserver { isFocused = it.isFocused }
    .clickable(indication = null) {
        focusRequester.requestFocus()
        // TODO(b/163109449): Showing and hiding keyboard should be handled by BaseTextField.
        //  The requestFocus() call here should be enough to trigger the software keyboard.
        //  Investiate why this is needed here. If it is really needed, instead of doing
        //  this in the onClick callback, we should move this logic to the focusObserver
        //  so that it can show or hide the keyboard based on the focus state.
        keyboardController.value?.showSoftwareKeyboard()
    }

I found in this question that with views I can extend EditText and change the functionality, but I haven't found a equivalent for Compose: Android: Disable soft keyboard at all EditTexts

public class NoImeEditText extends EditText {
    public NoImeEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    public boolean onCheckIsTextEditor() {
        return false;
    }
}
Vitor Ramos
  • 589
  • 3
  • 13
  • Never used compose myself, but I'm quite interested. I watched a couple of talks and if I understand them, wouldn't it just be a matter of using `NoImeEditText` as a `@Composable` method? I mean in a custom composable. – Fred Sep 14 '20 at 15:32
  • As I understand, Compose isn't built on top of traditional views, it's a completely new UI toolkit, so I cannot make a composable function based on a traditional view. – Vitor Ramos Sep 14 '20 at 15:37
  • oh I see, my bad then. – Fred Sep 14 '20 at 15:50
  • why do you use TextField() if you don't expect user input directly in TextField() ? Is it possible to replace TextField() with Text()? – Eric Cen Sep 15 '20 at 18:28
  • I want the user to be able to use selection like usually in a TextField, besides the visuals, basically I want every thing the TextField has to offer, except for the soft keyboard opening on focus – Vitor Ramos Sep 16 '20 at 21:25
  • Basically you want to replicate what the Android calculator does, right? – Fred Sep 17 '20 at 05:53
  • Yes, I actually tried that because I thought it would be a simple one. – Vitor Ramos Sep 17 '20 at 13:29
  • There's an issue for that in compose's issue tracker. I'll update here when it releases. https://issuetracker.google.com/issues/169035120 – Vitor Ramos Sep 29 '20 at 17:40

1 Answers1

2

Explanation

I created a Composable ReadonlyTextField, that places a invisible box in front of the text field. The box has the same size as the text field.

With that workaround you can't focus the text field anymore, so no keyboard appears. In order to apply custom click handling, i added a onClick to the Box-Modifier.

This is not really a clean solution, but a good workaround.

Implementation of ReadonlyTextField

@Composable
fun ReadonlyTextField(
    value: TextFieldValue,
    onValueChange: (TextFieldValue) -> Unit,
    modifier: Modifier = Modifier,
    onClick: () -> Unit,
    label: @Composable () -> Unit
) {

    Box {
        TextField(
            value = value,
            onValueChange = onValueChange,
            modifier = modifier,
            label = label
        )

        Box(
            modifier = Modifier
                .matchParentSize()
                .alpha(0f)
                .clickable(onClick = onClick),
        )
    }
}

Usage of ReadonlyTextField

@Composable
fun App() {
    val textState = remember { mutableStateOf(TextFieldValue()) }

    Column {
        ReadonlyTextField(
            value = textState.value,
            onValueChange = { textState.value = it },
            onClick = {
                // custom click handling (e.g. open dialog)
            },
            label = {
                Text(text = "Keyboardless Input")
            }
        )
    }
}

A complete integrated example can be found in my medium post: https://caelis.medium.com/jetpack-compose-datepicker-textfield-39808e42646a

Credits also go to this stackoverflow answer: Jetpack Compose: Disable Interaction with TextField

Caelis
  • 36
  • 2
  • Some time ago I opened an issue for this and the Compose team answered me telling they are changing the whole focus API, which will include a way to not show the keyboard o text input focus, but thanks for your answer. – Vitor Ramos Feb 08 '21 at 13:28