[3.7.0] Android change broke with appcompat >= 1.3.x and detachPreviousScreen
See original GitHub issueDescription
https://github.com/software-mansion/react-native-screens/pull/1066 introduced a bug that causes inactive screen to end up stuck when detachPreviousScreen is used alongside androidx.appcompat:appcompat 1.3.1 or above. The issue does not happen when using the outdated 1.1.0 version.
Screenshots

Steps To Reproduce
Force a compat version greater than 1.1, e.g.,
implementation('androidx.appcompat:appcompat') {
version {
strictly '1.3.1'
}
}
Use stack navigator with detachPreviousScreen, e.g.,
/* eslint-disable react/display-name */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Text, View, StyleSheet,TouchableOpacity, StatusBar, Animated } from 'react-native';
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
const animationConfig = {
animation: 'spring',
config: {
stiffness: 1100,
damping: 100,
mass: 3,
overshootClamping: true,
restDisplacementThreshold: 10,
restSpeedThreshold: 10,
useNativeDriver: true,
},
};
//let animationInterp = CardStyleInterpolators.forHorizontalIOS; // use iOS slide as well on Android, looks cool
// https://reactnavigation.org/docs/stack-navigator/#animations
// https://github.com/react-navigation/react-navigation/blob/6cba517b74f5fd092db21d5574b558ef2d80897b/packages/stack/src/TransitionConfigs/CardStyleInterpolators.tsx#L14
const {multiply} = Animated;
const animationInterp = ({current, next, inverted, layouts: {screen}}) => {
const translateFocused = multiply(
current.progress.interpolate({
inputRange: [0, 1],
outputRange: [screen.width, 0],
extrapolate: 'clamp',
}),
inverted,
);
const translateUnfocused = next
? multiply(
next.progress.interpolate({
inputRange: [0, 1],
outputRange: [0, screen.width * -0.8],
extrapolate: 'clamp',
}),
inverted,
)
: 0;
const overlayOpacity = current.progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 0.1],
extrapolate: 'clamp',
});
const shadowOpacity = current.progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 0.3],
extrapolate: 'clamp',
});
return {
cardStyle: {
transform: [
// Translation for the animation of the current card
{translateX: translateFocused},
// Translation for the animation of the card on top of this
{translateX: translateUnfocused},
],
},
overlayStyle: {opacity: overlayOpacity},
shadowStyle: {shadowOpacity},
};
};
function ScreenX({navigation, route}) {
return (
<View style={styles.screenContainer}>
<Text style={styles.paragraph}>
This screen is used for testing bugs!
</Text>
<TouchableOpacity onPress={() => navigation.goBack()}><Text>Go Back</Text></TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate(route.params?.next)}><Text>Go Next</Text></TouchableOpacity>
</View>
);
}
function ScreenEnd({navigation}) {
return (
<View style={styles.screenContainer}>
<Text style={styles.paragraph}>
Final screen.
</Text>
<TouchableOpacity onPress={() => navigation.goBack()}><Text>Go back</Text></TouchableOpacity>
</View>
);
}
const Stack = createStackNavigator();
const StackNavigator = () => {
return (
<Stack.Navigator
screenOptions={({navigation, route}) => {
return {
headerMode: 'screen',
header: (props) => (
<View style={styles.headerStyle}>
<Text>{props.options.title}</Text>
</View>
),
gestureEnabled: false,
cardOverlayEnabled: true,
transitionSpec: {
open: animationConfig,
close: animationConfig,
},
cardStyleInterpolator: animationInterp,
};
}}>
<Stack.Screen
name="Screen1"
component={ScreenX}
options={({navigation, route}) => {
return {
title: 'Home',
header: (props) => (
<View style={styles.headerStyle}>
<Text>Home Test</Text>
</View>
),
};
}}
initialParams={{ next: 'Screen2'}}
/>
<Stack.Screen
name="Screen2"
component={ScreenX}
options={{title: 'Screen2'}}
initialParams={{ next: 'Screen3'}}
/>
<Stack.Screen
name="Screen3"
component={ScreenX}
options={{
title: 'Screen3',
headerShown: false,
animationEnabled: true,
detachPreviousScreen: false,
}}
initialParams={{ next: 'Screen4' }}
/>
<Stack.Screen
name="Screen4"
component={ScreenEnd}
options={{title: 'Screen4'}}
/>
</Stack.Navigator>
);
};
export default function BuggedStack() {
return (
<View style={styles.container}>
<NavigationContainer>
<StackNavigator />
</NavigationContainer>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: StatusBar.currentHeight,
},
screenContainer: {
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'black',
padding: 24,
},
paragraph: {
margin: 24,
marginTop: 0,
fontSize: 14,
fontWeight: 'bold',
textAlign: 'center',
},
headerStyle: {
height: 80,
elevation: 3,
top: 0,
left: 0,
right: 0,
zIndex: 100,
backgroundColor: 'black',
}
});
Run the app and navigate next until the last screen.
Navigate back and observe
Expected behavior
Screen transitioning normally
Actual behavior
Screen stuck
Snack or minimal code example
https://github.com/cristianoccazinsp/react-native-screens/tree/bug-repro https://github.com/software-mansion/react-native-screens/compare/master...cristianoccazinsp:bug-repro?expand=1
Package versions
- React: 17.0.2
- React Native: 0.65.1
- React Native Screens: 3.7.0
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:14 (6 by maintainers)
Top Results From Across the Web
Getting error while running `react-native run-android`
The fix is simply to specify android core version via androidXCore in build. ... to implementation 'androidx.appcompat:appcompat:1.3.0'.
Read more >react-native-screens - bytemeta
SearchBar not appearing · Swipe to go back gesture on iOS does not work properly · [3.7.0] Android change broke with appcompat >=...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found

Ok, let me know if you find out anything concerning the behavior in newer versions. I will also try and debug it a little in the free time, but I guess it won’t be easy to apply changes to the code such that it works correctly for all versions of
appCompat.I’m using
detachPreviousScreenfor a totally non-related issue. Basically, I have one screen that renders stuff in a modal and exposes that feature through its context. However, that screen may sometimes take the user to a second screen that in turn may request the previous screen to show its modal (odd I know!).So without
detachPreviousScreen, the modal cannot be shown because the previous screen is taken out of the rendering tree. I ended up usingdetachPreviousScreenonly on iOS, as on Android, it does show even if the previous screen is detached. This difference in behaviour is also a bit odd, but not a big deal.