If you didn't know already, there's a defect with Android's material shadows, the ones that came with Material Design and its concepts of surfaces, lighting, and elevation. Also, if you didn't know, Compose utilizes many of the same graphics APIs as the View framework, including those responsible for said shadows, so it has the same glitch that Views do, at least for now.
Card(), FloatingActionButton(), ExtendedFloatingActionButton(), and Surface() shown with and without translucent backgrounds.
For reasons I won't get into here,* I don't believe that there is any proper fix for this – i.e., I don't think that the platform offers any method or configuration by which to clip out or otherwise remove that artifact – so we're left with workarounds. Additionally, a main requirement is that the shadows appear exactly as the platform ones normally would, so any method that draws shadows with other techiques, like a uniform gradient or blur or whatnot, are not acceptable.
Given that, can we create a robust, generally applicable solution in Compose?
I personally landed on an overall approach of disabling the original shadow and drawing a clipped replica in its place. (I realize that simply punching a hole through it is not how shadows work realistically, but that seems to be the predominately expected effect.) I've shared an example of the Compose version of this in an answer below, but the primary motivation for this question was to check for better ideas before this is put into a library.
I'm sure that there are technical details in my example that can be improved, but I'm mainly curious about fundamentally different approaches or suggestions. I'm not interested in, for instance, somehow using drawBehind() or Canvas() instead to do essentially the same thing, or refactoring parameters just to slot the content in, etc. I'm thinking more along the lines of:
Can you devise some other (more performant) way to trim that artifact without creating and clipping a separate shadow object? With
Views, about the only way I found was to draw theViewtwice, with the content clipped in one draw and the shadow disabled in the other. I eventually decided against that, though, given the overhead.Can this be extracted to a
Modifierand extension, similar to the*GraphicsLayerModifiers andshadow()/graphicsLayer()? I've not yet fully wrapped my head around all of Compose's concepts and capabilities, but I don't think so.Is there any other way to make this generally applicable, without requiring additional wiring? The shadow object in my example depends on three optional parameters with defaults from the target composable, and I can't think of any way to get at those, apart from wrapping the target with another composable.
* Those reasons are outlined in my question here.