How To Collect Flows Lifecycle-Aware In Jetpack Compose
Why it’s important to consider the app’s lifecycle also in Jetpack Compose and how you can easily take care of it

In a previous article, I talked about how to facilitate your everyday life as a developer when it comes to collecting Flows
while considering the app’s lifecycle with the help of some handy extension functions.
However, that article focused only on handling the collection of Flows
in the legacy XML way of defining UIs.
But the requirement to take care of the app’s lifecycle doesn’t stop with the usage of Jetpack Compose.
While composables have their own lifecycle, which leads finally to the requirement of converting our streams to State
objects, we still need to take the actual app’s lifecycle when it comes to observing or, in the case of Flow
objects, collecting streams.
Therefore, in this follow-up article, I want to share some useful helper functions that will allow you to easily collect Flow
streams in your composables while automatically taking care of the app lifecycle.
1. Lifecycle-Awareness in Jetpack Compose
You may ask yourself the following:
“Why do we even need to consider the app’s lifecycle if composables have their own anyway?”
While it's true that composables have their own lifecycle for rendering the UI and maintaining their state, the app lifecycle still remains.
Just like in the legacy XML way of defining UIs, we still need to take care of cases for example in which the app goes to the background or simply just stops while still collecting streams.
As already mentioned, in Jetpack Compose we often don’t directly use the items of a stream but convert them into a State
.
Because we still listen to streams that potentially can be triggered while the app is not present or remain over the app lifecycle without getting properly handled, we need to introduce a new toolkit of functions on how to handle these cases.
2. Collect a Flow in a composable
So how do we collect a Flow
and convert into a State
that we can use in our Jetpack Compose code?
Let’s assume that we have a Flow
that comes from the data layer, for example from our Android Room Database, and gets forwarded through our ViewModel
.
Finally, we want to display the list of example entities in our UI by collecting the Flow
in our UI or more precisely in our composable.
Conventionally, we would achieve that by converting the Flow
to a State
object in our composable with the help of the Flow
extension function collectAsState()
like you can see in the following snippet:
As you can see we just need to call that function via the by
delegate. We then no longer need to handle the underlying State
because it directly gets evaluated to a list of entities.
We can then just pass the List
to our content composable that is responsible for displaying the UI.
You will probably recognize that this approach is very easy to use and handy to manage. Unfortunately, if we use this without further do, there is a danger that the Flow
will be collected even if the app runs in the background.
2. Collect a Flow lifecycle-aware in a composable
So how do we now achieve lifecycle-awareness? As I showed you in my mentioned previous article, we could try to use the repeatOnLifecycle()
function. But that makes no sense for our use case at this point.
As Manuel Vivo stated in his article “A safer way to collect flows from Android UIs” we can use the Flow
extension function flowWithLifecycle()
to achieve the same requirements.
How he states, the function is implemented using the repeatOnLifecycle()
function and therefore can manage the collection of the Flow
while considering the state of the app’s Lifecycle
.
So to use this function, we are no longer able to directly collect the Flow
from our ViewModel
but need to wrap it in an own remember statement before we can use our collectAsState()
property delegation once again.
The code snippet above now shows our previous example but with the conversion of the Flow
to be lifecycle-aware. We now invoke our collectAsState()
function no longer directly on the Flow
from our ViewModel
itself but on the Flow
called with the flowWithLifecycle(..)
function.
The collection now only proceeds if the Lifecycle
is at least in the Lifecycle.Started
state.
But if you ask me, that is quite a lot of boilerplate code, don’t you think? Imagine you have not one but five or more Flow
objects you first need to convert before you can safely collect them.
There must be a better way!
3. Extension functions to the rescue
Optimally, we would have the ability to use the presented collectAsState()
function and automatically collect the respective Flow
lifecycle-aware.
So let’s try to achieve that.
3.1 The Flow transformation
The transformation of the flow into a lifecycle-aware form is performed in the same way for each flow. So here is our first approach for improvement potential.
Take a look at the following code snippet and compare it with the transformation in the code snippet shown in section 2.
Do you recognize it? We now just take the Flow
we want to transform, directly pass it as key1 to the remember function, and apply the flowWithLifecycle()
function to it.
The Flow
collection can now already be reduced to the following code snippet:
Very nice, we can now just use our rememberFlow()
function with the respective Flow
as input parameter and collect the produced Flow
and are automatically lifecycle-aware.
But it’s still two instead of one line of code for every Flow
collection. Let’s try to achieve our initial optimal requirement
“we would have the ability to use the presented
collectAsState()
function and automatically collect the respectiveFlow
lifecycle-aware.”
3.1 The Flow collection
Every Flow
collection will need the step to use our newly created rememberFlow
function. So maybe we could just merge it with the collectAsState()
function?
Consider the following code snippet:
If you step into the implementation of the collectAsState()
extension function, you will recognize what we did here.
We just replicated and put our rememberFlow()
function in front before we return the State
we can then evaluate using the by
delegate in our composable.
Take a look at the following example to see the result:
We did it!
We can now collect the Flow
lifecycle-aware with just one line of code.
4. Bonus: Collect a StateFlow lifecycle-aware in a composable
If you try to use the presented collectAsStateLifecycleAware()
extension function, you will see that even if you declared an initial parameter in the initialization of the MutableStateFlow
, the collectAsStateLifecycleAware()
still will need you to provide an initial parameter once again on the UI layer or to be more specific in the composable.
That is a little bit annoying and can lead to higher maintenance effort if you always need to take care of the initial value at two different places for the same StateFlow
.
Let’s adapt the function, and convert it to an extension function specially for the StateFlow
.
As you can see we once again use our collectAsStateLifecycleAware()
function.
Since we are in the scope of the StateFlow
, we can now access its value
and set it directly as the initial
value of the collectAsStateLifecycleAware()
function and therefore as the initial value of the underlying produced State
object.
Glossary
Finally, the following code snippet shows all presented helper and extension functions at a glance:
Update 17.07.2022
As time goes on, new capabilities arise. That’s why I’ve written a follow-up article on this topic in the meantime that you should check out!
Conclusion
Considering the lifecycle when it comes to observing or collecting states has always been quite a hustle.
Helper functions like repeatOnLifecycle
or flowWithLifecycle
on the other hand, provide some very handy methods to make our apps more robust regarding sudden changes in the state of our apps.
By using Kotlin’s Delegate properties and extension function we can use them to easily integrate them in our architecture and therefore in our daily development process.
Using the provided functions, there is no further boilerplate coded needed to achieve lifecycle-awareness when it comes to collecting Flows
in your composables.
If you are already using the features I hope that the presented functions might help you to clean up your code a bit or otherwise inspire you to check your app for lifecycle-awareness.
I hope you had some takeaways, clap if you liked my article, make sure to subscribe to get notified via e-mail, and follow for more!