https://kotlinlang.org logo
#compose
Title
# compose
n

nglauber

07/03/2020, 6:08 PM
I don’t make questions here for a while, then sorry if this is a FAQ, but here it goes… Why margin is not a modifier like padding is? 🤔
☝️ 1
l

Leland Richardson [G]

07/03/2020, 6:10 PM
realistically, “padding” is actually just “spacing”
the names “margin” and “padding” have alot of historical context influenced by other toolkits
💯 1
a

Adam Powell

07/03/2020, 6:11 PM
goodness, is that an understatement 😅
l

Leland Richardson [G]

07/03/2020, 6:12 PM
for instance, margins often don’t add together when being layed out, and paddings do (ie, two boxes with margins next to each other have spacing between eachother of max(margin1, margin2) )
calling our padding modifier “spacing” would be more correct
but also no one would ever find it, lol
so we chose “padding”
so if you do Modifier .padding(10.dp) .border(Color.Black) .padding(20.dp)
👍 1
a

Adam Powell

07/03/2020, 6:13 PM
yeah we did call it spacing for a while, but the idea that spacing surrounds a thing on all sides was bizarre in practice, and it was confusing when placed next to the
Spacer
element
l

Leland Richardson [G]

07/03/2020, 6:13 PM
the 10.dp is closest to the web’s “margin”, and the 20.dp is closest to the web’s “padding”
👍 1
but its the same modifier. its position just matters
a

Adam Powell

07/03/2020, 6:14 PM
well, android's margin anyway, since it doesn't collapse with adjacent element margins
l

Leland Richardson [G]

07/03/2020, 6:14 PM
right
a

Adam Powell

07/03/2020, 6:15 PM
padding is at least consistent with flutter's use of the term here as well, so there's that
n

nglauber

07/03/2020, 6:15 PM
thanks for the explanation… well… I’m not sure if it is because I’m working with Android since the beginning, but this is a clear concept to me: margin = it’s the outer space and does not change the component size padding = it’s the inner spacing and it changes the component size
l

Leland Richardson [G]

07/03/2020, 6:15 PM
yeah but with those definitions then none of these are correct 🙂
a

Adam Powell

07/03/2020, 6:16 PM
well, sort of. Padding is correct with that definition
l

Leland Richardson [G]

07/03/2020, 6:16 PM
depends what you think of as “component size”
1
a

Adam Powell

07/03/2020, 6:16 PM
Padding does change the component size as far as the containing layout or modifiers that come before it are concerned
l

Leland Richardson [G]

07/03/2020, 6:17 PM
the problem is “component size” is an ambiguous term
i’ve said before, we need to solidify language for these concepts
1
a

Adam Powell

07/03/2020, 6:18 PM
or at least document it. 😄 I feel like we do have solid definitions for these things that are very consistent throughout the system, but it's easy to bring assumptions in that do not match how the system works.
n

nglauber

07/03/2020, 6:20 PM
btw… Flutter uses both: margin and padding which is more clear (for me at least)
l

Leland Richardson [G]

07/03/2020, 6:20 PM
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
a

Adam Powell

07/03/2020, 6:21 PM
l

Leland Richardson [G]

07/03/2020, 6:21 PM
I feel like we do have solid definitions for these things
If we do, I have not seen them. I think most of us understand how the system works, but we haven’t solidifed names for what the conceptual “box” of a modifier in a modifier chain is, and what the thing to the “right” and “left” of it is referred to. Most of the terms we use end up having ambiguities but we sludge through it because we can glean just enough based on context of what the person is talking about
a

Adam Powell

07/03/2020, 6:23 PM
the most precise usage we have in implementation anyway is the
Measurable
that gets measured with given constraints, and the resulting
Placeable
, which has a defined width/height.
n

nglauber

07/03/2020, 6:30 PM
@Adam Powell thanks for the link. But as client of the library I’m not so interested on its internals and definitions (even it’s important to understand it). What I mean is that the terms margin and padding in Android are being used since the beginning, and these properties also exist in flutter. Looks strange to me don’t have them in Compose… 🤷‍♂️
l

Leland Richardson [G]

07/03/2020, 6:34 PM
so you want to have Modifier .margin(10.dp) .border(Color.Black) .padding(20.dp) ?
🙏 2
🚫 1
if so, what would you want to happen in the following case: Modifier .padding(20.dp) .border(Color.Black) .margin(10.dp)
n

nglauber

07/03/2020, 6:35 PM
yep!
In this simple case, it makes difference
Copy code
Row(
    modifier = Modifier.drawBackground(color = Color.Red)
        .padding(16.dp)
//                .margin(16.dp)
) {
   Text("1")
    Text("2")
    Text("3")
}
I want the
Row
content to be red + margins…
l

Leland Richardson [G]

