One Off the Slack: Principle of Least... Modifiability?

Alejandro Carbajo asked:

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?

Google’s Adam Powell pushed back:

generally speaking, don’t try. Someone can always do this:

Box(Modifier.somethingYouDontWant()) {
  YourComposable()
}

and you can’t do anything about it.

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.

Alejandro offered:

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.

Adam continued:

…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

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

Colton Idle, though, has been taking the approach that Alejandro was aiming for:

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

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.

Adam was not a fan:

The right answer is to just accept the modifier param

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

there are exceptions that prove the rule, but that’s all they are

Colton agreed… to a point:

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.

Adam wrapped with:

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


Read the original thread in the kotlinlang Slack workspace. Not a member? Join that Slack workspace here!