One Off the Slack: Visibility and Declarative Thinking
Alex Gherschon asked:
Is there an equivalent to
View.setVisibility
on Composables? I’d like to hide a Composable when needed but not just change its opacity because this means it still catches onClicks!
And, later, after some explanation, he concluded:
Yes, it seems that 10 years of non-declarative UI molded my brain to think in a certain way 😂
Android developers are used to views having state, such as “am I visible?”, and to change the visual effect we change that state.
Compose is “declarative”, which in this context means “we describe the UI that we want”. So, if this time we do not want this particular composable to be seen, we simply do not include it this time in the composable tree that our functions build up. So long as something triggers the recomposition, Compose gets the new tree and figures out how to modify what we have drawn on the screen to reflect the difference between the old and the new tree.
If you have used RecyclerView
and its take on ListAdapter
, ListAdapter
offers
a precursor to this sort of approach. We just hand the ListAdapter
our new set
of data. ListAdapter
, along with a “differ” object, determine which items in the
RecyclerView
have changed as a result of our data change, and ListAdapter
then
tells RecyclerView
to re-render those items. We do not do that low-level work
ourselves: we declare “this is the new set of data”, and ListAdapter
figures out
how to do that as efficiently as possible.
Or, consider git
. In the end, git
works off of patch sets, describing which lines
in text files need to be replaced by other lines. However, as developers, we do not
work with those patch sets directly very much. Instead, we commit
changes to a source
tree, and git
figures out a patch set that represents the difference betwen what
we had before and this new set of files. While patch sets represent commands for how
to change the text, we instead “declare” that this is our new code, and we rely
on git
to figure out how to represent that change efficiently.
In terms of Compose, our composables do not know or care what our previous UI
rendering was. Instead, we say “this is what we want rendered now”, and we let
the Compose runtime (plus code-generated assistance code) figure out how to do
that efficiently.
So, instead of having a Button
property or variable that we manipulate visibility
based on input (e.g., some isTheButtonNeeded
boolean value), we use that input
to branch in our code:
if (isTheButtonNeeded) {
Button(...) {
// TODO
}
} else {
// never mind, we do not need the button
}
Or, as Brandon McAnsh put it:
Composable’s have the ability to recompose by design. If you don’t need the view to be present simply don’t add it to the tree
It will take time for developers coming from a classic Android View
background
to get used to this approach. Declarative UIs require a slightly different way
of thinking about the problem than what we used before.
Read the original thread in the kotlinlang Slack workspace. Not a member? Join that Slack workspace here!