react-native-safe-area-context
react-native-safe-area-context copied to clipboard
SafeAreaView flickers with incorrect initial insets on Android
This seems to happen with translucent status bar, when it is initially rendered on app start.
Can you provide a minimal repro?
@jacobp100 I discovered this issue in the expensify app in the PR https://github.com/Expensify/App/pull/15778, will work on getting a minimal repro in the example app soon.
Sorry I didn’t realise it was you 🤣 I’m not too familiar with the android side of things. Are the insets available when the view mounts?
I think they are, but wrongly have a bottom value, which might be what causes the flicker. I noticed by logging initialWindowMetrics. Might be related to the flags the app window has initially.
Oh interesting. I wonder if android does the same thing of reporting only the safe area that overlaps with the view itself
It flickers both android ios, on production build, development build, and inside expo. I use react navigation and only few of my views utilize SafeAreaView.
It may be connected with this issue - https://github.com/th3rdwave/react-native-safe-area-context/issues/186, which @jacobp100 by some reason closed without explanation on how it could be solved or workarounded.
I closed it because there’s not enough information to reproduce it
I'm seeing the same behavior with "react-native-safe-area-context": "4.2.5"
on Android only...
You need a full repo showing it because the config you put in your android manifest affects the behaviour of the library
@jacobp100 Interesting! What exact parameters from the Android manifest file do affect the behavior of this lib?
EDIT: what easy changes could I try before following your suggestion of providing a reproducer?
I don't work on Android anymore - so I don't remember
We have a ticket for this https://github.com/th3rdwave/react-native-safe-area-context/issues/349 - it would be really useful if someone is able to help out with this - because I won't be able to
@janicduplessis did you manage to fix it? If yes, could you share how you did it?
Hello guys!
- Here is an example video, slow down to see what is happening.
- Layout shift happens on the first and fourth start, there are 4 starts in total.
- I was never able to replicate the phenomenon in any way at Expo Go.
- This effect only occurs in final live builds. I used EAS Build / Play Console.
- I also noticed that if I clear the application data (not the cache, but everything), the first startup is always good.
- The occurrence of the problem seems to be completely random, sometimes 10 times in a row.
- @jacobp100 I can give you access to my private repo so you can have test code. There is only one not too complicated screen. Just please let me know if you need it.
Update:
- I just tested the same app on Android 10 and it doesn't have this problem.
- By the way, the video was made on Android 14.
In the App.js
I use the SafeAreaProvider
:
<SafeAreaProvider>
<HomeScreen onLayout={hideSplashScreen} />
<StatusBar style="auto" translucent />
</SafeAreaProvider>
The HomeScreen
wrapped with SafeAreaView
:
<SafeAreaView onLayout={onLayout} style={[styles.flexFit, theme.container]}>
<ScrollView
contentContainerStyle={[styles.container]}
showsVerticalScrollIndicator={false}
keyboardDismissMode="interactive"
>
...
</ScrollView>
</SafeAreaView>
https://github.com/th3rdwave/react-native-safe-area-context/assets/18605518/e2df7891-3974-4c31-b575-2c246e3ed862
In my case problem was with incorrect initialWindowMetrics from the lib. First render. It get's some safe area insets
insets {"bottom": 24, "left": 0, "right": 0, "top": 38.095237731933594} frame {"height": 852.1904907226562, "width": 411.4285583496094, "x": 0, "y": 38.095237731933594}
Second rerender, after layout
layout {"height": 852.1904907226562, "width": 411.4285583496094, "x": 0, "y": 0}
LOG insets {"bottom": 0, "left": 0, "right": 0, "top": 0} frame {"height": 852.1904907226562, "width": 411.4285583496094, "x": 0, "y": 38.095237731933594}
SafeAreaProvider placed almost on the root.
const Providers = ({ children }: PropsWithChildren) => (
<SafeArea.Provider>
<GestureHandlerRootView className="flex-1">
<CacheLibraryProvider>
<NavigationContainer
theme={NAVIGATION_THEME}
onStateChange={onNavigationStateChange}
ref={setNavigationContainerRef}
>
<BottomSheetModalProvider>
<ModalProvider>
<NoInternetToast.Provider shouldShow={$$(!$isOnline.value)}>
<LogoutProvider>{children}</LogoutProvider>
</NoInternetToast.Provider>
</ModalProvider>
</BottomSheetModalProvider>
<Toast />
</NavigationContainer>
</CacheLibraryProvider>
</GestureHandlerRootView>
<OrientationLocker orientation="PORTRAIT" />
</SafeArea.Provider>
);
const App = ({
isHeadless,
}: {
/**
* ios specific props from firebase messaging https://rnfirebase.io/messaging/usage#background-application-state
*/
isHeadless?: boolean;
}) => {
useEffect(() => {
Analytics.triggerEventByName('AppLaunched');
//TODO: Rewrite the code to avoid using this hack
void restartWatchIfStarted();
SplashScreen.hide();
}, []);
useAntifraudService();
if (isHeadless) {
return null;
}
return (
<Providers>
<StatusBar backgroundColor={'#151515'} />
<View className="absolute inset-0 bg-gray-700" />
<NoInternetToast.Ui />
<ConditionalRouting />
</Providers>
);
};
// eslint-disable-next-line import/no-default-export
export default withIAPContext(Sentry.wrap(App));