Hi, does anyone know how to save/retain keyboard f...
# compose
m
Hi, does anyone know how to save/retain keyboard focus and cursor position for a
TextField
? I'm having a list screen with a search option and showing either list or error screen below the search box based on search results. It roughly looks like this for list state
Column{
SearchBox()
List()
}
and like this for error state
Column{
SearchBox()
Text("No results")
}
SearchBox
has
BasicTextField
and other decorations around it. Now when I start typing in the search box, the keyboard state and cursor position is retained for recompositions of the list screen update, but when the error state is shown, the keyboard is being closed and the
TextField
loses focus. The same happens when transitioning from error state to list screen.
z
At least two solutions: 1. You could hoist your
TextFieldValue
up so the same value is used by the search boxes in both composables. This would fix cursor, but not focus. 2. Hoist the
SearchBar
composable itself up so only the list contents beneath it change between error and success states, and the
SearchBar
is not recreated. This should fix cursor and focus.
m
1. Thanks, I guess I never checked about
TextFieldValue
. I directly hoisted the string value and used that. I will try this. 2. This approach seems interesting, I'll try this too. My code for the snippet I shared actually looks like more this.
Copy code
Column{
  if(hasData){
    DataView()
  } else {
   ShowError()
  }
}

fun DataView(){
 SearchBox()
 List()
}

fun ShowError(){
  SearchBox()
  Text("No results")
}
I guess the
SearchBox
composable is created fresh when the transition from error to success state or vice versa happens instead of using older one with just recomposition? I'm not sure how the composables are handled internally which decides whether to recompose or just dispose of the previous one and create a fresh instance. Is this documented somewhere?
z
Yes. A composable’s “identity” is basically its location in the call graph, i.e. where the function is called in the source code. So the same composable called from two different places, even if it ends up in the same place in the layout, will be two completely separate composables, with new internal state, etc.
E.g. even in this case:
Copy code
if (condition) SearchBox() else SearchBox()
If
condition
changes, those search boxes will be destroyed and re-created every time.
It’s also a bit sketchy imo to emit multiple nodes from a composable without a layout, especially when those composables assume a particular layout (e.g. a searchbox and list would look very odd in a row). So I would probably have done this anyway:
Copy code
Column {
  SearchBox()
  if(hasData){
    DataView()
  } else {
    Error()
  }
}
(Note that the Compose API guidelines recommend that composables that return Unit should be named as nouns, not verbs)
m
Thanks for the explanation. Regarding the layout and naming, I gave the example from mobile, hence no layout and improper naming. My actual code has proper layouts and proper naming.