-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Description
Is there an existing issue for this?
- I have searched the existing issues
- I have read the guide to filing a bug
Use case
I want to add fairly arbitrary visual details to my app's ElevatedButton's (this feature request applies to all the material button types, but I'll use ElevatedButton as a stand in).
Here are some examples:
- Adding a linear/radial/etc gradient
- Adding a border
- Using a semi-opaque image to texture the button
- Using an arbitrary box shadow instead of the material elevation system
- Placing .svgs above, behind and around the button as visual accents
As-is, ButtonStyle limits me to a very narrow range of style customization: shape, color, elevation...
I could create my own button system, but the material buttons do an enormous amount of extremely helpful things (manage splash factory, handle material state, tune visual density to match the platform, etc) that would be a huge hassle to reverse engineer.
Here are some examples of random apps in the wild that have buttons that are not achievable without heavy workarounds.
My app. I use a linear gradient and a border using a complicated work around that puts every elevated button in a stack:

Marvel snap. Marvel snap uses a particularly complex system containing tons of elements:
- asymmetric shape
- Texture
- Texture fades to solid background in center
- Stroke text
- Metallic border
- Light flare
- And more...
Proposal
I think there are a lot of different ways to add greater customization to ButtonStyle. I will list a few I've thought of here, I'm very open to more suggestions!
- Mimic
Container's foreground and backgroundBoxDecorationarguments
I don't love this solution as it's fairly limiting. For example, you can't use aStackto stack elements on top of each other in this context. - Include foreground and background builder arguments. The builders would take in a context, the current material state(s) and the
EdgeInsetsby which the button is padded with (as they should be able to draw in and outside of the button without having to reverse engineer the complicated padding logic).
I like this much more, as it gives me completely arbitrary design control. For example, even Marvel Snap's insanely complicated button would probably be doable with this system. The implementation would need to ensure the foreground child doesn't interfere with hit testing. The disadvantage is that even pretty simple customizations (like adding a linear gradient) would be pretty complicated.
In both cases, the background argument would be placed between the button's child and the background Material. And/or the Material could be left out entirely if the background argument is non null.
Maybe to ensure that simple customization is easy while complex customization is still possible, BOTH BoxDecoration and builder based customization could be provided?
What this might look like to use (coded off the top of my head, probably has minor syntax errors):
ElevatedButton(
style: ButtonStyle(
backgroundBuilder: (context, padding, materialStates, style) {
final baseColor = style.resolveFor(materialStates);
final shape = style.shape;
return Padding(
// Pad the background by the inherited padding.
// Elements could be placed above this padding to draw outside
// of the elevated button's background.
padding: padding,
child: _ClipToShape(
shape: shape,
child: Stack(
children: [
_MyGradient(color: baseColor),
_MySvgAccent(),
// And so on.
],
)
),
);
},
),
);