guys is there a good sample app with custom compon...
# compose
p
guys is there a good sample app with custom components that I can take a look?
s
This https://github.com/android/compose-samples list probably has something that you’re looking for. But really depends what “Custom components” means.
p
so I have the question when for instance you have desing system and you make you button
if the modifier should be pass if it is changed
I'm letting the modifier be there so people can modify width
Copy code
internal fun BaseButton(
    modifier: Modifier = Modifier,
even thought internally I do apply fix height for the button
p
NICE cus it's super hard to convince them that it's is normal prctice to let the modifier pass
s
Yeah makes sense. It depends how strict you want your design system to be. But usually if an item accepts a Modifier, and I change the width for example but it does not change I’d be super weirded out. I’d say the design system can provide the defaults, but always let customization open for the caller. You may want to read this one too https://chris.banes.dev/posts/always-provide-a-modifier/
p
Copy code
@Composable
fun FancyButton(
    text: String,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) = Text(
    text = text,
    modifier = modifier.surface(elevation = 4.dp)
        .clickable(onClick)
        .padding(horizontal = 32.dp, vertical = 16.dp)
)
It's example code from that article how to merge argument modifier with internals.
p
but then you just setting a box arround any component you do this about the @cb article
@PHondogo that is what I did the issue is that imagine in there you are doing this
Copy code
padding(horizontal = 32.dp, vertical = 16.dp)
so if you use
Copy code
FancyButton
is not clear that if use
Copy code
FancyButton(modifier = Modifier.padding(0.dp))
nothing happens because internally you are overriding
the article https://chris.banes.dev/posts/always-provide-a-modifier/ is just making the component wrapped in a box and you only pass the modifier to that one
p
In androidx.compose.material.Button there is additional parameter contentPadding. May be you can do the same. And also keep parameter modifier for customizing your root element. LazyColumn use the same way.
c
The value of adding a modifier param is about giving more flexibility, but not necessarily ultimate flexibility. If a FancyButton doesn’t want padding overridden that is up to you, but the modifier param at least gives the caller the ability to change the height/width, align it differently with any sibling, etc. unfortunately the component developer doesn’t have an easy way to declare what exactly a modifier param overrides in the method signature, but that is ok. What is a good practice, which is reflected in how the Material components are designed, is to have explicit parameters for things you want the consumer to customize to make it clearer. So in your case if you want ppl to be able to change FancyButton padding, it might be better to make that a parameter, e.g. contentPadding, versus relying on a generic modifier parameter.
c
Question off of that. If you wanted to provide a modifier param, but didn't want the person to be able to change the padding... how would you prevent that? like lets say its a HUGE design system issue if people start adding padding so you want to prevent that. Would you just inspect the param before applying it?
s
Doesn't
requiredSize
do that? Lemme test actually 👀
Okay no, damn turns out I don’t quite understand how
size
requiredSize
and the such work and interact with each other 🤯
Okay okay I couldn’t bare not understanding this so I wrote some CFD code to test this 😄 Now code follows: SizingItems is this as is showcases in the first item, to serve as a budget ruler 😂
Copy code
@Composable
fun BoxScope.SizingItems() {
    Box(Modifier.width(100.dp).height(5.dp).background(Color.Red))
    Box(Modifier.width(75.dp).height(10.dp).background(Color.Yellow))
    Box(Modifier.width(50.dp).height(15.dp).background(Color.Green))


    Box(Modifier.height(100.dp).width(5.dp).background(Color.Red))
    Box(Modifier.height(75.dp).width(10.dp).background(Color.Yellow))
    Box(Modifier.height(50.dp).width(15.dp).background(Color.Green))
}
And then the code:
Copy code
@Composable
fun ModifiersCheck() {
    Row(
        horizontalArrangement = Arrangement.spacedBy(40.dp),
        modifier = Modifier.padding(10.dp).border(1.dp, Color.Red).padding(50.dp)
    ) {
        Column(verticalArrangement = Arrangement.spacedBy(25.dp)) {
            Text(
                "Green = 50.dp\nYellow = 75.dp\nRed = 100.dp"
            )
            Box {
                SizingItems()
            }
        }
        Column(verticalArrangement = Arrangement.spacedBy(40.dp)) {
            Text("#1:size(50.dp)\n#2:size(100.dp))")
            Box {
                Box(Modifier.size(50.dp).size(100.dp).background(Color.Blue))
                SizingItems()
            }
        }
        Column(verticalArrangement = Arrangement.spacedBy(40.dp)) {
            Text("#1:size(50.dp)\n#2:requiredSize(100.dp))")
            Box {
                Box(Modifier.size(50.dp).requiredSize(100.dp).background(Color.Blue))
                SizingItems()
            }
        }
        Column(verticalArrangement = Arrangement.spacedBy(40.dp)) {
            Text("#1:requiredSize(50.dp)\n#2:requiredSize(100.dp))")
            Box {
                Box(Modifier.requiredSize(50.dp).requiredSize(100.dp).background(Color.Blue))
                SizingItems()
            }
        }
        Column(verticalArrangement = Arrangement.spacedBy(40.dp)) {
            Text("#1:requiredSize(100.dp)\n#2:requiredSize(50.dp))")
            Box {
                Box(Modifier.requiredSize(100.dp).requiredSize(50.dp).background(Color.Blue))
                SizingItems()
            }
        }
        Column(verticalArrangement = Arrangement.spacedBy(40.dp)) {
            Text("#1:requiredSize(50.dp)\n#2:size(100.dp))")
            Box {
                Box(Modifier.requiredSize(50.dp).size(100.dp).background(Color.Blue))
                SizingItems()
            }
        }
    }
}
And then the output in the screenshot: Some thoughts: So the latest modifier wins when it comes to using
requiredSize
. This does however come with the side effect that the item does measure at your required size, but it might be placed a bit off if the caller decides to be weird and call its own
requiredSize
. You can see that in the 4th and the 5th item. Do not just use
size
as the 6th item does in your design system, that allows the caller to use
requiredSize
itself and override your size. However using
requiredSize
makes it so that items get placed at the center of whatever the call-site decided, but just make your item overflow to the edges as seen in items #3 and #4 I think this tells us that if you want your design system item to ALWAYS be of some size, you can safely use the modifier without doing anything to it, since you’ll then after it use your
requiredHeight
which will override whatever comes from the modifier. And this doesn’t require you to do anything out of the ordinary, it is standard practice to use the passed
modifier
first, and chain other modifiers after it. But still quite a pickle, I guess I might be missing something in general, or it’s just that you can’t really do this flawlessly no matter what you chose in your internal component and you’re at the mercy of the call-site. Maybe it’s not actually such a big deal when there’s more room anyway and your item won’t have to measure itself in such a way that it goes “out of bounds” so to say, like #3 and #4, but 🤷‍♂️
p
the intersting part is that @Stylianos Gakis if we have this as a pattern so instead of passing an extra parameter we could use the require+method to override something or create a new one. i.e overridePadding() or overrideSize()
s
I am not sure if I understood you correctly, but I do not propose not having a
modifier
parameter in your composables. Especially not in your design system ones which are meant to be reused a lot. But yeah in general I’m not sure what you meant here.
p
@Stylianos Gakis what I meant with this is it would be cool if we could have some sort of overriding + function in the modifier. i.e.
Copy code
@Composable
fun FancyButton(
    text: String,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) = Text(
    text = text,
    modifier = modifier.surface(elevation = 4.dp)
        .clickable(onClick)
        .padding(horizontal = 32.dp, vertical = 16.dp)
)

@Composable
fun TestFancyButtonOverride() {
    FancyButton(modifier = Modifier.overridePadding(horizontal = 0.dp, vertical = 0.dp))
}
this way if the caller actually wants to override the Padding or Size or anything else we could. Not sure if this is feasible for the modifier.
p
Modifier can have multiple paddings in chain, for example when margin need to be emulated. In that case what should override function override?
p
good question it's true 😞 I mean forget my idea then I was just wondering if there is a best way besides having modifier + attribute to override internally.