I have a TabBar. 3 Tabs. Each Tab has its own stack. On iOS, being in Tab 3 and navigating inside that tab and switching tabs and returning back to tab 3: everything works as expected. I am still on the same screen.
When I do the same on android, I get back to the first screen of that Tab, while I should remain on the same inside stack screen. So the navigation state gets lost. Assume I have an open form and just clicked on a chat notification, returning back and everything is gone and I have to go over again.
React-native-screens 2.2.0.
react-native-screens/native-stack
react-navigation 5
Try setting https://reactnavigation.org/docs/bottom-tab-navigator#unmountonblur to false. Does the stack still reset after this?
It鈥檚 false by default. And it does not occur on iOS, only on Android.
It鈥檚 false by default. And it does not occur on iOS, only on Android.
True. Can you make a quick snack or repo with the minimal configuration that will show the issue?
I have absolutely the same issue on my Android (v9). ([email protected])
I cannot repro this issue on Android (v9), but I am not testing on a physical device. Can someone provide a repo or snack with the configuration needed to recreate the issue?
Neither am I. It's reproducible on emulator and physical device. Tested with Expo 37 and 2.4.0 directly with RN
I have 3 tabs and each one has a native-stack with two screens. Going to the second screen in any of the stack navigators and next switching between tabs does not reset the stack navigator both on Android 9 and X. I use bare RN project with the newest RNScreens. Remember that using ExpoClient v. 37 will give you native code from RNScreens in v. 2.2.0.
@WoLewicki Didn't understand that sentence
Remember that using ExpoClient v. 37 will give you native code from RNScreens in v. 2.2.0.
I use Expo v36.0.0, you want to say that issue can disappear after switching to the latest version?
When using Expo Client, it comes with the native code inside it that can't be changed by giving higher version of package in package.json. So if you use Expo Client v. 36, RNScreens will have native code of version "2.0.0-alpha.12" which you can see here: https://unpkg.com/browse/[email protected]/bundledNativeModules.json
@WoLewicki I've updated to the latest version of expo (at the moment it is 37.0.3) and issue still reproducible
P.S. I also have updated expo app on my android which running my app.
What I'm saying is you should try to make a bare RN project with the latest version of RNScreens and check if the issue exists there as well. If it doesn't, then it was probably fixed in the newer version of RNScreens and then it means that it will not work in Expo Client until they release the version with fixed native code of RNScreens so probably ~second half of June.
Ah, I understand. I have no time to create bare RN project, so I can't be useful here :(
P.S. I highly depend on expo in my project, so I will probably wait till they release the fix
Issues exists with v2.2.0 + 2.4.0
import * as React from "react";
import { Platform } from "react-native";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "react-native-screens/native-stack";
// import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createMaterialBottomTabNavigator } from "@react-navigation/material-bottom-tabs";
import { Entypo, MaterialCommunityIcons } from "@expo/vector-icons";
import { connect } from "react-redux";
import Profile from "../social/Profile";
import Timeline from "../social/Timeline";
import Messages from "../social/Messages";
import User from "../social/User";
import Message from "../social/Message";
import EditProfile from "../social/EditProfile";
import Account from "../social/Account";
import EditAccount from "../social/EditAccount";
import Blocked from "../social/Blocked";
import AddCar from "../social/Car/AddCar";
import SelectVehicle from "../social/Car/SelectVehicle";
import SelectMake from "../social/Car/SelectMake";
import SelectModel from "../social/Car/SelectModel";
import SelectType from "../social/Car/SelectType";
import SelectEngine from "../social/Car/SelectEngine";
import SelectPicture from "../social/Car/SelectPicture";
import Login from "../login/Login";
interface AppStackProps {
app: {
authToken: string;
user?: any[any[]];
unreadMessages?: number;
};
}
function getTabBarVisibleForTimeline(route) {
const routeName = route.state
? route.state.routes[route.state.index].name
: route.params?.screen || 'Home';
console.log(routeName);
if (routeName === 'User') {
return false;
}
return true;
}
function getTabBarVisibleForProfile(route) {
const routeName = route.state
? route.state.routes[route.state.index].name
: route.params?.screen || 'Profile';
console.log(routeName);
if (routeName === 'AddCar') {
return false;
}
return true;
}
const Chat = createNativeStackNavigator();
const ChatStack = () => {
return (
<Chat.Navigator
screenOptions={{
headerShown: true,
headerTintColor: "white",
headerLargeTitle: true,
headerStyle: { backgroundColor: "#b7cd23" }
}}
>
<Chat.Screen name="Messages" options={{ title: "Chats" }} component={Messages} />
</Chat.Navigator>
);
};
const Users = createNativeStackNavigator();
const UserStack = () => {
return (
<Users.Navigator
screenOptions={{
headerShown: false,
headerTintColor: "white",
headerLargeTitle: true,
gestureEnabled: true,
headerStyle: { backgroundColor: "#b7cd23" }
}}
>
<Users.Screen name="User" options={{ title: "Chats" }} component={User} />
{Platform.OS === "ios" && (
<>
<Users.Screen
options={{
stackPresentation: Platform.OS === "ios" && Platform.Version > 12 ? "modal" : "push",
headerShown: false,
gestureEnabled: true,
title: "Chat",
headerLargeTitle: false
}}
name="Message"
component={Message}
/>
</>)
}
</Users.Navigator>
);
};
const StackTimeline = createNativeStackNavigator();
const TimelineStack = () => {
return (
<StackTimeline.Navigator
screenOptions={{
headerShown: true,
headerTintColor: "white",
gestureEnabled: true,
headerLargeTitle: true,
headerStyle: { backgroundColor: "#b7cd23" }
}}
>
<StackTimeline.Screen options={{ title: "Entdecken" }} name="Timeline" component={Timeline} />
<StackTimeline.Screen
options={{
stackPresentation: "modal",
headerShown: false,
title: "User",
gestureEnabled: true,
headerLargeTitle: true
}}
name="User"
component={UserStack}
/>
</StackTimeline.Navigator>
);
};
const Stack = createNativeStackNavigator();
const ProfileStack = () => {
return (
<Stack.Navigator
screenOptions={{
headerShown: true,
headerTintColor: "white",
headerLargeTitle: true,
headerStyle: { backgroundColor: "#b7cd23" }
}}
>
<Stack.Screen name="Profile" options={{ title: "Einstellungen" }} component={Profile} />
<Stack.Screen
name="EditProfile"
options={{ headerShown: true, headerBackTitle: "Einst.", headerLargeTitle: false, title: "Profil bearbeiten" }}
component={EditProfile}
/>
<Stack.Screen
name="AddCar"
options={{ headerBackTitle: "Einst.", headerLargeTitle: false, title: "Kennzeichen" }}
component={AddCar}
/>
<Stack.Screen
name="SelectVehicle"
options={{ headerBackTitle: "Zur眉ck", headerLargeTitle: false, title: "Fahrzeugtyp" }}
component={SelectVehicle}
/>
<Stack.Screen
name="SelectMake"
options={{ headerLargeTitle: false, title: "Marke" }}
component={SelectMake}
/>
<Stack.Screen
name="SelectModel"
options={{ headerLargeTitle: false, title: "Model" }}
component={SelectModel}
/>
<Stack.Screen
name="SelectType"
options={{ headerLargeTitle: false, title: "Typ" }}
component={SelectType}
/>
<Stack.Screen
name="SelectEngine"
options={{ headerLargeTitle: false, title: "Motor" }}
component={SelectEngine}
/>
<Stack.Screen
name="SelectPicture"
options={{ headerLargeTitle: false, title: "Fast geschafft" }}
component={SelectPicture}
/>
<Stack.Screen
name="Account"
options={{ headerBackTitle: "Einst.", headerLargeTitle: false, title: "Account" }}
component={Account}
/>
<Stack.Screen
name="EditAccount"
options={{ headerLargeTitle: false, title: "Bearbeiten" }}
component={EditAccount}
/>
<Stack.Screen
name="Blocked"
options={{ headerLargeTitle: false, title: "Blockiert" }}
component={Blocked}
/>
</Stack.Navigator>
);
};
const Tab = createMaterialBottomTabNavigator();
const TabBar = (props: AppStackProps) => {
const { app } = props;
const hasPlate = app.user && app.user.plates && app.user.plates.length > 0;
return (
<Tab.Navigator
initialRouteName="Chats"
activeColor="#b7cd23"
barStyle={{ backgroundColor: "#f5f5f5" }}
tabBarOptions={{
activeTintColor: "#b7cd23",
keyboardHidesTabBar: false
}}
>
<Tab.Screen
name="Timeline"
component={TimelineStack}
options={({ route }) => ({
tabBarLabel: "Entdecken",
tabBarVisible: getTabBarVisibleForTimeline(route),
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons name="newspaper" color={color} size={25} />
)
})}
/>
<Tab.Screen
name="Chats"
component={ChatStack}
options={{
tabBarBadge: hasPlate && app.unreadMessages > 0 ? app.unreadMessages : false,
tabBarLabel: "Chats",
tabBarIcon: ({ color }) => (
<Entypo style={{ height: 30, width: 30 }} name="chat" color={color} size={25} />
)
}}
/>
<Tab.Screen
name="Profile"
component={ProfileStack}
options={({ route }) => ({
tabBarLabel: "Einstellungen",
tabBarVisible: getTabBarVisibleForProfile(route),
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons name="settings" color={color} size={25} />
)
})}
/>
</Tab.Navigator>
);
};
const TabStack = connect(state => ({ app: state }))(TabBar);
const RootStack = createNativeStackNavigator();
const Root = createNativeStackNavigator();
const AppStack = (props: AppStackProps) => {
const { app } = props;
return (
<NavigationContainer>
<Root.Navigator
screenOptions={{
headerShown: false,
headerTintColor: "white",
headerLargeTitle: true,
headerStyle: { backgroundColor: "#b7cd23" }
}}
>
{app.authToken && true && app.authToken !== "" ? (
<>
<RootStack.Screen name="Home" component={TabStack} />
<RootStack.Screen
options={{
headerShown: false,
headerBackTitleVisible: false,
title: "Chat",
headerLargeTitle: false
}}
name="Message"
component={Message}
/>
<RootStack.Screen
options={{
headerShown: false,
title: "User",
headerLargeTitle: false
}}
name="User"
component={User}
/>
</>
) : (
<RootStack.Screen name="Login" component={Login} />
)}
</Root.Navigator>
</NavigationContainer>
);
};
function arePropsEqual(prevProps, nextProps) {
return (
prevProps.app.authToken === nextProps.app.authToken &&
prevProps.app.unreadMessages === nextProps.app.unreadMessages
);
}
const AppWithNavigation = connect(state => ({ app: state }))(React.memo(AppStack, arePropsEqual));
export default AppWithNavigation;
Maybe it has something to do with react-redux? Can you check if disabling it makes the bug disappear?
I'll try that but I doubt that connecting a store has anything to do with that.
@WoLewicki as I thought, react-redux isnt the issue. The tabs get resetted on android while they work perfectly fine on iOS
Hmm ok, so can you provide a repo/snack with minimal code needed to spot the issue? I cannot work on the code you provided higher since it's too big and has too many dependencies.
@WoLewicki I've finally found out the reason. It ONLY happens on
import { createMaterialBottomTabNavigator } from "@react-navigation/material-bottom-tabs";
and not on
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
Now I have to find out why and prob. report this to react-navigation.
Can you check if the problem exists with stack, not native-stack navigator?
@WoLewicki good catch. createMaterialBottomTabNavigator with STACK works perfect (no resets on android), but breaks with native-stack.
It may be the same issue as the one fixed in #471. Could you try and check if applying this change fixes the issue?
My specific app is on Expo RN, I can't test your fix currently :/
But since you've already tested this and have an test-example you might want to run it against my configs?
import * as React from 'react';
import {View, Button} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createMaterialBottomTabNavigator} from '@react-navigation/material-bottom-tabs';
import {enableScreens} from 'react-native-screens';
import {createNativeStackNavigator} from 'react-native-screens/native-stack';
enableScreens();
function HomeScreen({navigation, route}) {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Button title="Edit" onPress={() => navigation.navigate('Edit')} />
</View>
);
}
function EditScreen({navigation, route}) {
return (
<Button
title="+1"
onPress={() => {
navigation.navigate('Home');
}}
/>
);
}
const Tab = createMaterialBottomTabNavigator();
function HomeStack() {
const Stack = createNativeStackNavigator();
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Edit" component={EditScreen} />
</Stack.Navigator>
);
}
function EditStack() {
const Stack1 = createNativeStackNavigator();
return (
<Stack1.Navigator>
<Stack1.Screen name="Home" component={HomeScreen} />
<Stack1.Screen name="Edit" component={EditScreen} />
</Stack1.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="HomeStack" component={HomeStack} />
<Tab.Screen name="Edit" component={EditStack} />
</Tab.Navigator>
</NavigationContainer>
);
}
This works ok with the latest changes. Can you confirm that the issue exists with your configuration and such setup?
As I told you, I am on Expo SDK 37 (react-native-screens v.2.2.0) - so I can't confirm if your fix works on my setup. I need to eject, grab the update and confirm it.
Asking the other way around: does the bug occur also for you with 2.2.0 and material bar + native-stack and is it fixed for you with the master?
Ok I managed to recreate the issue. Will work on it.
Awesome! Thank you
@WoLewicki following the official specs at https://material.io/components/bottom-navigation#behavior, this looks like it is intended.
If so (which I am not sure about), it might have to be configurable. I still think that this "intention" is caused by a bug as it only happens on the native-stack with material-bottom-tabs.
Just posted this as an informative article.
It is a bug caused by tabs of material-bottom-tabs not being Screen components since react-native-screens is not used in that package. It causes ScreenStack of each native-stack navigator to have the same FragmentManager, which pops one Screen at each change of the tab in https://github.com/software-mansion/react-native-screens/blob/master/android/src/main/java/com/swmansion/rnscreens/ScreenStack.java#L34. I am trying to fix it now. The workaround, for now, is to set gestureEnabled to false on Android on each native-stack.
Oh I didn鈥檛 know about that. But I think none of the tabs are using Screen, right? Actually a loss of performance :-
Your suggested workaround works, which is pretty weird, since android don't have any gestures support anyway :D (at least they removed it on navigation v5 and native-stack never supported it for me on android afaik).

