One Off the Slack

These articles summarize the discussion on a particular thread in the #compose channel of JetBrains’ Slack workspace for Kotlin developers. If you would like to read the original thread, you will need access to that workspace — sign up at!

Immutability Is Your Friend

As with last week’s highlighted Slack thread, this one focuses on state change detection and mutability.

Colton Idle asked:

My activity has a field myTodoList mutableListOf and it has items added to it by another source (right now I have a post delayed handler that waits for 5 seconds, and then adds ten items) and I expect the items to update this Screen composable, but nothing shows.

Mutable stuff (var, MutableList, etc.) is fine, but Compose knows nothing about it. On the whole, Compose — particualarly State — works much better with immutable objects.

As Florian Schuster put it:

Your problem lies not in compose but with your list in your activity. The list needs to be observable if you want your composables to recompose on a change of the items in the list. Either use a Flow, LiveData or mutableStateOf(List), hand that over to your MyActivityScreen and observe it in there.

Colton then tried:

val listOfItems = mutableStateOf(mutableListOf<String>())



However, this still uses MutableList. State and MutableState know when you supply a fresh value, but they do not know when you change the internals of the existing value, such as adding an element to the MutableList.

Florian suggested:

val listOfItems = mutableStateOf(listOf<String>())


listOfItems.value = listOf("a", "b" )

And in the end that wound up being the solution.

In general, in Kotlin, prefer immutable things to mutable ones (val instead of var, List instead of MutableList). Unidirectional data flow architectures tend to rely on this convention, and Compose leans into it as well.

Posted 2020-09-27, based on

Stability Now!

Maik had a MutableList parameter to a MyList() composable and was wondering why MyList() would recompose, even if that MutableList did not change.

The problem is that while Maik knows that the list did not change, Compose does not. As Google’s Jim Sproch put it:

MyList(items) takes in a MutableList, which is (as the name suggests) mutable, so Compose can’t know if that list has been modified or not, so it needs to be conservative and rerun that code. If you use a list implementation that is stable (like SnapshotStateList) then MyList(items) is less likely to run.

Even a List would be insufficient, as that simply prevents one from modifying what elements are in the list — the contents of the elements themselves could change.

In general, composable functions work best with truly immutable contents, and that will require using types that Compose know are truly immutable (e.g., ones with the @Stable annotation).

Jim also points out a key risk:

…keep in mind that your code should NEVER make assumptions about when composables will run. Compose is free to run composable functions for any reason and at any time, at its sole discretion.

This has the potential of being an ongoing problem with Compose development, relying on side-effects from composition. A composable should be idempotent: whether it gets called zero, one, two, or a thousand times should have no impact on the rest of the app.

Posted 2020-09-20, based on

TextField and Names

Jan Skrasek asked:

Is there any issue/”initiative” to decompose TextField? Such simple thing as setting a background color seems quite impossible. (outline api doesn’t provide bg color property, filled text field applies 0.12% transparency on the passed value…) All building blocks are internal/private

The core problem is that TextField() is a part of the Material Design set of composables, whereas BaseTextField() is the non-Material basis for text entry. Or, as Google’s Ian Lake put it:

I mean, a Material TextField is a very, very specific thing with defined styles from the Material spec. BaseTextField is indeed the non-private building block you’d want to build your own custom styling on top of. Maybe also consider creating issues about what made it difficult to use BastTextField if you want a non-material style

Zhelyazko Atanasov then hit on the core problem: people not realizing that BaseTextField() exists and that TextField() represents a materialized approach:

It’s a bit confusing having a specific implementation of a component (the Material implementation) named after the generic component - TextField, while the barebone, non-specific implementation is BaseTextField. Wouldn’t it be better to rename:

or similar.

Louis noted that Compose UI used to work that way:

That used to be the case, but then we heard the opposite feedback - developers would intuitively reach for TextField first, and be confused because it has no opinionated UX or styling, and requires a lot of work to look like a Material TextField. The current rename was to point developers to the opinionated component first, as this is usually the desired component.

And, as Andrey Mischenko pointed out, the problem is prior solutions getting a bit too cute:

I think a part of expectations that TextField is styled as material design component comes from current situation with Android views, when material library replaces standard views during inflation of layout (edited)

This always worried me with the approach taken by AppCompat and similar libraries. XML elements in layout resources are supposed to indicate the Java class that is the implementation of the View or ViewGroup. AppCompat changes that, swapping in its own classes for the ones declared in the layout. This made AppCompat adoption easier, at the cost of causing some developers to not realize that AppCompat’s widgets were not the same as the framework’s widgets.

This sort of thing could be handled somewhat in documentation, such as mentioning BaseTextField() in the KDocs for TextField().

Posted 2020-09-13, based on


Older “One Off the Slack” articles can be found in the archives.