I have a custom button and I want to allow only so...
# compose
c
I have a custom button and I want to allow only some modifiers like weight or width, for example I don't want to allow user to change button height. Is there any way to place restrictions on modifiers?
c
I think the general suggestion from people will be to just specify those things as optional args and leave out Modifiers completely, and then just apply those modifiers yourself. At least that's the route I've gone when wanting to enforce a a select few modifiers.
πŸ‘ 1
a
generally speaking, don't try. Someone can always do this:
Copy code
Box(Modifier.somethingYouDontWant()) {
  YourComposable()
}
and you can't do anything about it.
πŸ‘ 1
For height (and size) specifically though, you can use the
Modifier.requiredHeight
modifier, which will enforce a given size regardless of parent constraints. The parent will still allocate space with respect to the constraints it gave you, but your composable will stubbornly take on its required size anyway
this can result in the element being clipped or overlapping other nearby content, or otherwise floating surrounding by empty space if it's smaller than a minimum size
but it visually asserts that the button itself isn't broken, the integration point is.
while still staying as usable as possible without crashing, since it's extremely common for your app to end up running under screen sizes you may not have planned for.
c
thanks both for the answer, πŸ™‚
to give you more context I am trying to create a Design System. In this way the components already have their modifiers already applied and I am not interested in anyone modifying them from the outside. ex:
ButtonPrimaryBlue (text = "Button", onClick = {})
This component already has some colors and shapes applied, which is what causes the component to be called like that. But I find the case that sometimes I want it to fit the size of the text and other times to fit its container (a Row for example)
Copy code
Row {
    Box(modifier = Modifier.weight(1f)) {
        PrimaryBlueButton(
            onClick = { },
            text = "Click",
            fillMaxWith = true
        )
    }
    Box(modifier = Modifier.weight(1f)) {
        PrimaryBlueButton(
            onClick = { },
            text = "Click",
            fillMaxWith = true
        )
    }
}
Would this approach be right?
a
No, the note about Box from above was meant to illustrate that you can't stop someone from effectively applying the modifiers they want to your composables, therefore it's better to follow convention and have your composables accept a Modifier like everything else
πŸ‘ 1
c
So, I feel that creating a Composable called ButtonBlue is a mistake because anyone can change the background color, for example
a
1. Anyone can put another background behind yours, that's different from changing the internal content of the button away from blue. 2. Even if (1) weren't the case, defaults matter. They help us think about higher level concerns without explaining every last detail of a given concept we invoke. Whether you choose to give that a different name that doesn't speak to its literal contents directly is up to you πŸ™‚
Modifier.background
isn't part of some defined box model for UI elements, it just says, "at this point, draw something behind the modified content."
c
i need to think about how to create a good Design System. I think that I'm taking a wrong approach πŸ˜…
a
Try building a few UIs without that abstraction first and see what you need; setting out to build a design system before you know what the needs of the clients are is going to be very difficult πŸ™‚
βž• 1
c
@carbaj0 for what it's worth. I've taken basically the approach of making my components as least modifiable as possible so it prevents misuse. For example, for a BlueButton that I have, it only takes in a lambda and a string. Instead of an actual button which takes many more params, but takes in content instead of a string. As my design grows, my composable becomes less restrictive if necessary.
c
the problem comes when the BlueButton (which does't accept modifiers), need a weight so that it adjust to the screen
let's say the height is fixed, but the width depends on the container
c
I would just put an optional weight argument with a sensible default value.
Either way. I don't think there's any "right" answer to this. More of an art than anything of creating a good api. But again. For me, modifiers open up a world of possibilities, and so I rather have required/optional arguments that guide the user.
a
I would just put an optional weight argument with a sensible default value.
you can't do this since
Modifier.weight
is provided by DSL scopes from specific containers only
The right answer is to just accept the modifier param
πŸ‘ 1
you're not restricting things without it, you're just making the legitimate use cases you didn't think about up front more annoying
a key design mindset for working with compose is that there are always three parties involved: the author of the parent, the author of the child, and the integrator placing the child within the parent container
the child always knows the least context, then the parent knows a little more, but the integrator always understands the use case better than the other two ever could
the stubborn child rarely knows best when it wants to get its way πŸ˜›
there are exceptions that prove the rule, but that's all they are.
c
Yeah. I've definitely added modifier back to some components, but again I was just talking about my current approach which is to make it as restrictive as possible and then opening up. The design system is used internally by my team, so it's not something we worry about a lot if we have to iterate. If you are making this as a 3rd party to consume, then I would almost most definitely add a modifier param.
a
it costs so little to write
modifier: Modifier = Modifier
in the parameter list and then use
modifier.foo()...
instead of
Modifier.foo()...
in the implementation code of a composable that emits a single root UI element
πŸ‘ 2