So I've added
gestureEnabled: Platform.OS === "android" ? false : true,
which fixed it for me and is sufficient enough. Thanks for your help here!
I have updated to the latest version of the expo (Expo SDK 37) and issue disappeared on Android (v10)
The issue started to appear on Expo SDK 38, the issue also exists in expo SDK39. Only happens on Android. Disabling enableScreens for now
@kamote could you please provide a snack/repo that shows the bug?
@WoLewicki here's the repo https://github.com/kamote/navigation-v5-screens-bug/blob/master/App.js#L124-L129
The issue kind of weird on my side and the issue only happening on expo standalone Android and when enableScreens
Like I said in my Previous comment this issue started on Expo SDK 38
I got it working by removing the View wrapper with the style of flex:1
so from
<View
style={{
flex: 1,
backgroundColor: 'yellow'
}}
>
<PaperProvider theme={theme}>
{Platform.OS === 'ios' && (
<StatusBar barStyle={theme.dark ? 'light-content' : 'dark-content'} />
)}
<NavigationContainer>
<MainApp></MainApp>
</NavigationContainer>
</PaperProvider>
</View>
to
<PaperProvider theme={theme}>
{Platform.OS === 'ios' && (
<StatusBar barStyle={theme.dark ? 'light-content' : 'dark-content'} />
)}
<NavigationContainer>
<MainApp></MainApp>
</NavigationContainer>
</PaperProvider>
I opened your repo but I don't know how to check that resetting behavior? What are the steps to reproduce the issue and what is the issue exactly?