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!

A Space By Any Other Name…

Nelson Glauber asked what might have been thought of as a simple question:

Why margin is not a modifier like padding is?

My personal interpretation of the question is: why do we have Modifier.padding(), but we do not have Modifier.margin()?

However, as Google’s Leland Richardson quickly pointed out:

the names “margin” and “padding” have alot of historical context influenced by other toolkits

Strictly speaking, Modifier.padding() might better have been called Modifier.spacing(), but they were concerned that nobody would be looking for “spacing” as a term.

Part of the difficulty is that “padding” and “margin”, as they are used, are properties of something (I’ll use the term “widget” here). Modifiers are not properties. Or, as Leland put it:

When i use Modifier.padding(10.dp) I’m not really modifying the size of a thing that existed before, i’m creating a new layer/element of the tree of things that is broadcasting its size as 10.dp more than what was below it

Google’s Adam Powell elaborated later in the thread:

The model is: composable UI elements are responsible for their own content and that content may not be inspected or manipulated by a parent unless that element’s author exposes an explicit API to do so. Modifiers of an element decorate the element they modify in the same opaque way. Modifiers are encapsulated; a layout modifier that comes before another measures and places the element and all of its later modifiers as one unit, unaware of the specifics. The “box model” is defined by how you choose to order the relevant modifiers.

The concern of some is transference of knowledge. Or, as Henrique Horbovyi put it:

Would be much simpler a migration from a xml world to the compose. And I think this is important because if we want to decrease the learning curve and bring more developers to the “Compose way” and should give kinda more similarities with the classic.

Adam offered a counterpoint:

we’ve definitely made a lot of design decisions to keep things as familiar as we can, but we have to balance that with not carrying forward the deeply troublesome bits (or trappings that necessarily imply deeply troublesome bits) about android view layout and xml just for the sake of familiarity too.

Google specifically did not think in terms of the padding-vs.-margins distinctions used in other UI toolkits. Instead, the Modifier system relies on the order how things get applied, so whether any given Modifier.padding() visually represents padding or margin (or neither) depends entirely on when and how it is used.

That led to Daniel Gom to argue that if Modifier.padding() really is not padding, then it should not be named as such:

I believe that we should use the term “spacing” alone or preserve the concept of “padding” & “margin” together and make it work accordingly. …margins are ingrained in developers’ minds, not only on Android, so when we have only padding, something will always be missing. I feel that it’s easier to think about either just spacing or margins + padding.

I cannot argue with Daniel’s point, and I would probably lean towards spacing() as the name. However, even if Compose goes with the current padding() name, it is a matter of education to explain how that relates to similar concepts in Android’s View system or other UI toolkits. There will need to be lot of that sort of education, so needing it for yet one more topic does not worry me very much.

Posted 2020-07-05, based on https://kotlinlang.slack.com/archives/CJLTWPH7S/p1593799693346200


How Long Is Too Long?

Vitali Nartov asked a fine question:

Is there any concept in mind of how the composable should be decoupled or how big should/could be views/pages/screens with nested composable elements?

Google’s Jim Sproch said, in essenece, don’t worry about it:

for the size of Composables, it is a somewhat subjective judgement call. I think most new compose developers tend to be a bit paranoid about function length and try to break up composables into helper functions that are far too small. There are benefits to having large composable functions, like being able to use lexical scoping to get data to the appropriate child widget. In the absence of specifics, I’d tell people not to worry too much about length. Instead, focus on what pieces are reusable widgets that will be used elsewhere in your app, and make each of those into a separate composable function.

However, he went on to point out that while the entry point of a composable is a function, this does not mean that composables have to only be functions:

Widgets can (and should) have their own logic, and can introduce/expose their own classes and data model types. For example, a DatePicker or CreditCardEntryForm or AddressForm could all have very sophisticated validation logic. If you were implementing GoogleDocs, you might have a GoogleDocTextEditor widget that took in a mutable RichTextDoc data structure describing the document. But the logic of these widgets should be self-contained to the widget’s specific concerns, and not explicitly depend on the app as a whole.

This feels like the sort of area where we will be refining our techniques in the years to come.

Posted 2020-06-29, based on https://kotlinlang.slack.com/archives/CJLTWPH7S/p1592909452013600


Reworking Recomposer

Andylamax asked:

Why do need to call setContent(Recomposer.current()) {} in fragments? and if we must pass a Recomposer as a parameter, in which scenario would I need to pass another Recomposer?

Google’s Adam Powell answered with information about a potential upcoming change. They might get rid of the setContent() extension function on ViewGroup:

We’re probably going to remove the ViewGroup.setContent extension altogether in favor of some sort of public AbstractComposeView that accepts a composable function as a required constructor parameter. Since the current extension has to create one anyway, and today it’s just an added layer of view hierarchy.

To Andylamax’s concern, Adam points out that Recomposer.current() is “thread-specific”. If you look at the dev13 implementation, it relies on a ThreadLocal and has guard logic to require you to be on the main application thread when trying to retrieve it. And that is a bit icky, or as Adam put it:

…binding to the current thread by default has been a source of issues in other Android APIs over the years. Being explicit is better here.

Posted 2020-06-21, based on https://kotlinlang.slack.com/archives/CJLTWPH7S/p1592750183462000


More!

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