0

I'm having a small problem with my implementation of this tutorial:

I'm trying to position a Column to the bottom of the entire screen, and then when a text field is focused, I want the Column to rise above the keyboard.

I have achieved the following with this code:

@OptIn(ExperimentalFoundationApi::class, ExperimentalLayoutApi::class)
@ExperimentalComposeUiApi
fun Modifier.bringIntoViewAfterImeAnimation(): Modifier = composed {
    var focusState by remember { mutableStateOf<FocusState?>(null) }
    val relocationRequester = remember { BringIntoViewRequester() }

    val isImeVisible = WindowInsets.isImeVisible

    LaunchedEffect(
        isImeVisible,
        focusState,
        relocationRequester
    ) {
        if (isImeVisible && focusState?.isFocused == false) {
            relocationRequester.bringIntoView()
        }
        relocationRequester.bringIntoView()
    }

    bringIntoViewRequester(relocationRequester).onFocusChanged { focusState = it }
}

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun SpaceCreator() {
    val localFocusManager = LocalFocusManager.current

    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Bottom
    ) {
        Column(
            modifier = Modifier.bringIntoViewAfterImeAnimation()
                .background(color = Color.Gray.copy(alpha = 0.2f))
        ) {
            Text(
                modifier = Modifier.fillMaxWidth()
                    .background(color = Color.Yellow.copy(alpha = 0.5f)),
                text = "Top Text",
                style = MaterialTheme.typography.h2
            )
            Text(text = "Content", style = MaterialTheme.typography.h2)

            OutlinedTextField(
                value = "ss",
                onValueChange = { },
                label =  { Text("Email Address") },
                singleLine = true,
                modifier = Modifier.fillMaxWidth(),
                keyboardOptions = KeyboardOptions(
                    keyboardType = KeyboardType.Email,
                    imeAction = ImeAction.Done,
                ),
                keyboardActions = KeyboardActions(
                    onDone = { localFocusManager.clearFocus() }
                )
            )

            OutlinedTextField(
                value = "ss",
                onValueChange = { },
                label =  { Text("Email Address") },
                singleLine = true,
                modifier = Modifier.fillMaxWidth(),
                keyboardOptions = KeyboardOptions(
                    keyboardType = KeyboardType.Email,
                    imeAction = ImeAction.Done
                ),
                keyboardActions = KeyboardActions(
                    onDone = { localFocusManager.clearFocus() }
                )
            )

            Text(text = "Content", style = MaterialTheme.typography.h2)
        }
    }
}

And when I tap on the first, or second text field, I get this desired result: enter image description here

However, when I tap on another text field, and the focus changes, the Column repositions again, like so: enter image description here

And the content of the Column moves under the keyboard.

I'm getting it's because the animation is occurring on the event of a focus change, but I only want it to happen if the IME is not active, and then I want everything to stay the same.

I'm not quite sure what to do, but I've only gotten as far as to change the text fields' keyboard actions to:

imeAction = ImeAction.Done

and

keyboardActions = KeyboardActions(
     onDone = { localFocusManager.clearFocus() }
)

But the user can still tap on another text field, which I need them to do, so this will break the usability. How can I fix this?

Any help would be appreciated, thank you and let me know if I can offer any more information.

Heron. F
  • 232
  • 3
  • 13

1 Answers1

1

This medium article solved the issue for me

https://medium.com/tech-takeaways/automatic-scrolling-to-composable-on-focus-change-with-bringintoviewrequester-in-jetpack-compose-bdeb72242bac

example code:

val coroutineScope = rememberCoroutineScope()
val bringIntoViewRequester = remember { BringIntoViewRequester() }

Column(modifier = Modifier.bringIntoViewRequester(bringIntoViewRequester)) {
    BasicTextField(
            modifier = modifier
                .onFocusEvent { state ->
                    if (state.hasFocus || state.isFocused) {
                        coroutineScope.launch {
                            bringIntoViewRequester.bringIntoView()
                        }
                    }
                },
                ...
    )
}