07/03/2020, 6:40 PM
In designing the APIs of compose, building upon previous conventions and understandings of Android is of course something we want to consider. But we have to be careful about over-valuing this when we are talking about building something for the next 10+ years. If we preserve the padding/margin names, but they do nothing but provide space, even though the top example reads more familiar since the margin is on the outside, it allows for really confusing situations like the 2nd example where margin is on the inside. And the problem is that margin in this case would mean nothing more than “space, right here”. Thus, we would actually be adding 10.dp of what you previously considered “padding”. This is confusing. We ended up settling for a middle ground which is that padding is familiar, but margin doesn’t exist so there isn’t a conflict. That may or may not have been the right choice, but it seems better than having a margin API that is just an alias to padding
to add “margins” you just need to put padding in front of the drawBackground
a

Adam Powell

07/03/2020, 6:41 PM
an alternative approach in this case would be if the containing layout scope included a
LayoutParams
-style
margin
paralleling how it works in android today
l

Leland Richardson [G]

07/03/2020, 6:41 PM
with Modifiers is that the order matters
a

Adam Powell

07/03/2020, 6:41 PM
but that adds an awful lot of complexity and expectation of features of layout composables for not a ton of benefit unless we also supported collapsing margins as a part of that
l

Leland Richardson [G]

07/03/2020, 6:42 PM
right
i really don’t like the existing scopes that we have
like ColumnScope/RowScope
a

Adam Powell

07/03/2020, 6:43 PM
what about them, more specifically?
l

Leland Richardson [G]

07/03/2020, 6:43 PM
i’d rather not add more to these
i guess i haven’t tried to articulate it, but it seems fragile and constraining in some ways
a

Adam Powell

07/03/2020, 6:45 PM
after working with all of the issues of LayoutParams for years I've found it to be a pretty elegant solution to that particular shape of problem
but it highlights that certain features are only available when using particular layouts, which tends to pull expectations toward large, monolithic feature sets across layouts
we never had the type safety for it in android layout xml
l

Leland Richardson [G]

07/03/2020, 6:47 PM
(my dog is gonna eat me if i don’t walk him RIGHT NOW, so i am gonna be back in a bit)
😄 2
🐶 7
h

henrikhorbovyi

07/03/2020, 7:05 PM
This is a really interesting topic. But I think I got @nglauber's point. 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.
But of course, it's just my opinion 😄
a

Adam Powell

07/03/2020, 7:08 PM
(trying to put lunch together over here too) I agree, it is constraining in that you can only do some things in particular layouts, and it is "fragile" in that it implies additional dependencies if you refactor/extract some chunks of code that makes use of it. But it's also honest - it reflects the constraints and requirements accurately. No one is going to paste some code that applies
android:layout_weight
to a child into a parent context that doesn't support that without having some hints from the compiler that something is up. And supporting something like collapsing margins is something a parent layout really does need to be involved in.
@henrikhorbovyi 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.
👍 4
🔝 2
h

henrikhorbovyi

07/03/2020, 7:18 PM
Yeah, it makes total sense
s

Sergey Y.

07/03/2020, 7:52 PM
I fully support @Leland Richardson [G]. The Compose is designed to give us developers a whole new experience in developing UIs. It should fix all the bad design decisions of the previous UI framework. And the
LayoutParams
is just one such examples. The similarity to the previous framework is an important thing, but not the main one. If we will pull the same bad architecture design decisions, then what's the point? More bold decisions need to be made. When you work with one technology for a long time, the eye is blurred. And it’s already difficult to understand a good design or not when you don’t see anything better. I am sure the Compose team will do everything in the best way and given us the community a chance to help in this.
💯 1
l

Leland Richardson [G]

07/03/2020, 8:06 PM
To be fair though, I think margin as a concept is separate from layout params’ implementation and I don’t think that the concept of margin is a bad one. The problem is that there were a lot of other factors that led us to the modifiers design and the end result is that the only difference between margin and padding in the modifiers world is when you call it. I make no claims to this being better or good, just that it is that way. The question then becomes if we are in that world (and we are convinced that modifiers is a good design for other reasons, which we are), then do we do anything to help map developers notions of padding/margin onto the current paradigm. 1. Having neither and just having “spacing” would mean we aren’t helping at all, but we are also making it more clear that the concept is more general 2. Having “padding” is helping somewhat, but then makes it feel like “margin” is missing. 3. Having both “padding” and “margin” gives developers two APIs to map it to, but it leads them into a trap of thinking that they work differently and then they miss the fact that the order is the actual thing that matters.
👍 6
n

nglauber

07/03/2020, 10:43 PM
I really appreciate your comments here. Now I understand that order of calls for the
Modifier
matters and just the
padding
function works for both purposes. Thanks @Leland Richardson [G] and @Adam Powell for you attention. 👍
👍 2
d

