364 lines
9.5 KiB
Markdown
364 lines
9.5 KiB
Markdown
# Navigation Setup
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
yarn add @react-navigation/native @react-navigation/stack @react-navigation/bottom-tabs
|
|
yarn add react-native-screens react-native-safe-area-context react-native-gesture-handler
|
|
```
|
|
|
|
## iOS Setup
|
|
|
|
```bash
|
|
cd ios && pod install
|
|
```
|
|
|
|
## Navigation Types
|
|
|
|
```typescript
|
|
// src/navigation/types.ts
|
|
export type RootStackParamList = {
|
|
Auth: undefined;
|
|
Main: undefined;
|
|
Splash: undefined;
|
|
};
|
|
|
|
export type AuthStackParamList = {
|
|
Login: undefined;
|
|
Register: undefined;
|
|
ForgotPassword: undefined;
|
|
OTPVerification: {email: string};
|
|
};
|
|
|
|
export type MainTabParamList = {
|
|
Home: undefined;
|
|
Profile: undefined;
|
|
Settings: undefined;
|
|
Notifications: undefined;
|
|
};
|
|
|
|
export type HomeStackParamList = {
|
|
HomeScreen: undefined;
|
|
Details: {id: string};
|
|
Search: undefined;
|
|
};
|
|
```
|
|
|
|
## Root Navigator
|
|
|
|
```typescript
|
|
// src/navigation/RootNavigator.tsx
|
|
import React from 'react';
|
|
import {NavigationContainer} from '@react-navigation/native';
|
|
import {createStackNavigator} from '@react-navigation/stack';
|
|
import {useAppSelector} from '@redux/hooks';
|
|
import {selectIsAuthenticated} from '@redux/selectors/authSelectors';
|
|
|
|
import AuthNavigator from './AuthNavigator';
|
|
import MainNavigator from './MainNavigator';
|
|
import SplashScreen from '@screens/SplashScreen';
|
|
import {RootStackParamList} from './types';
|
|
|
|
const Stack = createStackNavigator<RootStackParamList>();
|
|
|
|
export const RootNavigator: React.FC = () => {
|
|
const isAuthenticated = useAppSelector(selectIsAuthenticated);
|
|
const [isLoading, setIsLoading] = React.useState(true);
|
|
|
|
React.useEffect(() => {
|
|
// Simulate app initialization
|
|
const timer = setTimeout(() => {
|
|
setIsLoading(false);
|
|
}, 2000);
|
|
|
|
return () => clearTimeout(timer);
|
|
}, []);
|
|
|
|
return (
|
|
<NavigationContainer>
|
|
<Stack.Navigator screenOptions={{headerShown: false}}>
|
|
{isLoading ? (
|
|
<Stack.Screen name="Splash" component={SplashScreen} />
|
|
) : isAuthenticated ? (
|
|
<Stack.Screen name="Main" component={MainNavigator} />
|
|
) : (
|
|
<Stack.Screen name="Auth" component={AuthNavigator} />
|
|
)}
|
|
</Stack.Navigator>
|
|
</NavigationContainer>
|
|
);
|
|
};
|
|
```
|
|
|
|
## Auth Navigator
|
|
|
|
```typescript
|
|
// src/navigation/AuthNavigator.tsx
|
|
import React from 'react';
|
|
import {createStackNavigator} from '@react-navigation/stack';
|
|
import {AuthStackParamList} from './types';
|
|
|
|
import LoginScreen from '@screens/Auth/LoginScreen';
|
|
import RegisterScreen from '@screens/Auth/RegisterScreen';
|
|
import ForgotPasswordScreen from '@screens/Auth/ForgotPasswordScreen';
|
|
import OTPVerificationScreen from '@screens/Auth/OTPVerificationScreen';
|
|
|
|
const Stack = createStackNavigator<AuthStackParamList>();
|
|
|
|
const AuthNavigator: React.FC = () => {
|
|
return (
|
|
<Stack.Navigator
|
|
initialRouteName="Login"
|
|
screenOptions={{
|
|
headerShown: false,
|
|
cardStyleInterpolator: ({current, layouts}) => {
|
|
return {
|
|
cardStyle: {
|
|
transform: [
|
|
{
|
|
translateX: current.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [layouts.screen.width, 0],
|
|
}),
|
|
},
|
|
],
|
|
},
|
|
};
|
|
},
|
|
}}>
|
|
<Stack.Screen name="Login" component={LoginScreen} />
|
|
<Stack.Screen name="Register" component={RegisterScreen} />
|
|
<Stack.Screen name="ForgotPassword" component={ForgotPasswordScreen} />
|
|
<Stack.Screen name="OTPVerification" component={OTPVerificationScreen} />
|
|
</Stack.Navigator>
|
|
);
|
|
};
|
|
|
|
export default AuthNavigator;
|
|
```
|
|
|
|
## Main Navigator (Bottom Tabs)
|
|
|
|
```typescript
|
|
// src/navigation/MainNavigator.tsx
|
|
import React from 'react';
|
|
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
|
|
import {MainTabParamList} from './types';
|
|
import {CustomIcon} from '@components/common/CustomIcon';
|
|
import {useTheme} from '@providers/ThemeProvider';
|
|
|
|
import HomeNavigator from './HomeNavigator';
|
|
import ProfileScreen from '@screens/Profile/ProfileScreen';
|
|
import SettingsScreen from '@screens/Settings/SettingsScreen';
|
|
import NotificationsScreen from '@screens/Notifications/NotificationsScreen';
|
|
|
|
const Tab = createBottomTabNavigator<MainTabParamList>();
|
|
|
|
const MainNavigator: React.FC = () => {
|
|
const {colors} = useTheme();
|
|
|
|
return (
|
|
<Tab.Navigator
|
|
screenOptions={({route}) => ({
|
|
headerShown: false,
|
|
tabBarIcon: ({focused, color, size}) => {
|
|
let iconName: string;
|
|
|
|
switch (route.name) {
|
|
case 'Home':
|
|
iconName = 'home';
|
|
break;
|
|
case 'Profile':
|
|
iconName = 'user';
|
|
break;
|
|
case 'Settings':
|
|
iconName = 'settings';
|
|
break;
|
|
case 'Notifications':
|
|
iconName = 'bell';
|
|
break;
|
|
default:
|
|
iconName = 'home';
|
|
}
|
|
|
|
return <CustomIcon name={iconName} size={size} color={color} />;
|
|
},
|
|
tabBarActiveTintColor: colors.primary[500],
|
|
tabBarInactiveTintColor: colors.neutral.gray500,
|
|
tabBarStyle: {
|
|
backgroundColor: colors.neutral.white,
|
|
borderTopColor: colors.neutral.gray200,
|
|
paddingBottom: 5,
|
|
height: 60,
|
|
},
|
|
tabBarLabelStyle: {
|
|
fontSize: 12,
|
|
fontWeight: '500',
|
|
},
|
|
})}>
|
|
<Tab.Screen name="Home" component={HomeNavigator} />
|
|
<Tab.Screen name="Profile" component={ProfileScreen} />
|
|
<Tab.Screen name="Settings" component={SettingsScreen} />
|
|
<Tab.Screen name="Notifications" component={NotificationsScreen} />
|
|
</Tab.Navigator>
|
|
);
|
|
};
|
|
|
|
export default MainNavigator;
|
|
```
|
|
|
|
## Home Stack Navigator
|
|
|
|
```typescript
|
|
// src/navigation/HomeNavigator.tsx
|
|
import React from 'react';
|
|
import {createStackNavigator} from '@react-navigation/stack';
|
|
import {HomeStackParamList} from './types';
|
|
|
|
import HomeScreen from '@screens/Home/HomeScreen';
|
|
import DetailsScreen from '@screens/Home/DetailsScreen';
|
|
import SearchScreen from '@screens/Home/SearchScreen';
|
|
|
|
const Stack = createStackNavigator<HomeStackParamList>();
|
|
|
|
const HomeNavigator: React.FC = () => {
|
|
return (
|
|
<Stack.Navigator
|
|
initialRouteName="HomeScreen"
|
|
screenOptions={{
|
|
headerShown: false,
|
|
}}>
|
|
<Stack.Screen name="HomeScreen" component={HomeScreen} />
|
|
<Stack.Screen name="Details" component={DetailsScreen} />
|
|
<Stack.Screen name="Search" component={SearchScreen} />
|
|
</Stack.Navigator>
|
|
);
|
|
};
|
|
|
|
export default HomeNavigator;
|
|
```
|
|
|
|
## Navigation Service
|
|
|
|
```typescript
|
|
// src/navigation/NavigationService.ts
|
|
import {createNavigationContainerRef, StackActions} from '@react-navigation/native';
|
|
|
|
export const navigationRef = createNavigationContainerRef();
|
|
|
|
export function navigate(name: string, params?: any) {
|
|
if (navigationRef.isReady()) {
|
|
navigationRef.navigate(name as never, params as never);
|
|
}
|
|
}
|
|
|
|
export function goBack() {
|
|
if (navigationRef.isReady()) {
|
|
navigationRef.goBack();
|
|
}
|
|
}
|
|
|
|
export function reset(routeName: string) {
|
|
if (navigationRef.isReady()) {
|
|
navigationRef.reset({
|
|
index: 0,
|
|
routes: [{name: routeName}],
|
|
});
|
|
}
|
|
}
|
|
|
|
export function push(name: string, params?: any) {
|
|
if (navigationRef.isReady()) {
|
|
navigationRef.dispatch(StackActions.push(name, params));
|
|
}
|
|
}
|
|
|
|
export function replace(name: string, params?: any) {
|
|
if (navigationRef.isReady()) {
|
|
navigationRef.dispatch(StackActions.replace(name, params));
|
|
}
|
|
}
|
|
```
|
|
|
|
## Navigation Hooks
|
|
|
|
```typescript
|
|
// src/hooks/useNavigation.ts
|
|
import {useNavigation as useRNNavigation} from '@react-navigation/native';
|
|
import {StackNavigationProp} from '@react-navigation/stack';
|
|
import {RootStackParamList} from '@navigation/types';
|
|
|
|
export type NavigationProp = StackNavigationProp<RootStackParamList>;
|
|
|
|
export const useNavigation = () => {
|
|
return useRNNavigation<NavigationProp>();
|
|
};
|
|
```
|
|
|
|
## Deep Linking
|
|
|
|
```typescript
|
|
// src/navigation/LinkingConfiguration.ts
|
|
import {LinkingOptions} from '@react-navigation/native';
|
|
import {RootStackParamList} from './types';
|
|
|
|
const linking: LinkingOptions<RootStackParamList> = {
|
|
prefixes: ['myapp://'],
|
|
config: {
|
|
screens: {
|
|
Auth: {
|
|
screens: {
|
|
Login: 'login',
|
|
Register: 'register',
|
|
ForgotPassword: 'forgot-password',
|
|
OTPVerification: 'otp-verification',
|
|
},
|
|
},
|
|
Main: {
|
|
screens: {
|
|
Home: {
|
|
screens: {
|
|
HomeScreen: 'home',
|
|
Details: 'details/:id',
|
|
Search: 'search',
|
|
},
|
|
},
|
|
Profile: 'profile',
|
|
Settings: 'settings',
|
|
Notifications: 'notifications',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
export default linking;
|
|
```
|
|
|
|
## Navigation Container Setup
|
|
|
|
```typescript
|
|
// App.tsx (updated)
|
|
import React from 'react';
|
|
import {NavigationContainer} from '@react-navigation/native';
|
|
import {Provider} from 'react-redux';
|
|
import {PersistGate} from 'redux-persist/integration/react';
|
|
import {store, persistor} from './src/redux/store';
|
|
import {RootNavigator} from './src/navigation/RootNavigator';
|
|
import {navigationRef} from './src/navigation/NavigationService';
|
|
import linking from './src/navigation/LinkingConfiguration';
|
|
|
|
const App: React.FC = () => {
|
|
return (
|
|
<Provider store={store}>
|
|
<PersistGate loading={null} persistor={persistor}>
|
|
<NavigationContainer ref={navigationRef} linking={linking}>
|
|
<RootNavigator />
|
|
</NavigationContainer>
|
|
</PersistGate>
|
|
</Provider>
|
|
);
|
|
};
|
|
|
|
export default App;
|
|
``` |