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!