'Jetpack compose - how do I refresh a screen when app returns to foreground
I need to automatically refresh an Android Compose screen when the app returns to the foreground.
I have an that requires permissions and location services.
If the user has switched any of these off a list is drawn of the items that need to be changed. When the user goes to Settings and the app returns to the foreground I would like the list to refresh to reflect the changes.
I am using Compose and Compose navigation. I have looked and I can't figure out the equivalent of onResume lifecycle event that could be used to trigger the refresh.
Any ideas would be gratefully received as I am at a loss.
Solution 1:[1]
Edit: If you want a "pure" compose answer, check @JoJoIV 's answer
Answer:
Compose is not aware of state changes like onPause
or onResume
, you have to handle it using the parent activity's methods.
An example would be a LiveData
instance in your activity that updates each time onResume
is executed and observe it as a state in your main parent composable.
Let's take a look at the following example:
class MainActivity : AppCompatActivity() {
// Use whatever type your prefer/require, this is just an example
private val exampleLiveData = MutableLiveData("")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// Your main composable
MyApplicationTheme {
// Save the state into a variable otherwise it won't work
val state = exampleLiveData.observeAsState()
Log.d("EXAMPLE", "Recomposing screen - ${state.value}")
Surface(color = MaterialTheme.colors.background) {
Greeting("Android")
}
}
}
}
override fun onResume() {
super.onResume()
// Save whatever you want in your live data, this is just an example
exampleLiveData.value = DateTimeFormatter.ISO_INSTANT.format(Instant.now())
}
}
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
MyApplicationTheme {
Greeting("Android")
}
}
As you can see in this example, I have a LiveData
property in my activity that containts a String. Whenever onResume
is executed the property is updated with the new timestamp and the observing composable is recomposed.
Solution 2:[2]
I came up with this:
@Composable
fun OnLifecycleEvent(onEvent: (owner: LifecycleOwner, event: Lifecycle.Event) -> Unit) {
val eventHandler = rememberUpdatedState(onEvent)
val lifecycleOwner = rememberUpdatedState(LocalLifecycleOwner.current)
DisposableEffect(lifecycleOwner.value) {
val lifecycle = lifecycleOwner.value.lifecycle
val observer = LifecycleEventObserver { owner, event ->
eventHandler.value(owner, event)
}
lifecycle.addObserver(observer)
onDispose {
lifecycle.removeObserver(observer)
}
}
}
It seems to work just fine. But there may be some issues in some cases so be careful.
It is also possible that there is some redundant code.
Usage:
OnLifecycleEvent { owner, event ->
// do stuff on event
when (event) {
Lifecycle.Event.ON_RESUME -> { /* stuff */ }
else -> { /* other stuff */ }
}
}
Solution 3:[3]
I slightly improved @JojoIV answer and made it flat usage without callback like you observe LiveData
in compose what @Abdelilah El Aissaoui answered
@Composable
fun Lifecycle.observeAsState(): State<Lifecycle.Event> {
val state = remember { mutableStateOf(Lifecycle.Event.ON_ANY) }
DisposableEffect(this) {
val observer = LifecycleEventObserver { _, event ->
state.value = event
}
[email protected](observer)
onDispose {
[email protected](observer)
}
}
return state
}
and then usage
@Composable
fun SomeComposable() {
val lifecycleState = LocalLifecycleOwner.current.lifecycle.observeAsState()
val state = lifecycleState.value
// or val lifecycleState by LocalLifecycleOwner.current.lifecycle.observeAsState()
// will re-render someComposable each time lifecycleState will change
}
Solution 4:[4]
example from google site
@Composable
fun HomeScreen(
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
onStart: () -> Unit, // Send the 'started' analytics event
onStop: () -> Unit // Send the 'stopped' analytics event
) {
// Safely update the current lambdas when a new one is provided
val currentOnStart by rememberUpdatedState(onStart)
val currentOnStop by rememberUpdatedState(onStop)
// If `lifecycleOwner` changes, dispose and reset the effect
DisposableEffect(lifecycleOwner) {
// Create an observer that triggers our remembered callbacks
// for sending analytics events
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_START) {
currentOnStart()
} else if (event == Lifecycle.Event.ON_STOP) {
currentOnStop()
}
}
// Add the observer to the lifecycle
lifecycleOwner.lifecycle.addObserver(observer)
// When the effect leaves the Composition, remove the observer
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
/* Home screen content */
}
Full description of how it works in google site https://developer.android.com/jetpack/compose/side-effects#disposableeffect
Solution 5:[5]
i changed @ojoIV code to this (if your composable code is in Activity)
@Composable
fun ComponentActivity.LifecycleEventListener(event: (Lifecycle.Event) -> Unit) {
val eventHandler by rememberUpdatedState(newValue = event)
val lifecycle = [email protected]
DisposableEffect(lifecycle) {
val observer = LifecycleEventObserver { _, event ->
eventHandler(event)
}
lifecycle.addObserver(observer)
onDispose {
lifecycle.removeObserver(observer)
}
}
}
usage
LifecycleEventListener(event = { lifecycleEvent ->
when (lifecycleEvent ) {
Lifecycle.Event.ON_CREATE -> {}
Lifecycle.Event.ON_START -> {}
Lifecycle.Event.ON_RESUME -> {}
Lifecycle.Event.ON_PAUSE -> {}
Lifecycle.Event.ON_STOP -> {}
Lifecycle.Event.ON_DESTROY -> {}
else -> return@LifecycleEventListener
}
})
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | |
Solution 3 | Jakoss |
Solution 4 | Sergei S |
Solution 5 |