Skip to content
zed-alpha edited this page Feb 28, 2026 · 9 revisions

Contents


General

Ugly gaps between shadows and their Views

If you notice odd gaps or spaces between shadows and their targets, especially those with irregular shapes, particularly along their lower edges while at the bottom of their Window, it's likely not due to this library, though this may have made the issue more noticeable.

Close-up of the bottom edge of a target showing the unwanted gap.

I'm not sure of the exact cause so I won't go into any details here, but I would point you to this question on Stack Overflow that illustrates the issue, and has an answer of mine that gives a few workaround options. We're talking about Views there but the same outline adjustment can be applied in Compose as well using GenericShape.

This is a problem with the native shadows so the library does nothing to address it. I mention it for those who might notice the issue while using the library and suspect that something here could be the cause. Also, certain examples in the demo app – e.g., the popups on the pages for the root topics – seem prone to suffering this defect, especially on larger screens.

Avoid using the clip fix unnecessarily

The library's clip fix is not necessary if the target's background and the region behind it are each a single simple color. In that case, it is preferable to calculate the opaque color that results from compositing the translucent one over the other, then set that as the target's background instead.

  • Compose already has the compositeOver() function in its androidx.compose.ui.graphics.Color class that can do the necessary calculations internally.

  • The View framework has no such function out of the box, but this extension from the androidx test source shows how to do the math for android.graphics.Color.

Alternative solutions for the clip fix

If you only need the clip fix for relatively simple setups, you might prefer to put something together from the independent solutions demonstrated in the following Stack Overflow posts. If those basic examples are sufficient for your design, you probably don't want the overhead here.

  • The View version is covered in this answer. The two techniques shown there are the library's primary and fallback functionalities for this framework.

    As is, the given setup works only with static shadows. If you need to handle buttons presses or animations or the like, you'll have to hook up a ViewTreeObserver.OnPreDrawListener to manually invalidate when a target is modified.

  • The Compose solution is in this answer, and it also is quite similar to the library's implementation for Composables, lacking only color compat.

    As a Modifier and extension, it is a drop-in replacement for shadow().

Use of features on root UI elements

Much of the library's internal workings rely on or otherwise involve a target's parent or root ancestor. Since those don't really exist if the target is the root of its hierarchy, there are restrictions for use on such elements.

  • For Views, both of the library's fixes can be used on ones at the root of their respective hierarchies, but only with a specific configuration: using the Inline plane with the root's clipToOutline set to false.

    If you plan to use this with an Activity or Dialog, be aware that their Windows' styles usually set the decor (root) View's clipToOutline = true, so you'll likely have to disable that yourself. It can be done either through the android:windowClipToOutline theme attribute, or in code through the decor's clipToOutline or the Window's setClipToOutline().

    There is one particular setup that is problematic: non-ViewGroup roots, e.g., ImageViews added directly to WindowManager. On API levels 21..27, library shadows are not possible here if the fallback draw method is in use, and on API level 28, they don't work here at all. A new state enum and helper extension have been added to allow for user fallbacks: ShadowMode and View.doOnShadowModeChange. Details can be found on the Experimental wiki page.

  • For Compose, any root Composable on Android is (effectively) the only child of its ComposeView, which it fills completely.

    The clip fix by itself will work on a root Composable as long as the ComposeView is not being clipped to its outline or bounds. Specifically, the ComposeView itself must have clipToOutline set to false (the default), and its parent ViewGroup, if there is one, must have clipChildren set to false as well.

    Using color compat here isn't currently possible, since the compositing layers are sized to a target's root Composable. This could conceivably be updated to fix that, but I don't think it's worth the effort for this feature that is quickly becoming obsolete as projects update their minSdks to 28 and beyond. The :view module's color compat can be applied to the ComposeView instead, if needed.

Native color issue on Android Pie

The native ambient and spot shadow colors are supported on Pie and above, technically. They absolutely do work for Q+, but I cannot get the native shadow colors to work at all on Pie itself, with or without this library involved. All of the relevant methods and attributes were introduced with that version, and the documentation indicates that they should work like normal, but none of the emulators I've tested on show anything but black shadows. I can't find any mention of anyone else having the same issue, though, so I'm completely baffled by this.

The demo app's Intro page has a setup that lets you fiddle with the shadow color, so that could be used as a quick test, if you're curious. It is set up to fall back to the new color compat mechanism for API levels <28, but 28 itself uses the native ambient and spot colors, so the colors will fail on Pie if you observe the same behavior I do.

Report bugs using GitHub Issues

The Issues feature for this repository is active. Please report bugs and any other problems there, for any version of the library.

Download numbers indicate that many projects are still using old versions. If that's due to unaddressed bugs or other issues in the newer releases, please let me know about them. I'd really appreciate the opportunity to improve things.


Views

Invalidation caveat

The library's particular technique causes a target's parent ViewGroup to be invalidated more than it normally would. For static shadows, it's just an extra time or two when switched on or updated. For animated shadows, though, the parent is invalidated on each step of the animation, no matter the type, effectively defeating some of the optimizations that hardware acceleration brings. Button presses, for example, aren't "free" when the button has an active library shadow.

It's not great, but it's likely not a huge deal for most setups, especially considering that many animations will cause the same invalidations anyway. The app in the demo module has several different arrangements that translate and rotate and such, so you can investigate the effects, if curious.

ViewOutlineProvider caveat

In order to disable a target's inherent shadow, its ViewOutlineProvider is wrapped in a custom implementation. This has the possibility of breaking something if some function or component is expecting the View to have one of the static platform implementations; i.e., BACKGROUND, BOUNDS, or PADDED_BOUNDS. This shouldn't cause a fatal error or anything – it's no different than anything else that uses a custom provider – but you might need to rework some background drawables or the like.

Additionally, any user implementations of ViewOutlineProvider should be set before enabling library shadows, or at least before the target attaches to its Window.

ProGuard/R8 issue with RecyclerView

The Recycling ShadowsViewGroups used to have specialized behavior that optimizes shadow creation and disposal for rapid scrolling. At some point, I figured out a way to apply that behavior to all RecyclerViews and AdapterViews, whether they're ShadowsViewGroups or not. At the time, however, I overlooked the fact that this was creating an irremovable dependency on RecyclerView. That is, RecyclerView is always referenced in the base view functionalities, so it cannot be stripped by ProGuard/R8, even if you're not using it anywhere. Whoops.

This can be corrected without a regression, but the possible solutions are unwieldy and inelegant. Given that and the fact that RecyclerView usage is rather ubiquitous these days, I've decided not to do anything about it preemptively. If you do really, really need that fixed, you can file an issue for it, and I'll try to figure something out. Otherwise, it might be easier to clone the repo and remove the single relevant reference in the ShadowSwitch file.


Compose

State of color compat

The ExperimentalColorCompat annotation has been removed from the library.

Clone this wiki locally