It is not a surprise anymore what the world of building UIs on mobile screens will be in the upcoming years, finally, we can get rid of all XML pain and rely on the reactive programming paradigm to not only build beautiful UI but also keep our code and architecture clean.
It’s been a month now since releasing the alpha version of Jetpack Compose, And it is pretty obvious that compose had been welcomed from the wider android community, Personally, I’m very excited about it and I play with it regularly to discover its new capabilities So, here in this article I’m gonna share some best practices and tips I found them very helpful when getting to work with compose. Let’s Start :)
1- Making custom states:
if you’ve ever worked with compose chances are you used state in many places you need scrollState? compose replies: we’ve got remeberScrollState
for you, swipeState? no problem, we have also rememberSwipeableState
For you. And state doesn’t just stop here for examples Colors
class implemented like this:
So, if I want to save and remember a state to make some decisions based on this state(e.g button is enabled when text isn’t empty) I can do it in fairly simple way like this:
We can take it one step further and implement our own rememberTextState
With that we keep “state” of our components in manageable maintainable way .
2- Stateful/Stateless Components:
continue speaking of states we can make our composable functions code much more simple and keep our architecture clean if we’ve leveraged stateless/stateful technique.
briefly, by applying this to any composable fun we’ll end up with two functions:
- one that is responsible for managing/controlling state of components displayed on screen but doesn’t actually display them
- the second function that is responsible for displaying components on screen but doesn’t need to worry about state
Seems ambiguous in theory, let’s take an example From some compose code I wrote for SunFlower app:
You get it!! the second composable(stateless) doesn’t know anything about state of our plants it just displays them, this also make stateless PlantListScreen
more reusable in other places of codebase
But wait!!! every best practice is good when you apply it correctly, Don’t throw stateless/stateful components everywhere in your code you’ll end up duplicating a lot of code leading to annoying boilerplate. Only apply this pattern with top-level composables(e.g SearchScreen
which displays the entire search Screen) but it seems inconvenient to do that with things like buttons or Texts.
3- Using layoutId to find or mark certain items:
Have you missed “android:id=…”, findViewById
and all that stuff related to ids in XML/view system? (I think no one would miss findViewById
we have ViewBinding 😁😁)
Well, compose has a handy fun called Modifier.layoutId()
it is very handy tip when you want to tell compose: hey, you remember the item with that id, GIVE IT TO ME
let’s take an example, consider if I want to make a custom layout and I want to measure and layout an item in different way than others, code will look something like this:
So, as you saw I can distinguish between many items and treat each one solely
4- Make reusable Composables generic as much as possible:
At first look, You may say this is trivial and intuitive thing to know, who can mistake in something like that? but I bet you’ll get amazed just like me when you look at how it is implemented in Rally (One of the official compose samples)
In this sample there are two screens(Accounts, Bills) have much in common in their structure, see GIF:
As you see, we need an agnostic shared composable able to display two screens, and here’s where StatementBody
come to play, its implementation is fairly simple just like that:
And so Accounts and Bills screens can be implemented like this:
Pretty neat!, also sample has beautiful animations I recommend you to check it out.
5- the power of Ambients
If I have to choose something compose can do it like a magic definitely it will be Ambients
Briefly if you don’t know what ambients is?
- it is a way to flow data down the tree to lower composables without leaking private implementation details. You’ll understand more with an example
We have ton of built-in Ambients like ContextAmbient
, DensityAmbient
…etc, And that is because implementing your custom ambient is quite easy, let’s take an example:
say if we want in some composable function to know current configuration of screen like screenWidth to make some calculations based on that, how it will look like in the code? (Note: compose has already a built-in ConfigurationAmbient
but let’s make simple one to understand the idea)
You see!! with just three or two lines of code we made our custom ambient that any composable can call it and tell it: hey, what is the current configuration of you because I really need to know the current width available
We can extend it more with ProxyPattern to make this configuration part of our theme, like:
With that our AppTheme
becomes a single source of truth for any unified shared Info.
This is the end of this article I hope you have now better understanding of how to make complex composables without sacrificing on things like architecture and if you want to deep dive into compose you may already know who to follow! it is him Leland Richardson. 👏 👏