Stack Navigator issue in a react-native expo app

1k views Asked by At

I have some sample code in an expo 38.0 react-native app that has a login, signup and forgotpwd screens. The navigation between these screens is controlled by a Stack Navigator (using react navigation 5.x) with login screen as the initial screen.

The stack navigator works as expected on my android phone, but there is a problem when Back button is pressed on ForgotPwdScreen component. The problem is that a moment after the login screen shows its contents everything on this screen suddenly moves down by a few pixels. I researched a lot for the last one day but nothing seemed to work. I have tried wrapping contents in SafeAreaView in login screen but it didn't help.

But if I include ForgotPwdScreen code in App.js then this problem is no more there. Only if ForgotPwdScreen is in its own separate file does this problem happen.

The full code sample is at the following snack: Working code for this issue

A video of this issue can be seen at Issue of stack navigator

This issue only happens on android phone and not on iphones.

Question : Why are the contents suddenly moving down in login screen after it has fully rendered when back button is pressed on ForgotPwdScreen but only when ForgotPwdScreen is in a separate js file?

Code files are as below.

package.json

{
  "dependencies": {
    "expo-status-bar": "^1.0.2",
    "react-native-screens": "~2.9.0",
    "@react-navigation/stack": "^5.9.1",
    "react-native-reanimated": "~1.9.0",
    "@react-navigation/drawer": "^5.9.1",
    "@react-navigation/native": "^5.7.4",
    "react-native-safe-area-context": "~3.0.7",
    "@react-native-community/masked-view": "0.1.10",
    "@react-native-community/async-storage": "~1.11.0"
  }
}

App.js

import React, { useState } from 'react';
import {
  StyleSheet,
  Text,
  View,
  TextInput,
  TouchableOpacity,
  KeyboardAvoidingView,
  ScrollView,
  StatusBar,
  Keyboard,
  ActivityIndicator,
  Platform,
  Image,
} from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import {
  createStackNavigator,
  TransitionSpecs,
  HeaderStyleInterpolators,
  CardStackStyleInterpolator,
  CardStyleInterpolators
} from '@react-navigation/stack';
    
import LoginScreen from './components/LoginScreen'
import ForgotPwdScreen from './components/ForgotPwdScreen'

function SignUpScreen () {
    /* Component state. Change someState, setEnteredSomeState and state object as per your scenario. */
    const [someState, setEnteredSomeState] = useState({
        state1: '',
        state2: '',
        state3: false,
    });
    return (
        <View style={styles.container}>
            {/* Set statusbar background color and style so it blends with the screen */}
            <StatusBar backgroundColor="#fff" barStyle="dark-content" />
            <Text style={styles.label}>SignUp  will show here</Text>
        </View>
    );
}

const Stack = createStackNavigator();

const App = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator
        initialRouteName="Login"
        screenOptions={{
          headerShown: false,
          headerTitleAlign: 'center',
          headerMode: 'screen',
        }}>
        <Stack.Screen
          name="Login"
          component={LoginScreen}
          options={{ headerShown: false }}
        />
        <Stack.Screen
          name="ForgotPwd"
          component={ForgotPwdScreen}
          options={{ headerShown: true }}
        />
        <Stack.Screen
          name="SignUp"
          component={SignUpScreen}
          options={{ headerShown: true }}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: '#fff',
  },
  scroll: {
    flexGrow: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  inputView: {
    width: '80%',
    backgroundColor: '#fff',
    height: 50,
    marginBottom: 20,
    justifyContent: 'center',
    alignSelf: 'center',
    textAlign: 'left',
    padding: 20,
    borderBottomColor: 'black',
    borderBottomWidth: 1,
    marginTop: -20,
  },
  inputText: {
    height: 50,
    color: 'grey',
  },
  loginBtn: {
    width: '70%',
    backgroundColor: '#F26722',
    height: 50,
    alignItems: 'center',
    justifyContent: 'center',
    alignSelf: 'center',
    marginTop: 40,
    marginBottom: 10,
    borderWidth: 1,
    borderColor: '#F26522',
  },
  loginText: {
    color: 'white',
  },
  forgotText: {
    color: '#337ab7',
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
    marginTop:20,
  },
  signupText: {
    color: '#337ab7',
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
  },
  forgotBtn: {
    height: 50,
    width:'100%',
    marginTop:10,
  },
  signupBtn: {
    height: 50,
    width:'100%',
    marginTop:20,
  },
  label: {
    textAlign: 'center',
  },
});

export default App;

ForgotPwdScreen.js

import { StatusBar } from 'expo-status-bar';
import React, { useState } from 'react';
import { StyleSheet, Text, View, Button, ScrollView} from 'react-native';

export default function ForgotPwdScreen() {
  return (
    <View style={styles.container}>
      
        {/* Set statusbar background color and style so it blends with the screen */}
        <StatusBar backgroundColor="#fff" barStyle="dark-content" />
        <Text style={styles.label}>ForgotPwd will show here</Text>
   
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  label: {
    textAlign: 'center',
  },
});

LoginScreen.js

import React, { useState } from 'react';
import {
  StyleSheet,
  Text,
  View,
  TextInput,
  TouchableOpacity,
  KeyboardAvoidingView,
  ScrollView,
  StatusBar,
  Keyboard,
  ActivityIndicator,
  Platform,
  Image,
} from 'react-native';
import AsyncStorage from '@react-native-community/async-storage';  


export default function LoginScreen({ route, navigation }) {
  return (
    <View style={styles.container}>
      <ScrollView
        contentContainerStyle={styles.scroll}
        keyboardShouldPersistTaps="always">
        <StatusBar backgroundColor="#fff" barStyle="dark-content" />

        <TouchableOpacity
          style={styles.forgotBtn}
          onPress={() => navigation.navigate('ForgotPwd')}>
          <Text style={styles.forgotText}>Forgot Password?</Text>
        </TouchableOpacity>

        <TouchableOpacity
          style={styles.signupBtn}
          onPress={() => navigation.navigate('SignUp')}>
          <Text style={styles.signupText}>Signup</Text>
        </TouchableOpacity>
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: '#fff',
  },
  scroll: {
    flexGrow: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  forgotText: {
    color: '#337ab7',
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
    marginTop: 20,
  },
  signupText: {
    color: '#337ab7',
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
  },
  forgotBtn: {
    height: 50,
    width: '100%',
    marginTop: 10,
  },
  signupBtn: {
    height: 50,
    width: '100%',
    marginTop: 20,
  },
});
1

There are 1 answers

0
Sunil On BEST ANSWER

At last after 2 days of trying, I found the reason for this odd behavior.

This issue was happening due to expo-status-bar package that was included in ForgotPwdScreen.js. When I removed the import for expo-status-bar and instead used the StatusBar component that comes in react-native then the issue disappeared.

So, clearly there is some issue with expo-status-bar. It's best to not use it and rather use the default StatusBar that comes with react-native.

Original imports in ForgotPwdScreen.js

import { StatusBar } from 'expo-status-bar';
import React, { useState } from 'react';
import { StyleSheet, Text, View, Button, ScrollView} from 'react-native';

Changed imports in ForgotPwdScreen.js that resolved the issue

import React, { useState } from 'react';
import { StyleSheet, Text, View, Button, ScrollView, StatusBar} from 'react-native';