Commit 1b3d3f9
fix(Android): crash API 25 when going back from screen with flat list (#2964)
## Description
Fixes #2810
This PR should fix a crash when going back from the screen that has flat
list.
## Changes
There are two exceptions thrown:
**First:**
```
Error: Exception in HostFunction: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
```
**Second:**
```
java.lang.IndexOutOfBoundsException: getChildDrawingOrder() returned invalid index 1 (child count is 1)
```
And it fails around this place in `Screen.kt`:
```
(...)
if (parent is SwipeRefreshLayout && child is ImageView) {
// SwipeRefreshLayout class which has CircleImageView as a child,
// does not handle `startViewTransition` properly.
// It has a custom `getChildDrawingOrder` method which returns
// wrong index if we called `startViewTransition` on the views on new arch.
// We add a simple View to bump the number of children to make it work.
// TODO: find a better way to handle this scenario
it.addView(View(context), i)
} else {
child?.let { view -> it.startViewTransition(view) }
}
(...)
```
Based on the comment, I assume `it.addView(View(context), i)` is to
prevent the **second** crash. Because the **first** exception, we do not
add "workaround" view, thus we run into **second**. We get the **first**
exception, as the operation is performed from different thread, thus I
ensure the adding view is performed from UIThread.
## Test code and steps to reproduce
<details>
<summary>Source code</summary>
```
import * as React from 'react';
import { View, Text, FlatList } from 'react-native';
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
import { Button } from '@react-navigation/elements';
import { SafeAreaProvider } from 'react-native-safe-area-context';
type StackParamList = {
Home: undefined;
Details: undefined;
};
function HomeScreen() {
const navigation = useNavigation<NativeStackScreenProps<StackParamList, 'Home'>['navigation']>();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button onPress={() => navigation.navigate('Details')}>
Go to Details
</Button>
</View>
);
}
function DetailsScreen() {
const [isRefreshing, setIsRefreshing] = React.useState(false);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<FlatList
data={[1, 2, 3, 4, 5]}
renderItem={({ item }) => <Text>{item}</Text>}
onRefresh={() => {
return new Promise<void>((resolve) => {
setIsRefreshing(true);
setTimeout(() => {
resolve();
}, 3000);
}).then(() => {
setIsRefreshing(false);
});
}}
refreshing={isRefreshing}
/>
</View>
);
}
const Stack = createNativeStackNavigator<StackParamList>();
function NestedStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
);
}
export default function App() {
return (
<SafeAreaProvider>
<NavigationContainer>
<NestedStack />
</NavigationContainer>
</SafeAreaProvider>
);
};
```
</details>
## Checklist
- [x] Included code example that can be used to test this change
- [ ] Ensured that CI passes
---------
Co-authored-by: Kacper Kafara <[email protected]>1 parent 3644f6b commit 1b3d3f9
File tree
1 file changed
+7
-1
lines changed- android/src/fabric/java/com/swmansion/rnscreens
1 file changed
+7
-1
lines changedLines changed: 7 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
66 | | - | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
67 | 73 | | |
68 | 74 | | |
69 | 75 | | |
| |||
0 commit comments