Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SafeAreaView flickers with incorrect initial insets on Android #364

Open
janicduplessis opened this issue Mar 9, 2023 · 22 comments
Open

Comments

@janicduplessis
Copy link
Collaborator

This seems to happen with translucent status bar, when it is initially rendered on app start.

@janicduplessis janicduplessis changed the title SafeAreaView flickers with incorrect initial insets SafeAreaView flickers with incorrect initial insets on Android Mar 9, 2023
@jacobp100
Copy link
Collaborator

Can you provide a minimal repro?

@janicduplessis
Copy link
Collaborator Author

@jacobp100 I discovered this issue in the expensify app in the PR Expensify/App#15778, will work on getting a minimal repro in the example app soon.

@jacobp100
Copy link
Collaborator

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?

@janicduplessis
Copy link
Collaborator Author

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.

@jacobp100
Copy link
Collaborator

Oh interesting. I wonder if android does the same thing of reporting only the safe area that overlaps with the view itself

@MichalKrakow
Copy link

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.

@todorone
Copy link

It may be connected with this issue - #186, which @jacobp100 by some reason closed without explanation on how it could be solved or workarounded.

@jacobp100
Copy link
Collaborator

I closed it because there’s not enough information to reproduce it

@zabojad
Copy link

zabojad commented Mar 15, 2023

I'm seeing the same behavior with "react-native-safe-area-context": "4.2.5" on Android only...

@jacobp100
Copy link
Collaborator

You need a full repo showing it because the config you put in your android manifest affects the behaviour of the library

@zabojad
Copy link

zabojad commented Mar 15, 2023

@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?

@jacobp100
Copy link
Collaborator

I don't work on Android anymore - so I don't remember

We have a ticket for this #349 - it would be really useful if someone is able to help out with this - because I won't be able to

@HenrikZabel
Copy link

@janicduplessis did you manage to fix it? If yes, could you share how you did it?

@toviszsolt
Copy link

toviszsolt commented Mar 21, 2024

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>
Screen_Recording_20240320_223732_Tip.Split.Calculator.mp4

@XantreDev
Copy link

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));

@isaachinman
Copy link

Anyone have any advice on how to solve this? Seeing this only on Android, Expo v50 / expo-router / react-navigation.

@XantreDev
Copy link

Our workaround is to set initial metrics for the lib as a fullscreen

@rpc1910
Copy link

rpc1910 commented Aug 22, 2024

Is there a solution for this issue?

@melyux
Copy link

melyux commented Oct 15, 2024

This happens on iOS for me too, not just Android.

@SimpleCreations
Copy link

Here's my workaround. Please note that it works best if you have a translucent status bar but opaque navigation bar. I haven't tested this with other cases.

import { Platform } from 'react-native'
import { initialWindowMetrics } from 'react-native-safe-area-context'

// Workaround for the issue causing `initialWindowMetrics` to have incorrect
// bottom inset value on some Android devices
const getInitialWindowMetrics = () => {
  if (Platform.OS !== 'android' || initialWindowMetrics == null) {
    return initialWindowMetrics
  }

  const { frame, insets } = initialWindowMetrics
  return {
    frame: { ...frame, height: frame.height + insets.top },
    insets: { ...insets, bottom: 0 }
  }
}

export const initialSafeAreaWindowMetrics = getInitialWindowMetrics()
  return (
    <SafeAreaProvider initialMetrics={initialSafeAreaWindowMetrics}>
      <StatusBar translucent backgroundColor="transparent" />
       {/* ... */}
    </SafeAreaProvider>
  )

@Tsabary
Copy link

Tsabary commented Dec 10, 2024

Facing the same issue on Pixel 9 pro XL

@Tsabary
Copy link

Tsabary commented Dec 10, 2024

Here's my workaround. Please note that it works best if you have a translucent status bar but opaque navigation bar. I haven't tested this with other cases.

import { Platform } from 'react-native'
import { initialWindowMetrics } from 'react-native-safe-area-context'

// Workaround for the issue causing `initialWindowMetrics` to have incorrect
// bottom inset value on some Android devices
const getInitialWindowMetrics = () => {
  if (Platform.OS !== 'android' || initialWindowMetrics == null) {
    return initialWindowMetrics
  }

  const { frame, insets } = initialWindowMetrics
  return {
    frame: { ...frame, height: frame.height + insets.top },
    insets: { ...insets, bottom: 0 }
  }
}

export const initialSafeAreaWindowMetrics = getInitialWindowMetrics()
  return (
    <SafeAreaProvider initialMetrics={initialSafeAreaWindowMetrics}>
      <StatusBar translucent backgroundColor="transparent" />
       {/* ... */}
    </SafeAreaProvider>
  )

This doesn't seem to work for me. Copied the code, an using like this

     <SafeAreaProvider
        initialMetrics={initialSafeAreaWindowMetrics}
        style={{ flex: 1, backgroundColor: "#030712" }}
      >
        <StatusBar style="light" translucent />

And I still get flickering

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests