One Off the Slack
These articles summarize the discussion on a particular thread in the
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 https://slack.kotlinlang.org!
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 (
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
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
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
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
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 https://kotlinlang.slack.com/archives/CJLTWPH7S/p1600999908031800
Maik had a
MutableList parameter to a
MyList() composable and was wondering
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
MyList(items)is less likely to run.
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
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 https://kotlinlang.slack.com/archives/CJLTWPH7S/p1600333577162900
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,
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:
- BaseTextField -> TextField
- TextField -> MaterialTextField
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
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
in the KDocs for
Posted 2020-09-13, based on https://kotlinlang.slack.com/archives/CJLTWPH7S/p1599494195155900
Older “One Off the Slack” articles can be found in the archives.