Fun with Compose: Bad UI in a Great Framework
Tags: android, compose, ux, jetpackcompose
Published: 2021-07-30 15:03:14

## Intro
To celebrate Jetpack Compose finally hitting 1.0, I wanted to take a bit of a deep dive to see what the framework has to offer. Instead of more of the same code labs with useful UI, I decided to bring back an old reddit trend of creating the absolute worst volume control UI possible ([highlights here](https://uxdesign.cc/the-worst-volume-control-ui-in-the-world-60713dc86950)). You can take a look at the code on my Github. {% github mrf7/volume-gore %}

## The Results
Microtransactions | Radio Buttons | Catapult (WIP)
-- | -- | --
<img src="https://media.giphy.com/media/gCeelw0MJgkjCQ7fiF/giphy.gif"> | <img src="https://media.giphy.com/media/qbKWJdGpLOBxZvR70o/giphy.gif"> |  <img src="https://media.giphy.com/media/VWD8QmlLT0h2Ymw2kW/giphy.gif">

Digits Of PI | Seven Segment
-- | --
<img src="https://media.giphy.com/media/8h1loDBBTvdzGyI3Re/giphy.gif"> | <img src="https://media.giphy.com/media/dLYhwt8pRCIQPwu30m/giphy.gif"> 


## Takeaways
As someone who’s never really been a fan of UI work, building UI in Compose has been extremely pleasant and straightforward. Things like custom animations that I haven't done in the View system were surprisingly simple and concise. With Compose I no longer need to context switch between writing Kotlin code and XML layouts, the flow between writing backend code and UI code is much more seamless. The best part was the often lauded `LazyColumn` in compose that turns 3 files worth of boilerplate `RecyclerView` code into a couple lines. While it took some effort to change my approach for the Compose mental model, this is a huge leap in the right direction towards making UI work quicker for experienced devs and easier for new devs to learn. 
{% gist https://gist.github.com/mrf7/11bdfc86c60bd32fa61c99ed41d535c5 %}


## Lessons Learned
A vital concept when learning Compose is how the runtime manages state and recomposition via `(Mutable)State` objects. Most of the "magic" of recomposition relies on the `getValue` and `setValue` methods of these classes. and if you mistakenly go around these methods to read and update your apps state, you can actually prevent the UI from updating as you intended. 

I learned this the hard way while implementing the editable seven segment display by using a `MutableState<MutableList>` and updating the underlying `MutableList` values on user interaction (i.e. `state.value[index] = newValue`). Desugaring that statement to `state.getValue()[index] = newValue` we see that we're never calling `MutableState.setValue`, so the Compose runtime has no indication of the underlying value change, never triggering a recomposition. On top of that, even if something else triggered a recomposition, the UI wouldn't be updated since we mutated the list without changing the reference, so the underlying equality check used to determine if our composable should run again  never detects a change.

Luckily,Compose gives us a `mutableStateListOf(...)` and `List.toMutableStateList()` that return a `SnapshotStateList`, which is a subclass of `MutableList` (and equivalents for `Maps`). That means we can just treat it as a  `MutableList` and the state updates will be handled for us when we do things like `snapshotStateList[index]  = newValue`. The main rule of thumb to learn from this is that in Compose you should avoid mutating objects you intend to use as a state and instead emit a brand new value to your LiveData/Flow/State using the `copy` method on data classes. You can force this practice on yourself by avoiding `var`s entirely in classes used for UI state. 

For a demonstration of this issue, here’s a snippet showing my initial `MutableList` approach, a functional workaround I made using a list of mutable states, and the proper solution using `mutableStateListOf()`. 
{% gist https://gist.github.com/mrf7/9fca2cafee3b1b3c3d48f606091d5a2b %}

## Conclusion
Now that Compose is officially at 1.0, you should give it a shot ASAP. Wherever you update the state of your UI, keep an eye out for spots that mutate the value in the `State` instead of the `State` itself. Above all, don't be afraid to get creative and have fun when playing with compose, you might be surprised by the things you're able to make easily compared to the old View system.