dagomni

07/03/2020, 10:47 PM
@Leland Richardson [G] I believe that we should use the term "spacing" alone or preserve the concept of "padding" & "margin" together and make it work accordingly. In a different thread you said
"[...] I find that most people will just write the code that feels the most natural and its "good enough" most of the time. We want Compose to be the same way. [...]".
Even though with "good enough" you meant performance, I think that in general Compose is the way to make writing UI as much a natural process as it can be. And 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.
👍 6
p

pavi2410

07/04/2020, 12:16 PM
What's the Compose equivalent of CSS' box model? If not, how would you describe it? https://www.w3.org/TR/CSS2/box.html https://en.m.wikipedia.org/wiki/CSS_box_model
Currently, if I want to add margins to any component, I have to wrap it in a Box and use padding modifier, which doesn't feel right, especially when repeated.
a

Adam Powell

07/04/2020, 12:57 PM
You don't need to wrap it in a Box, just use the modifier on the element you want the padding around. 🙂
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.
👍 2
This use of explicit order to define behavior rather than a predefined box model is in direct response to systems like Android's View, which grew unmaintainably over time. As the core element it needed to be changed any time a new general purpose property needed to be added. Developers were not able to add new universal properties, and the defined interactions between properties became harder to reason about with each new one we added to View itself.
👍 1
For developers to be able to add new properties externally, there has to be a firm and simple ordering rule to resolve diamond dependencies from different third parties. If library A defines property foo that sits between border and margin in a defined box model, and library B defines property bar that also sits between border and margin, if libraries A and B were written without knowledge of one another's existence, does the outer to inner order become [margin, foo, bar, border], or [margin, bar, foo, border], and why?
To get concrete about it, cases where complexity in View grew significantly included when Android added bidi layout support, and padding became very complicated with rules about start/end vs. left/right; supporting both over time was extra difficult because we could not reasonably backport the behavior and developers had to be able to write layouts that could run both before and after the functionality existed
Or when material design was introduced, we also had to introduce additional properties for elevation, shadows, and custom outlines for defining those shadow casting shapes and clipping to them. Writing material components without these is extra difficult, and these general purpose properties could not be defined and shipped along with a material components library
By making ordering explicit, the modifier model (and flutter's single child widget wrappers) makes it possible for design system libraries (and others) to add their own new universal decorative and behavioral properties to UI elements by removing the diamond dependency problem. It means the core compose UI team and our release schedule can't block your teams from implementing your designs because you couldn't get us to merge something upstream fast enough; you don't have to maintain deep and extensive forks to add that functionality until we get to it.
I think that's worth the up front friction of it being different from Views or CSS. 🙂
We've had extensive conversations around whether to offer higher level helpers that let you express a pack of these things at once in a box model-like way; the
Box
composable function is a centerpiece of this ongoing discussion
(and thank you all for joining it!)
Other things we've thought about: should there be a
Modifier.box
? Does the existence of these helpers defer understanding of the underlying model in a way that hurts more in the long run? Already we're seeing signs of some kind of pathological UI tree shapes resulting from an overuse of
Box
and we're keeping an eye on it
p

pavi2410

07/04/2020, 1:57 PM
Thanks Adam for sharing this! I am now confident that I don't have to wrap content in a Box in order to apply margins. This is how I can emulate CSS box model now
Copy code
@Composable
fun BoxModel() {
    Box(
        modifier = Modifier // applies from outside (left) to inside (right)
            .size(100.dp)
            .drawBackground(Color.Red).padding(4.dp)   // margin
            .drawBorder(1.dp, Color.Black)             // border
            .drawBackground(Color.Green).padding(8.dp) // padding
    ) {
        Box(modifier = Modifier.drawBackground(Color.Blue).fillMaxSize())
    }
}
a

Adam Powell

07/04/2020, 2:03 PM
Yep, you've got the idea. And you can probably see where you don't even need the outer
Box
there at all, you could just as easily stick this one modifier chain on a
Spacer
,
Text
,
Button
or any other content element itself rather than wrapping it in a container only meant to hold the modifiers
👍 2
v

Vinay Gaba

07/12/2020, 4:41 AM
a

Adam Powell

07/12/2020, 5:34 AM
I'm not so sure. ConstraintLayout's use of margin seems consistent with margin being a property of the parent configuration and padding being a property of the child independent of parent configuration
v

Vinay Gaba

07/12/2020, 5:01 PM
interesting…need to still wrap my head around what you just said. I did like the generalization of padding irrespective of how you use it in the chain. At the end of the day, it’s just space and so it should not matter if the parent is adding that space or the child, because the order of the padding would have the effect you desire. Feels like an implementation detail led to it being called margin but I’m going to think a bit more about this.