So, I have a weird need. I have a set of libraries...
# compose
f
So, I have a weird need. I have a set of libraries, that gets put together to build an app. Say, app A uses featureA and featureB, setting up navigation on its own, etc, while app B uses featureB and featureC. And now, I have app C. App C is just like app A, except there's one crucial change: some UI components are visually different. Not massively different, but different enough that it cannot be done with just theming a MaterialButton. I have this idea of having a CompositionLocalProvider that provides common UI elements. The default implementation of LocalComponents.Button() would just be a MaterialButton behind the scenes, but app C could override it to be CustomButtonThatIsWildly different. If you've read this, you're probably horrified at the idea. So am I. But as it stands, I cannot figure a better way to provide different UI elements (with a common interface). Any idea how this could be done otherwise ?
s
Can the component just take in an object indicating what it needs to look like?
Copy code
@Composable
fun YourButton(foo: Any,..., config: YourButtonConfig) {
  when(config) {
    smth#1 -> //style 1
    smth#2 -> //style 2
  }
}
And on the call-site, depending on what config you want, you pass the appropriate one. If that comes from a CompositionLocal or not is up to you, but the composable itself doesn’t need to be hidden behind something like that. And if you don’t even want to call the same composable in those cases, why not have 2 completely separate composables, and in your UI code, depending on the state do
Copy code
fun AppUi(whichAppCurrenlyInUse: Any) {
  Title()
  Stuff()
  when(whichAppCurrenlyInUse) {
    .. -> Button()
    .. -> WildButton()
  }
  Foo()
}
?
f
I probably could make it so, however this leads to another problem: this is one client out of hundreds (that happens to be paying enough so that their UI wishes can come true), so polluting the whole signature just for that hurts me a little bit. As for the UI option, well, that'd be the same thing; all signatures, including composables deep down the stack suddenly get a new parameter, just for that. This whole line of thought is why I went to CompositionLocals: they're easy enough to set with defaults (which means previews don't break, etc), and easy enough to override for the one-off case. They just feel wrong, but I don't see any other way to conditionally inject UIs, aside from having a ButtonProvider class with a composable method injected, which is just a more explicit way of doing the same thing.
a
This seems like a use case for product flavours.