One Off the Slack: Designer-Resistant Composables
This week’s highlighted Slack thread started off with Jan Skrasek asking about styling of components:
I’d like to style my “components”. Let’s say I have specific color for my button.
Ideally, I like to call
Button(backgroundColor = Color.MyColor), but of course, also content color has to be handled. Explicit passing of such seems to be pretty verbose, but there is not probably any way to modify contentColorFor() behavior for my color. Other options is to create own Button implementation but that’s not much elegant.
Louis pointed out that wrapper functions are a recommended approach for this:
In general, it can be helpful to think of a ‘style’ as a new function, providing different defaults / values to the underlying component.
This is actually how internally some components are implemented, for example,
OutlinedButtonjust calls Button with some different defaults - and the same works well in application code as well.
This also allows you to only expose the parameters / customization your
LoginButtonactually needs, so you don’t need to expose color customization if you want to ensure it will always be the same color, or you can even provide a higher level API that accepts some object (like aString) that will be internally converted to text, instead of acontentlambda.
The other benefit is having semantically named components, rather than everything just being a
Buttonwith different theme / style configurations, that make it harder to understand what the component actually represents / how it appears.
Zhelyazko Atanasov pointed out that this approach will be needed to deal with designers who change their minds:
So if I have a relatively large app built with Compose and the UI designer wants to change the background of all the Buttons in the app, without affecting the rest of the components, I have to first create
MyFunkyButtoncomposable and replace all Button instances with it, right? If that’s indeed the case, probably it’s worth creating those semantically named “wrapper” composables that follow the company’s design system, saving us the headaches later when the company’s design system changes and it doesn’t 100% follow the Material guidelines
This concept has come up before on the #compose channel: teams creating CompanyButton(), CompanyText(),
and so on as thin wrappers around stock composables that implement company (or project)
style guides. The code that assembles the actual UI then refers to those, instead
of “raw” Compose-supplied composables, so there is one place to make changes
when those changes are needed.
Combining this with Louis’ recommendation, we may wind up with a three-tier
solution: a LoginButton that configures a CompanyButton() that, in turn,
is configuring a Compose Button. LoginButton represents customizations tied
to a UI use case, while CompanyButton provides app-wide settings.
Read the original thread in the kotlinlang Slack workspace. Not a member? Join that Slack workspace here!