compose-multiplatform icon indicating copy to clipboard operation
compose-multiplatform copied to clipboard

iOS. Support different `onFocusBehavior` for different screens in one `ComposeUIViewController`

Open igordmn opened this issue 2 years ago • 9 comments

Compose 1.5.0 introduces a new feature that allows to configure onFocusBehavior for text fields:

fun MainViewController() = ComposeUIViewController(configure = {
    onFocusBehavior = OnFocusBehavior.None
}) {
    App()
}

This useful if the app uses different ViewController's for different screens, but not useful, when the app is written in a single view controller, where different screens can require different behaviors. This will probably be the recommended approach in the future (it is not decided yet, but it is the recomended approach in Android).

  1. Figure out how Compose on Android solves the different behavior of keyboard in a single activity case.
  2. Does Compose for Android has a specific API, or users should use native API (window.setSoftInputMode) to dynamically change the behavior.
  3. Decide if a solution of supporting dynamic changes of onFocusBehavior will be enough (make it mutableStateOf), or we should introduce local declarative API (TextField(onFocusBehavior=)).

igordmn avatar Aug 21 '23 10:08 igordmn

https://github.com/JetBrains/compose-multiplatform/assets/3532155/1644eca2-c977-4d32-85c6-584ca14f0736

I investigated the problem. I guess we don't have to change the current behavior. onFocusBehavior flag is similar to Android's windowSoftInputMode. The requested feature (different behaviors for different Text fields on the same screen) can be achieved by setting a focus listener and an insets handler:

@Composable
internal fun App() = AppTheme {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.BottomCenter
    ) {

        val initialBottomPadding = 100.dp
        var f1IsFocused by remember { mutableStateOf(false) }
        var f1 by remember { mutableStateOf("") }
        OutlinedTextField(
            value = f1,
            onValueChange = { f1 = it },
            label = { Text("Movable TextField") },
            singleLine = true,
            modifier = Modifier
                .fillMaxWidth()
                .padding(start = 16.dp, end = 16.dp, bottom = initialBottomPadding)
                .onFocusChanged { f1IsFocused = it.isFocused }
                .let {
                    if (f1IsFocused) {
                        it.padding(bottom = max(0.dp, WindowInsets.ime.asPaddingValues().calculateBottomPadding() - initialBottomPadding))
                    } else {
                        it.padding(bottom = 0.dp)
                    }
                }
        )

        var f2 by remember { mutableStateOf("") }
        OutlinedTextField(
            value = f2,
            onValueChange = { f2 = it },
            label = { Text("Stable TextField") },
            singleLine = true,
            modifier = Modifier.fillMaxWidth().padding(16.dp)
        )
    }
}

terrakok avatar Oct 06 '23 09:10 terrakok

The requested feature (different behaviors for different Text fields on the same screen)

onFocusBehavior offsets/resizes the whole screen, not the component. This allows to show a proper scrolling, if we try to scroll with keyboard shown.

But it seems that indeed, users can implement something on the root level themselves.

But for that we need to either disable the default offseting (which isn't good) or provide an option to disable it. We done that we already done via onFocusBehavior.

The only issue I see now, that users have to write its own logic instead of using built-in one. It adds some code, and can differ.

If we don't have any more ideas what can be improved here, let's close it then until we have real cases?

igordmn avatar Oct 11 '23 16:10 igordmn

onFocusBehavior flag is similar to Android's windowSoftInputMode.

It can be changed dynamically compared to the confiugure approach that is called only on init

igordmn avatar Oct 11 '23 16:10 igordmn

Do you mean getWindow().setSoftInputMode(...)?

terrakok avatar Oct 12 '23 12:10 terrakok

Yes

igordmn avatar Oct 12 '23 12:10 igordmn

Is it possible to call from a compose code or you propose to add such an api to common compose?

terrakok avatar Oct 12 '23 12:10 terrakok

Is it possible to call from a compose code

Yes, users can pass Activity/Window in their Composables. But it is not a Compose API.

So, we have a situation that Android platform has a feature "change input mode dynamically", Compose doesn't have it, and user might need it.

We have options:

  • [easy] wait for a real user request
  • [normal] introduce ios-only API for entry-point properties changing. For example, we can make configure react to mutableStateOf, as UIKitView(update= does that.
  • [hard] introduce platform-independent Compose API (needs coordination with Google)

I would choose the first or the second.

igordmn avatar Oct 12 '23 13:10 igordmn

I guess it is not a popular API for compose apps. But I may be wrong. Let's wait for the user requests 👌

terrakok avatar Oct 14 '23 17:10 terrakok

Let's wait for the user requests 👌

Let's keep this issue open then.

igordmn avatar Oct 16 '23 01:10 igordmn

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.

okushnikov avatar Aug 23 '24 13:08 okushnikov