react-native: zIndex does not work with dynamic components on Android
I am trying to render elements conditionally where each element has different zIndex style property. Using folowing code in Android emulator with react-native 0.30.0.
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default class Demo extends Component {
constructor(props, ctx) {
super(props, ctx);
this.state = {
showGreen: true,
};
}
render() {
return (
<View style={{flex: 1, padding: 20}}>
<View style={[styles.item, {zIndex: 3, backgroundColor: 'red'}]}>
<Text>zIndex: 3</Text>
</View>
{this.state.showGreen &&
<View style={[styles.item, {zIndex: 2, backgroundColor: 'green'}]}>
<Text>zIndex: 2</Text>
</View>
}
<View style={[styles.item, {zIndex: 1, backgroundColor: 'blue'}]}>
<Text>zIndex: 1</Text>
</View>
<View style={styles.button}>
<Text onPress={() => this.setState({ showGreen: !this.state.showGreen })}>
Toggle green
</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
item: {
marginTop: -20,
height: 50,
paddingTop: 22,
},
button: {
backgroundColor: 'gray',
marginTop: 30,
}
});
Initial screen looks like expected:
When I click ‘Toggle green’ button to dynamically show/hide green element it hides blue element instead and furthermore element’s text is missing:
When I click the button again, red and green elements remains visible and the toggle button jumps down.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 42
- Comments: 84 (5 by maintainers)
Commits related to this issue
- Summary: Commit https://github.com/facebook/react-native/commit/3d3b067f6fc831b6b23726087fe39cf39ef86f00 implemented zIndex support for Android. However after zIndex resorting of the views, the JS do... — committed to goshacmd/react-native by asgvard 8 years ago
- fix: Fix compatibility issue of android with zIndex . Thanks to : https://github.com/facebook/react-native/issues/8968#issuecomment-293122473 — committed to abdennour/react-nanodegree-mobile-flashcards by abdennour 7 years ago
- fix overflow hidden Reviewed By: shergin Differential Revision: D5917111 fbshipit-source-id: e3d97f26b6aada199f700ec6659ace0d7dffd4c5 — committed to facebook/react-native by aaronechiu 7 years ago
I’m experiencing this also,
zIndex
+position:absolute
breaks in androidany updates on this? I am also experiencing this bug, with
zIndex
+position : absolute
on androidyou can finish the work without zIndex
Closing this since it is now fixed (sorry for not posting here I didn’t know about this issue).
This will make it in RN 0.45 (May) which should be in RC in a few days.
Can we just agree that this issue needs to be reopen NOW?
actually, elevation property worked on android, just remove zIndex property on android platform and make the component with elevation property is the last component.
it works for me! thank all!
Instead of
return null
I justreturn <View />
and it works fine for me on both Android and iOS.Adding “elevation: 100” to the style solved my issue. credit goes to Shukarullah Shah
0.54.0 The problem still exists
Same thing happened to me, so I’ll throw in some more details: In iOS, returning
null
makes the element disappear. In Android, you have to reduce the height to 0 and remove borders. What is worse is that you can’t stick to a single solution, because Android’s workaround won’t work for iOS. The element will just show up again.Here’s the code for a component that does this sort of thing:
Any updates on this issue?
v0.54.2
Still a problem.
same here,
zIndex
+position : absolute
, any update ?My react-native version is 0.49.3 and I am still encountering this problem with the zIndex and the elevation in android. I also used the @glenn-axsy workaround but still couldn’t solve the issue.
In the image below I want the user icon on top of the View but it is hiding and not coming on the top as expected
0.52.1 having the same issue
Why this issue is closed? zIndex still not working on Android, and elevation is not the solution, because of mess of pointer-events.
For anyone still stuck on the old RN here’s a function I use that can be spread into your styles. It’s based on the ideas presented by others above:
Use it like this in place of e.g:
zIndex: 100
Remove the Flow type checking from the function if you don’t need it
@janicduplessis I’ working on an app that is currently using RN 0.45.1 and it has a View with 3 images inside. I need left and right images to be on top of the center image, so I set a zIndex to the left image so its placed on top of the center one. It does not work. It only works when instead of using zIndex, I use elevation in Android. This way it does work, but I get the warning error saying
Warning: Failed prop type: Invalid props.style key `elevation` supplied to `RCTImageView`.
This is my code
BTW, this is what I’m trying to do
I’m still experiencing this just for the record!
Still exist in v0.55.4
Thanks for your case @jaredmharris. However using
elevation
is not the same aszIndex
, as pointer-events pass through what is underneath.elevation
also causes a shadow to be created.@mvf4z7 Just keep track of this PR: https://github.com/facebook/react-native/pull/10954
My local Android unit and integration tests passed. Circle and Travis CI failed for some unrelated reasons, so as soon as someone from Reviewers could have a look into this, maybe we will have it merged. I wouldn’t mind to have it asap as well 😉
Issue exists with react-native version 0.50.3 Works fine on iOS, compatibility issues with Android.
Still an issue in 0.32. It seems that zindex breaks component unmounting.
I think the issue is that zIndex was not updated properly after the view was mounted. #15203 should fix it, let me know if you can test it and it fixes your problem.
Can confirm what others have mentioned. I added the following to my style.alert and it works on both platforms now.
I’m also having an issue with
zIndex
+position: 'absolute'
on Android. I have a dropdown menu component that always displays the currently selected item, and when it is activated, it also displays a list of all of the available dropdown menu items below this. The view that this dropdown menu is used in has other components as siblings of the dropdown menu component, and they are all after the dropdown menu component, since I want the currently selected item of the dropdown menu component to be shown first.I’m trying to use
zIndex
so that the list of the available dropdown menu items is shown above the other components that are rendered after the dropdown menu. I’m simply applying azIndex: 2
to the<View>
that contains this list of available dropdown menu items to achieve this. The list of available dropdown menu items is rendered within a<View>
that hasposition: 'absolute'
.This technique is working perfectly on iOS and behaves exactly as I expect/want. However, the exact same code is not working on Android; The list of available dropdown menu items is always rendered behind the other components that are rendered after the dropdown menu, and therefore is not visible. I’ve tried all sorts of variations of
zIndex
values for different components in the hierarchy, as well aselevation
, and nothing seems to make this work on Android, even though it works fine on iOS.Something must be different about how
zIndex
works in iOS vs Android, as the same code behaves differently in these two environments.It’s been unresolved for too long, any updates?
Issue exists with react-native version 0.51.0 too
Got the same problem here ! After reading the discussions and skimming through the code I realized that the root problem lies in the current implementation of
zIndex
which is basically invokingbringToFront()
on the child views in order of their zIndex. ( It is done inViewGroupManager.reorderChildrenByZIndex()
method)This method spoils the natural index of child views in their parent. Therefore
indicesToRemove
parameter inNativeViewHierarchyManager.manageChildren
will no longer refer to the actual child.@asgvard did a beautiful job in PRs (#10954) trying to use tag-reference instead of child-index. However @astreet suggested the problem is better be solved fundamentally by migrating to a
zIndex
implementation mechanism that doesn’t mess with child-view indices (probably using android’sViewGroup#getChildDrawingOrder
).It seems like 23 days ago @janicduplessis fixed zIndex mechanism in commit 9a51fa8. When I used react-native source from master branch everything was working just fine.
Hope this commit goes on the next release !
Same issue here, everything works as expected without a zIndex (It just doesn’t look right) Once I add a zIndex of 1 it goes to shit.
any updates?
UPDATE:
First of all, I found out that it has nothing to do with the reorderChildrenByZIndex() at all, because we actually don’t need to reorder children after we remove something, because the relative zIndex order for remaining items remain the same. The problem was exactly in that it was removing the wrong
View
by it’s index in the array.Fixed by relying on
tagsToDelete
instead ofindicesToRemove
inmanageChildren
method ofNativeViewHierarchyManager
. Because after reordering we cannot rely on the item index in theViewGroup
, but thetag
seems to be reliable way of identifying theView
anytime. I think the similar reason of usingtagsToDelete
was applied here.Also this issue happens when Adding views, it adds them in the wrong index 😃 But since after adding the views we’re doing reorder again, it doesn’t matter that it was added into the wrong place.
So the proposed solution is to change
manageChildren
method, so it will first make a loop bytagsToDelete
, performremoveView()
on all thetagsToDelete
, then after the cleanup of all the nodes to delete, we perform a loop byviewsToAdd
and that’s it. It will remove the huge chunk of code from here to here, because essentially all it’s doing isviewManager.removeViewAt(viewToManage, indexToRemove);
only in the case if the node is not animated and not in thetagsToDelete
array. So instead we could just move this responsibility to the loop bytagsToDelete
.I will prepare PR soon 😃
If it’s helpful to anyone, I created a
Hideable
component to handle this:Here’s a simple use:
Here’s a more complex, interactive usage example:
You should be able to pass through whatever styles or props you’d like to
Hideable
’sView
, make it animatable, etc.Also, @tioback, I noticed you set a negative
zIndex
, is that something you recommend? It’s worked for me without it but I wasn’t sure if it was needed for certain devices or certain use cases. I also noticed that on my device I had to remove the vertical margins and padding as well, so there may be other styles to watch out for.I’m still pretty new to React Native so, grain of salt.
+1. Works great on ios, breaks views in android
My RN version is 0.49.5 and I’ve encountered the same issue when setting the
position
toabsolute
. My workaround is rearranging the components, whose orders are based on thezIndex
. This may introduce some chaos but should be a better approach than setting theelevation
.@glenn-axsy and others reading his comment, just a warning:
elevation
is not a one-to-one solution forzIndex
. For me it never worked how I needed it, I would get shadows when I didn’t, and also the pointerEvents order was incorrect.Fortunately I did not have to deal with pointer-events with my code. I popup an alert on the screen that disappears after a few seconds.
Are you using a Modal component? You shouldn’t need to set zIndex/elevation for that. Make sure the order of your components are ordered correctly. Children components appear on top of parent components so see if you can change things up to get it to work.
A sincere thank you for trying your hand to fix it @janicduplessis - it is a blocker issue for the app I am working on.
Yo I’m the author of that commit 😃
@asgvard Thanks for the fantastic bug reporting. I think you’re right, check out:
https://github.com/facebook/react-native/blob/9ee815f6b52e0c2417c04e5a05e1e31df26daed2/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewGroupManager.java#L123
I think the fix is as simple as matching the
addView
method so it looks like:I don’t really have the bandwidth to PR a fix and test it right now, but you probably could 😃 It’s a good first PR.