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 https://slack.kotlinlang.org!

The Scope of State

Dominic Fisher asked:

Is there much difference between SnapshotStateList<T> and MutableState<List<T>> ? In particular, is there any difference in the granularity of how mutations trigger recomposition? (Like in a LazyColumnFor)

Google’s Adam Powell and Leland Richardson had differing takes on that… and this time, I’m on Leland’s side.

(sorry, Adam!)

Adam’s initial reaction was:

The former is basically a fancy version of the latter, but that uses the kotlinx immutable collections and some other optimizations under the hood for efficiency

Leland countered:

I dunno if I would agree with SnapshotStateList<T> as a fancy version of MutableState<List<T>>.

When you’re using the latter, you’re essentially treating the list itself as an immutable piece of data, and whenver you want to change it, you have to provide a whole new list. So for example, to add an item in the middle of the list:

state.value =  
  state.value.let { 
    it.subList(0, n) + item + it.subList(n)
  }

This is because the “tracking” is really done on a single value, not on the individual elements of the list. You have a “tracked” reference to a list, and you’re updating what that reference points to.

On the other hand, the SnapshotStateList is made to look like a MutableList, but all mutations are tracked properly by Compose.

For instance, adding an element in the middle of the list is just:

state.insertAt(n, item)

In other words, with MutableState<T>, the T itself is the state, and we replace a T instance with another T instance to affect changes. With SnapshotStateList<T>, once again T is the state… but this time, it is a list of T objects, and we change individual elements in the list to affect changes. So, while both MutableState<List<T>> and SnapshotStateList<T> are states representing lists of T, with MutableState, we replace the whole list, and with SnapshotStateList, we mutate the contents of the list.

Leland went on to say:

This is really handy if you’re really thinking of the list and its elements as the stateful part of your app. However, I tend to lean away from SnapshotStateList in most cases just because I think the MutableState variant is not going to be any worse performance wise and it introduces fewer new concepts into your app (ie, you are probably already familiar using both MutableState and List all over the place, but likely not SnapshotStateList).

That said, it can be very useful in specific situations.

Posted 2020-11-22, based on https://kotlinlang.slack.com/archives/CJLTWPH7S/p1605742219327100


Design Systems and Composable Names

Bryan Herbst asked:

Why are all of the components in androidx.compose.material named as though they are the the canonical implementation, when the messaging so far is that Material is an implementation of a specific design system? Text is a great example- it’s name alone ensures that it will be the first text Composable that most engineers reach for, but that presents two problems in my mind:

Google’s Ian Lake suggested:

If you are building your own design system, wouldn’t you just also use Text in your own package namespace? Not including the material library (as it is a purposefully a separate artifact) would be enough to prevent confusion.

As I (Mark Murphy) noted in the thread, libraries are going to pull in material via transitive dependencies. And, as Bryan pointed out later:

Another challenge is that it is fairly likely that we will be pulling in Material in our design system library so that we can wrap some components that we don’t want to completely re-implement ourselves. As another example, while I want my engineers using my Button instead of the Material button, I don’t necessarily want to stop them from using a RippleIndication from Material

On that latter point, apparently there is a plan to move RippleIndication out of material, so other design systems can use ripples.

I suggested:

My guess is that there will be a cottage industry for Lint rules to enforce usage of the right composables.

Bryan and Alex Lockwood echoed that sentiment, with Alex pointing out this Lint rule as a possible example.

Alex also pointed out that .idea/codeInsightSettings.xml lets you set up an auto-complete blacklist:

<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="JavaProjectCodeInsightSettings">
    <excluded-names>
      <name>androidx.compose.material.*</name>
    </excluded-names>
  </component>
</project>

Jan Skrasek wondered about prefixes:

Well, if [using package namespaces] would be usable practically, Compose foundations wouldn’t have BasicText and the same the TextField should be without prefix, shouldn’t be? The reality is that including material (at least the compose’s) is a must and we and probably majority of others build upon it. Personally, this is one of my open questions if I should (re)name my components with prefix our let them just namespaced. At the same time, we know we wouldn’t wrap everything, so it seems pretty weird to have TextField and CompanyButton.

Alex and Bryan have are trying, via those auto-complete blocks and Lint rules, to go the Google approach and have un-prefixed composables.

Posted 2020-11-15, based on https://kotlinlang.slack.com/archives/CJLTWPH7S/p1605201059423700


What’s Missing in Compose for Desktop?

Scott asked:

So what parts of compose desktop are lacking compared to compose android?

Google’s Jim Sproch responded:

You tell us! Try it out, tell us what’s missing / needed most.

However, in truth, there are known gaps between the two environments. Drawables and SVG support is one area that is forthcoming, according to JetBrains’ Nikolay Igotti:

SVG icons support is missing, but we plan to fix that

Also, he pointed out that work is still in progress on Web rendering, maps, and stuff:

Some features, such as maps are not available on desktop, but maybe those could be handled with CEF support and showing them in browser

Here, “CEF” refers to the Chromium Embedded Framework, for using the Chromium engine within an app.

Some of the gaps are less about Compose and more about Android. For example, the popular image-loading libraries tend to be tied to Android, so Kotlin/Multiplatform equivalents will be needed.

And the reverse is true in a few cases, where Compose for Desktop is ahead of Compose for Android, such as with scrollbars, as Jim mentioned:

We intend to lift scrollbars into more common code, just hasn’t happened yet. But yes, scrollbars were implemented on Desktop first because they were more critical to the desktop experience.

Posted 2020-11-08, based on https://kotlinlang.slack.com/archives/C01D6HTPATV/p1604688999229800


More!

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