react-native-screens: bug with @react-navigation/bottom-tabs - initial jumping and flickering

Description

When I’m using @react-navigation/bottom-tabs with the native stack navigator, the header jumps on android and sometimes flickers on iOS on tab change (on the first render only). But it doesn’t happen if I use the JS stack navigator.

I wrapped NavigationContainer with SafeAreaProvider like this:

<SafeAreaProvider initialMetrics={initialWindowMetrics}>
      <NavigationContainer>
        <RootStack />
      </NavigationContainer>
</SafeAreaProvider>

I also patched react-navigation bottoms tabs with this PR applied by @WoLewicki react-navigation/react-navigation#9772

If I remove initialWindowMetrics jump on Android is gone, but on iOS, the header is still flickering.

it maybe related to this issue

Screenshots

https://user-images.githubusercontent.com/86000012/150523094-f45c0d6d-b059-45f1-aaae-2f7441b1f0a3.mov

https://user-images.githubusercontent.com/86000012/150523458-835419ca-5f86-4762-a677-6060101566a1.mov

Steps To Reproduce

  1. Open app.
  2. Click on Tab2.
  3. See the position of the header.

Expected behavior

the header should not jump on android and flicker on iOS(on initial render).

Actual behavior

on initial render, header jumps on Android and flickers on iOS

Reproduction

reproduction repo:

https://github.com/IvanIhnatsiuk/native-stack-bottom-jump

Platform

  • iOS
  • Android
  • Web
  • Windows
  • tvOS

Workflow

  • Managed workflow
  • Bare workflow

Package versions

package version
react-native 0.66.4
@react-navigation/native 6.0.9
@react-navigation/native-stack 6.2.5
react-native-screens 3.10.2
react-native-safe-area-context 3.3.2
react-native-gesture-handler 2.2.0
react-native-reanimated 2.3.1
@react-navigation/bottom-tabs 6.0.9

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 14
  • Comments: 27 (3 by maintainers)

Commits related to this issue

Most upvoted comments

@WoLewicki I also experience the iOS flicker when navigating for the first time into a tab. I can tell that:

  1. It happens from any version after 3.3.0. In 3.3.0, it works
  2. It only happens when your tab is a stack like here: https://github.com/IvanIhnatsiuk/native-stack-bottom-jump/blob/master/src/navigation/BottomTabNav/index.tsx#L10. If the stack is just a normal view, it does not flicker
  3. You need to have some content inside your tab. Not just a text or empty content in order for this to happen

I can also confirm this issue happens after this PR https://github.com/software-mansion/react-native-screens/pull/986.

The solution to this is to add initial safe area metrics to 0:

return <SafeAreaProvider initialMetrics={{
    insets: {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
    }
}}>
    <MyApp />
</ SafeAreaProvider>

And wrap your tab navigator with SafeAreaView:

return <SafeAreaView style={{ flex: 1}} >
    <Tab.Navigator>
        {myAwesomeTabs}
    </Tab.Navigator>
</SafeAreaView>

@ferrannp I am getting flickering when my tab navigator is nested within a stack navigator as well, and for me it only happens when the component rerenders very shortly after the component initially mounts. If no rerender happens quickly the problem does not occur

Reverted to 3.3.0 as you said, and it fixed the issue.

Is anyone working on this? Or should I just assume I need to default to version 3.3.0 moving forward to avoid this sort of thing?

The solution to this is to add initial safe area metrics to 0:

return <SafeAreaProvider initialMetrics={{
    insets: {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
    }
}}>
    <MyApp />
</ SafeAreaProvider>

And wrap your tab navigator with SafeAreaView:

return <SafeAreaView style={{ flex: 1}} >
    <Tab.Navigator>
        {myAwesomeTabs}
    </Tab.Navigator>
</SafeAreaView>

Wrapping Tab.Navigator with SafeAreaView worked for me! 🙏

@sjransom Thanks, your solution works! no more jumping and flickering

replaced <SafeAreaView> on Tab Screens to <View> with the insets

import { useSafeAreaInsets } from 'react-native-safe-area-context';

function HookComponent() {
  const insets = useSafeAreaInsets();

  return <View style={{ paddingTop: Math.max(insets.top, 16) }}> ... </View>
}

Yes, unfortunately you have to downgrade react-native-screens to 3.3.0 to remove the white flash between tab switches on ios. This doesn’t always happen, only when the screen loads for the first time. it seems that the native stack navigator doesn’t play well with the bottom tab navigator.

@ferrannp described exactly what caused the flicker.

On android the combination of native stack navigator and bottom tab navigator causes a short jump during initial load.

Same issue with react-native-screens@3.18.0

Downgrading to 3.3.0 resolved that problem. However, it messed up other things in the app so I upgraded back to 3.18.0.

Doing this fixed the <SafeAreaView/> flicker on all screens except the stacks that are used in bottom tab stack.

import { useSafeAreaInsets } from 'react-native-safe-area-context';

const Component = () => {
  const insets = useSafeAreaInsets();

  return <View style={{ marginTop: insets.top }}> ... </View>
}

Finally the solution:~

Don’t use SafeAreaView instead use the hook useSafeAreaInsets() If you use Bottom Tab Navigator createBottomTabNavigator(). Don’t pass a Stack in component prop. Pass a View. In some cases you might not want or be able to do that, if that’s the case downgrade to react-native-screens@3.3.0 is the only solution I found.

@mirzalikic solution worked for me, just downgrade to react-native-screens@3.3.0

I finally managed to fix it in my case by replacing materialBottomTabs to just the bare bottomTabs, it lacks nice animations but flicker is gone. Apparently it’s a known issue with materialBottomTabs and stack nav when they’re nested together. Hope this helps someone. If you’re looking for a pre-built animated component for the navbar I suggest https://www.npmjs.com/package/rn-wave-bottom-bar.

same issue on react-native-screens: "3.9.0" downgrade to 3.3.0 not helps

android platform

@SashaGo3 is there any chance you can provide a full example of something that works? I’m trying to use your suggestion but I’m still getting the glitching effect 😦

Fixed this by swapping <SafeAreaView /> with useSafeAreaInsets()

<View style={[styles.container, { paddingTop: Math.max(insets.top, 16) }]} {...otherProps}>

@WoLewicki are u suggesting that when using the native-stack there is no need to wrapping your entire app in a SafeAreaProvider ?

Sorry, in official react-navigation they suggest to wrap entire app in a SafeAreProvider : https://reactnavigation.org/docs/handling-safe-area/ But they also have SafeAreaPrivederCompat: https://github.com/react-navigation/react-navigation/blob/main/packages/stack/src/views/Stack/StackView.tsx#L430 And Js stacks are not flickering/jumping 🤔 Am I doing something wrong? Video with non-white color is attached:

https://user-images.githubusercontent.com/86000012/150536292-81ba476e-a9a6-435b-8103-cf4739ce21bd.mov