react-native-screens icon indicating copy to clipboard operation
react-native-screens copied to clipboard

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

Open IvanIhnatsiuk opened this issue 2 years ago • 27 comments

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

  • [x] iOS
  • [x] Android
  • [ ] Web
  • [ ] Windows
  • [ ] tvOS

Workflow

  • [ ] Managed workflow
  • [x] 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

IvanIhnatsiuk avatar Jan 21 '22 12:01 IvanIhnatsiuk

Can you explain why this SafeAreaProvider at the top is needed?

WoLewicki avatar Jan 21 '22 12:01 WoLewicki

@WoLewicki Yes, sure. We use the insets from SafeAreaProvider in our project, if you remove it, the flickering problem is still on iOS.

IvanIhnatsiuk avatar Jan 21 '22 12:01 IvanIhnatsiuk

I was asking since it is already applied in native-stack: https://github.com/react-navigation/react-navigation/blob/e9c690450068d29a2ce62004992ad75d626ca0c2/packages/native-stack/src/views/NativeStackView.native.tsx#L322 so it may be causing some problems with layout. As for the flickering, does the whole content flicker or is it just the header? It is not visible when you have white background 😅

WoLewicki avatar Jan 21 '22 13:01 WoLewicki

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

IvanIhnatsiuk avatar Jan 21 '22 13:01 IvanIhnatsiuk

@owinter86 Thank you for your research of the problem! I tried the solution of wrapping each tab element in SafeAreaProvider. Unfortunately, it doesn't solve the problem with flickering on iOS 😞 . Also, it doesn't always help on android, sometimes the screen jumps on the initial render 😞 . I think the author of this issue also tried it because he described it in the description. Also, I didn't find any information about wrapping each tab element in the documentation. I think that the application should have only one SafeAreaProvider and it should already contain all the metrics that the native Stack and JS Stack use. Anyway, It might be a good vector to find a problem, Thanks!

IvanIhnatsiuk avatar Jan 27 '22 08:01 IvanIhnatsiuk

I'm also expecting this issue on Android.

My content jumps, flickering and hides behind the header. Do you have any updates here?

vladyslavNiemtsev avatar Feb 18 '22 12:02 vladyslavNiemtsev

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

a-eid avatar Mar 02 '22 15:03 a-eid

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

ferrannp avatar Mar 31 '22 14:03 ferrannp

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

iway1 avatar May 25 '22 16:05 iway1

@iway1 when you say reverting to 3.3.0 you mean react-native-screens?

This is what I'm running right now and experiencing jumping on first render:

    "@react-navigation/bottom-tabs": "^6.3.1",
    "@react-navigation/native": "^6.0.10",
    "@react-navigation/stack": "^6.2.1",
    "react-native-gesture-handler": "^2.4.2",
    "react-native-safe-area-context": "^4.3.1",
    "react-native-screens": "^3.13.1",

l0gicgate avatar Jun 13 '22 23:06 l0gicgate

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.

mirzalikic avatar Jun 15 '22 16:06 mirzalikic

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>

mordechaim avatar Jun 15 '22 16:06 mordechaim

@mordechaim no, that's not the solution, it jumps anyway and creates unnecessary space in the safe areas, especially on ios

mirzalikic avatar Jun 15 '22 22:06 mirzalikic

@mirzalikic I’ve posted a solution on a similar issue. Stubbing SafeAreaView with a custom implementation using the useSafeAreaInsets measurements resolved my problems:

https://github.com/software-mansion/react-native-screens/issues/1251#issuecomment-1154533585

l0gicgate avatar Jun 16 '22 00:06 l0gicgate

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

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

sjransom avatar Jul 01 '22 20:07 sjransom

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

SashaGo3 avatar Jul 14 '22 19:07 SashaGo3

@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 :(

aymather avatar Jul 27 '22 20:07 aymather

Still having the issue aswell on android, removed all SafeAreaViews and replaced with View + insets but the problem persists. Could anyone post a full example to see if I'm doing anything wrong?/Alternate solutions?

ystefanov6 avatar Jul 29 '22 14:07 ystefanov6

@ystefanov6 check my comment on #1251 to see if that helps you at all

aymather avatar Jul 29 '22 14:07 aymather

@ystefanov6 check my comment on #1251 to see if that helps you at all

Thanks for this. Unfortunately I'm using expo and the latest supported version of screens for sdk 45.0 is 3.11.1, upgrading anyway doesn't seem to fix it either. Don't know what else to try at this point.

ystefanov6 avatar Jul 31 '22 16:07 ystefanov6

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

android platform

yahacom avatar Aug 04 '22 10:08 yahacom

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.

ystefanov6 avatar Aug 04 '22 10:08 ystefanov6

@mirzalikic solution worked for me, just downgrade to [email protected]

khethelogp avatar Sep 27 '22 13:09 khethelogp

Same issue with [email protected]

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 [email protected] is the only solution I found.

JoshBot-Debug avatar Nov 26 '22 14:11 JoshBot-Debug

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! 🙏

banjolina-jolie avatar Feb 19 '23 06:02 banjolina-jolie

Can anyone create a repo with the solution suggested? I tried wrapping SafeAreaView and SafeAreaProvider it was still flickering for me. @banjolina-jolie ?

Sumit2202 avatar May 08 '23 20:05 Sumit2202

I still facing same problem, there is any example to follow

ayoubechc avatar Jun 20 '23 10:06 ayoubechc