react-native: Elevation and border radius do not work well with opacity from Android 9

The way elevation works is that it creates a border around the element that is then shifted to the bottom. The background color of the element then hides the inside part of the border.

When we apply border radius to the same element, the border gets thicker to accommodate for the increased radius. So far so good.

Unfortunately if we then apply the opacity to the parent, the background color of the element gets semitransparent, making the border visible and creating an ugly effect.

Screenshot_2019-05-30-17-03-34-477_host exp exponent

React Native version: 0.57 Expo SDK 32 Android version: 9.0

Steps To Reproduce

Create an element and apply, elevation, border radius and background color. Then apply opacity on its parent.

Describe what you expected to happen:

The border underneath the element should not leak out

Snack, code example, or link to a repository:

https://snack.expo.io/SJDAlu6TV

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 43
  • Comments: 28 (4 by maintainers)

Most upvoted comments

Using backgroundColor: '#fff' sets it right and works as expected.

I found that this problem occur when changing opacity on a wrapping element that contain child elements using elevation for shadow. What fixed this bug for me temporarily was to set flag needsOffscreenAlphaCompositing on the wrapping element that i animated opacity style prop for. (For best practice usage check: https://reactnative.dev/docs/view#needsoffscreenalphacompositing) Otherwise setting opacity on each of the child elements should also work, instead of only the parent wrapper.

This was mentioned here: https://github.com/facebook/react-native/issues/23090#issuecomment-669157170

I discovered a simple way to fix the problem, which is adding an opacity property to the child component to overide the effect done by the parent element, such as opacity: 0.99;, see if it works for you.

Using backgroundColor: '#fff' sets it right and works as expected.

worked for me, what happened was my component was transparent hence i can see whats happening in the back, setting a background color makes it work as intended

The problem is that if you lower or animate the opacity of this element, fading it out, the background color doesn’t help. You can still see the shadow being rendered underneath, like a thick border.

Using backgroundColor: '#fff' sets it right and works as expected.

worked for me, what happened was my component was transparent hence i can see whats happening in the back, setting a background color makes it work as intended

Facing this issue with RN 0.59.8 as well.

Seems like needsOffscreenAlphaCompositing isn’t working for me. Anyone else encountered and sorted this?

@ferrannp hello sir, is there anyways to make this bug more priority. I am waiting for this too long sir.

Still facing the issue on RN 0.60.4

Still not work well.

Seems like the issue is reproducible only on Android version 9.0 (I was testing that bug also on Android version 7.0 and 8.1 where it looks properly).

Still an issue.

Setting backgroundColor: '#fff' works.

Using @simongoot solution, needsOffscreenAlphaCompositing worked great. In case someone else has the problem with a TouchableOpacity, where needsOffscreenAlphaCompositing did not work for me. You can replace the TouchableOpacity with an Animated Presseable and then animate the opacity via onPressIn and onPressOut.

import { Pressable, Animated } from 'react-native';
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);

const Button = ({children}) => {
  const animatedButtonOpacity = useRef(new Animated.Value(1));
  
  const buttonOpacity = {
    opacity: animatedButtonOpacity.current
  };
  return (
    <>
      {properties.map((property, index) => (
        <AnimatedPressable
          onPressIn={() => animatedButtonOpacity.current.setValue(0.5)}
          onPressOut={() => animatedButtonOpacity.current.setValue(1)}
          onPres={...}
          style={buttonOpacity}
          needsOffscreenAlphaCompositing
        >
          {children}
        </AnimatedPressable>
      ))}
    </>
  );
};

I discovered a simple way to fix the problem, which is adding an opacity property to the child component to overide the effect done by the parent element, such as opacity: 0.99;, see if it works for you.

This does not work for me, is there already another fix? (Expect from using two View components (one for shadow and one for the background color with opacity))