简体   繁体   中英

Composable reparenting in Jetpack Compose

Is there a way to reparent a Composable without it losing the state? The androidx.compose.runtime.key seems to not support this use case.

For example, after transitioning from:

// This function is in the external library, you can not 
// modify it!
@Composable
fun FooBar() {
    val uid = remember { UUID.randomUUID().toString() }
    Text(uid)
}
Box {
  Box {
    FooBar()
  }
}

to

Box {
  Row {
    FooBar()
  }
}

the Text will show a different message.


I'm not asking for ways to actually remember the randomly generated ID, as I could obviously just move it up the hierarchy. What I want to archive is the composable keeping its internal state.

Is this possible to do without modifying the FooBar function?

The Flutter has GlobalKey specifically for this purpose. Speaking Compose that might look something like this:

val key = GlobalKey.create()
Box {
  Box {
    globalKey(key) {
      FooBar()
    }
  }
}
Box {
  Row {
    globalKey(key) {
      FooBar()
    }
  }
}

remember will store only one value in the same view. The key in Compose has a very different purpose: if the key passed to remember has a different value from the last recomposition, it means that the old value is no longer relevant and must be recomputed.

There is no direct equivalent of Flutter keys in Compose.

You can simply declare a global variable. In case you need to change it, wrap it with a mutable state, so changes will update your view.

var state by mutableStateOf(UUID.randomUUID().toString())

I'm not sure if that the same what GlobalKey does, in any case it's not the best practice , just like any other global variable.


If you need to share some data between views, it is much cleaner to use view models .

@Composable
fun TestScreen() {
    val viewModel = viewModel<SomeViewModel>()
    Column {
        Text("TestScreen text: ${viewModel.state}")
        OtherView()
    }
}

@Composable
fun OtherView() {
    val viewModel = viewModel<SomeViewModel>()
    Text("OtherScreen text: ${viewModel.state}")
}

class SomeViewModel: ViewModel() {
    var state by mutableStateOf(UUID.randomUUID().toString())
}

The hierarchy topmost viewModel call creates a view model - in my case inside TestScreen . All children that call viewModel of the same class will get the same object. The exception to this is different destinations of Compose Navigation, see how to handle this case in this answer .

You can update the mutable state value, and it will be reflected on all views using that model. Check out more about state in Compose .

When the view that created the view model is removed from the view hierarchy, the view model is also freed, so a new one will be created next time.

There's no such functionally in Compose and for now it is not planned. 😔

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM