3

I had a search view that worked fine but didn't keep the user input on back navigation (from detail view). According to Keep text in BasicTextField on back navigation, all i had to do was to change

remember

to

rememberSaveable

like so:

val textState = rememberSaveable { mutableStateOf(TextFieldValue(""))

But now I am getting this.

Compose Runtime internal error. Unexpected or incorrect use of the Compose internal runtime API (pending composition has not been applied)

enter image description here enter image description here

 TopAppBar(
            elevation = 0.dp,
            title = {},
            navigationIcon = {
                IconButton(onClick = {
                    scope.launch {
                        scaffoldState.drawerState.open()
                    }
                }) {
                    Image(
                        //some image gere
                    )
                }
            },
            backgroundColor = backgroundColor,
            actions = {
                val textState = rememberSaveable { mutableStateOf(TextFieldValue("")) }
                CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
                    SearchView(state = textState, viewModel)
                    //rest of code

and the search view (edited for brevity):

@Composable
fun SearchView(state: MutableState<TextFieldValue>, viewModel: viewModel) {
    val interactionSource = remember { MutableInteractionSource() }


    BasicTextField(
        value = state.value,
        onValueChange = { value -> state.value = value;  viewModel.search(state.value.text)} ,
// rest of code

This error was already discussed on stackoverflow, but not in relation to rememberSaveable, and no solutions offered there anyway.

Stackoveflow

Edit: I solved the problem by initializing the textState with the search text saved into the viewmodel. Works fine, but I'm not providing this as an answer to my own question, as it is a hack, but not the real solution. At least for now, if there is a real solution to this. But if this turns out to be a bug in Compose, then I guess it will be an answer.

val textState = remember { mutableStateOf(TextFieldValue(viewModel.filter)) }

z.g.y
  • 5,512
  • 4
  • 10
  • 36
Dmitri
  • 2,563
  • 1
  • 22
  • 30

1 Answers1

1

I'm not sure why your'e having this, maybe you have to check your compose dependencies versions?

But the error your'e actually having is

MutableState containing TextFieldValue(text='', selection=TextRange(0, 0), composition=null) cannot be saved using the current SaveableStateRegistry. The default implementation only supports types which can be stored inside the Bundle. Please consider implementing a custom Saver for this class and pass it as a stateSaver parameter to rememberSaveable().

You have to create a saver because rememberSaveable is something that survives configuration changes and there are certain data structures you have to tell the saver how it will save and restore it.

It behaves similarly to remember, but the stored value will survive the activity or process recreation using the saved instance state mechanism (for example it happens when the screen is rotated in the Android application).

@Composable
fun <T> rememberSaveable(
    vararg inputs: Any?,
    stateSaver: Saver<T, out Any>,
    key: String? = null,
    init: () -> MutableState<T>
): MutableState<T> = rememberSaveable(

Just create a custom saver

val textFieldValueSaver = run {
    val textKey = "text"
    mapSaver(
        save = {
            mapOf(textKey to it.text)
        },
        restore = {
            TextFieldValue(it[textKey] as String)
        }
    )
}

and just pass it to rememberSaveable's stateSaver parameter

val textState = rememberSaveable(
    stateSaver = textFieldValueSaver
) { mutableStateOf(TextFieldValue("")) }

Reference: Official Docs

z.g.y
  • 5,512
  • 4
  • 10
  • 36
  • thanks; is there any advantage to preferring this approach over what I did - val textState = remember { mutableStateOf(TextFieldValue(viewModel.filter)) }. ViewModel survives config changes. (upvoted your answer) – Dmitri Dec 09 '22 at 01:34
  • Thank you for upvoting the answer, it would also be great if you tick the check button accepting it. :). I think rememberSaveable is not needed here anymore since your'e already hoisted/handling your states in the viewModel since ViewModel inherently survives config changes, there's no advantage to it actually, it depends on your use-case, you can check my [answer here about state hoisting](https://stackoverflow.com/questions/74616402/what-where-is-the-best-way-to-handle-with-states-on-jetpack-compose/74616884#74616884) – z.g.y Dec 09 '22 at 02:08
  • 1
    thnx! makes sense. accepted and upvoted the other too. – Dmitri Dec 09 '22 at 02:13
  • Thank you!, just to summarize, these concepts are about state-hoisting and there are many great articles and S.O posts regarding this topic that you can look around. – z.g.y Dec 09 '22 at 02:16