So what i want to achieve is a navigation bar that has a background color of blue and have a 60% opacity so that it can show the image behind it. I can only achieve this by making the header's position an absolute but it wont let me click the back button.
here is my code stacknavigator
const UnregisteredNavigator = () => {
return (
<Stack.Navigator
screenOptions={({ navigation,route }) => ({
headerStyle: {
height: 60
},
header: ({}) => {
return (
<ThemedTopNavigation navigation={navigation} route={route} />
);
},
})}
>
<Stack.Screen name="LandingScreen" component={LandingScreen} options={{headerShown:false}} />
<Stack.Screen name="Login" component={SignInScreen} />
</Stack.Navigator>
);
};
and here is my ThemedTopNavigation code
const TopNavigationBar = ({ navigation: { goBack },route,eva }) => {
const themeContext = React.useContext(ThemeContext);
console.log("styled", eva);
const [menuVisible, setMenuVisible] = React.useState(false);
const [checked, setChecked] = React.useState(false);
const onCheckedChange = (isChecked) => {
themeContext.toggleTheme()
setChecked(isChecked);
};
const backFunction = () => {
alert("back");
// goBack()
};
const renderBackAction = () => (
<TopNavigationAction style={{backgroundColor:"red",zIndex: 1, position:'relative'}} icon={BackIcon} onPress={backFunction}/>
);
return (
<Layout style={eva.style.navLayout}>
<TopNavigation
style={[eva.style.navContainer, styles]}
alignment='center'
title={()=>(<Button>CLICK</Button>)}
accessoryLeft={renderBackAction}
/>
</Layout>
);
};
I am using UI kitten. anyone can help me?
Related
when I tried to refactor the mobile app of 'Notes' from ‘JavaScript Everywhere’ book, a problem
The structure is like this:
RootNavigator(Stack.Navigator)
---AuthLoading(Screen)
---Auth(Stack.Navigator)
--SignIn(Screen)
--SignUp(Screen)
---App(Tab.Navigator)
--FeedStack(Stack.Navigator)
-FeedScreen(Screen)
-NoteScreen(Screen)
--MyNotesStack(Stack.Navigator)
-MyNotesScreen(Screen)
-NoteScreen(Screen)
--FavoritesStack(Stack.Navigator)
-FavoritesScreen(Screen)
-NoteScreen(Screen)
--SettingsStack(Stack.Navigator)
When a user login, the default tab is ‘Feed’ which goes to ‘FeedStack’, and FeedScreen list all the notes created by all the users, click one item of the notes, it goes to a NoteScreen, display the details of that ‘Note’, everything goes well.
When the user choose ‘MyNotes’ tab which goes to ‘MyNoteStack’, it list the notes created by the current user, click one of the ‘note’ item, it goes to NoteScreen, display the details of that ‘note’. However, now, the default focus of the Tab.Navigator is ‘Feed’, and when I click back button in the NoteScreen, it goes back to ‘FeedStack’ which displays all the notes. It is weird!
I can’t understand why I go to the NoteScreen from ‘MyNotes’, but back button leads it to ‘Feed’, how to fix this problem?
And the code is as follows.
In index.js (RootNavigator)
const Stack = createNativeStackNavigator();
const Tab = createBottomTabNavigator();
const AuthStack = createNativeStackNavigator();
const feedStack = createNativeStackNavigator();
const myNotesStack = createNativeStackNavigator();
const favoritesStack = createNativeStackNavigator();
const settingsStack = createNativeStackNavigator();
function FeedStack () {
return (
<feedStack.Navigator
screenOptions={
{headerShown:false}
}
>
<feedStack.Screen name="FeedScreen" component={FeedScreen} />
<feedStack.Screen name="NoteScreen" component={NoteScreen} options={{headerShown:true}}/>
</feedStack.Navigator>
);
}
function MyNotesStack () {
return (
<myNotesStack.Navigator
screenOptions={
{headerShown:false}
}
>
<myNotesStack.Screen name="MyNotesScreen" component={MyNotesScreen} />
<myNotesStack.Screen name="Note" component={NoteScreen} options={{headerShown:true}} />
</myNotesStack.Navigator>
);
}
function FavoritesStack () {
return (
<favoritesStack.Navigator
screenOptions={
{headerShown:false}
}
>
<favoritesStack.Screen name="FavoritesScreen" component={FavoritesScreen} />
<favoritesStack.Screen name="Note" component={NoteScreen} options={{headerShown:true}}/>
</favoritesStack.Navigator>
);
}
function SettingsStack () {
return (
<settingsStack.Navigator
screenOptions={
{headerShown:false}
}
>
<settingsStack.Screen name="SettingsScreen" component={SettingsScreen} />
</settingsStack.Navigator>
);
}
const TabNavigator = () => {
return (
<Tab.Navigator
initialRouteName="MyNotes"
activeColor='#f0f'
inactiveColor='#555'
barStyle={{
backgroundColor:'#999'
}}
screenOptions={({route}) => ({
headerShown: false,
tabBarIcon:({focused, size, color}) => {
let iconName;
if( route.name === 'FeedStack') {
iconName = 'home';
} else if (route.name === 'MyNotesStack') {
iconName = 'bed';
} else if (route.name === 'FavoritesStack') {
iconName = 'star'
} else {
iconName = 'spa'
}
color = focused ? '#f0f' : "#555";
size = focused ? 24 : 20;
return <FontAwesome5 name={iconName} size={size} color={color}/>;
},
})}
>
<Tab.Screen name='FeedStack' component={FeedStack} options={{headerShown: false}} />
<Tab.Screen name='MyNotesStack' component={MyNotesStack} options={{headerShown: false}} />
<Tab.Screen name='FavoritesStack' component={FavoritesStack} options={{headerShown: false}}/>
<Tab.Screen name='SettingsStack' component={SettingsStack} options={{headerShown: false}} />
</Tab.Navigator>
);
};
const Auth= () => {
return (
<AuthStack.Navigator
screenOptions={{headerShown:false}}
>
<AuthStack.Screen name='signIn' component={SignIn}></AuthStack.Screen>
</AuthStack.Navigator>
);
};
const RootNavigator = () => {
return (
<Stack.Navigator initialRouteName='AuthLoading'>
<Stack.Screen name='AuthLoading'
component={AuthLoading}
options={{title:'AuthLoading'}}
>
</Stack.Screen>
<Stack.Screen name='Auth'
component={Auth}
options={{
title: 'Auth',
headerStyle: {
backgroundColor: '#f4511e',
},
headerBackVisible: false,
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
>
</Stack.Screen>
<Stack.Screen name='App'
component={TabNavigator}
options={{
title: 'App',
headerStyle: { backgroundColor: '#f4511e'},
headerBackVisible:false,
headerTintColor: '#fff',
headerTitleStyle: {fontWeight: 'bold'},
}}
>
</Stack.Screen>
</Stack.Navigator>
);
};
export default RootNavigator;
In FeedScreen.js:
const FeedScreen = () => {
const { data, loading, error } = useQuery(GET_NOTES);
// if the data is loading, our app will display a loading indicator
if(loading)
return <Loading />;
if(error)
return <Text>Error loading notes.</Text>;
// if the query is successful and there are notes, return the feed of notes
return (
<NoteFeed notes={data.notes} />
);
};
In MyNotesScreen.js
const MyNotesScreen = () => {
const { data, loading, error } = useQuery(GET_MY_NOTES);
// if the data is loading, our app will display a loading indicator
if(loading)
return <Loading />;
if(error)
return <Text>Error loading MyNotes.</Text>;
// if the query is successful and there are notes, return the feed of notes
// else if the query is successful and there aren't notes, display a message
if(data.me.notes.length !== 0) {
return <NoteFeed notes={data.me.notes} />;
} else {
return <Text>No notes yet</Text>;
}
// If I don't use <NoteFeed> here, for example, show a button then go to <NoteScreen> it is ok.
// return (
// <View style={styles.container}>
// <Text>My Notes Screen</Text>
// use self-defined button
// <JereButton
// onPress={() => navigation.navigate('Note',{id:'63b94da5ccf7f90023169c3d'})}
// title="Go to a note"
// color={"#882"}
// />
// </View>
// );
};
In NoteFeed.js
const NoteFeed = props => {
// only screen components receive navigation prop automatically!
// if you wish to access the navigation prop in any of your components, you may use the useNavigation hook.
const navigation = useNavigation();
return (
<View style={styles.container}>
<FlatList
data={props.notes}
keyExtractor = {({id}) => id.toString()}
renderItem = {({item}) => (
<Pressable style={styles.feedview}
onPress={() => navigation.navigate('NoteScreen',{id: item.id})}
>
<Text style={styles.text}>{item.content}</Text>
</Pressable>
)}
/>
</View>
);
};
In NoteScreen.js
const NoteScreen = ({navigation, route}) => {
const {id} = route.params;
const { data, loading, error } = useQuery(GET_NOTE, {variables:{id}});
// if the data is loading, our app will display a loading indicator
if(loading)
return <Loading />;
if(error)
return <Text>Error Note not found.</Text>;
return (
<Note note={data.note} />
);
};
Thank you for your help.
I tried to replace useNavigation() to props solution, the issue is the same. Then I tried to do not use in 'MyNotes' to show the ‘note’, it is OK, but it doesn’t comply with the design.
I want to hide the tab bar on certain screens inside a nested stack navigator. I have a bottom tab bar, using a CUSTOM tab bar, set up like so:
const MainAppBottomTabs = createBottomTabNavigator<BottomTabParamList>();
const MainAppBottomTabsNavigator = (): JSX.Element => {
return (
<MainAppBottomTabs.Navigator
initialRouteName="ListScreen"
screenOptions={{
headerShown: false
}}
tabBar={(props: BottomTabBarProps) => (
<TabBar
state={props.state}
navigation={props.navigation}
descriptors={props.descriptors}
insets={props.insets}
/> // this is my custom tab bar
)}>
<MainAppBottomTabs.Screen
name="ListScreen"
component={ListScreen}
/>
<MainAppBottomTabs.Screen
name="ProductsScreen"
component={ProductsScreen}
/>
<MainAppBottomTabs.Screen
name="DetailsStackNavigator"
component={DetailsStackNavigator}
/>
</MainAppBottomTabs.Navigator>
);
};
Here's the nested DetailsStackNavigator:
const DetailsStack = createNativeStackNavigator();
const DetailsStackNavigator = (): JSX.Element => {
return (
<DetailsStack.Navigator
initialRouteName="UsersScreen"
screenOptions={{
headerShown: false
}}>
<DetailsStack.Screen
name="UsersScreen"
component={UsersScreen}
options={{ animation: 'none' }}
/>
<DetailsStack.Screen
name="OptionsScreen"
component={OptionsScreen}
options={{ animation: 'none' }}
/>
<DetailsStack.Screen name="OptionsDetailsScreen" component={OptionsDetailsScreen} />
<DetailsStack.Screen name="UsersDetailsScreen" component={UsersDetailsScreen} />
</DetailsStack.Navigator>
);
};
I want to hide the tab bar on the OptionsDetailsScreen and UsersDetailsScreen inside of the DetailsStackNavigator. I have tried the following which DOES hide the tab bar, but it cuts off the screens at the bottom where the tab bar would be:
const MainAppBottomTabs = createBottomTabNavigator<BottomTabParamList>();
const MainAppBottomTabsNavigator = (): JSX.Element => {
const getTabBarVisibility = (route) => {
const routeName = getFocusedRouteNameFromRoute(route);
const hideOnScreens = ['OptionsDetailsScreen', 'UsersDetailsScreen'];
return hideOnScreens.indexOf(routeName) <= -1 ? 'flex' : 'none';
};
return (
<MainAppBottomTabs.Navigator
// rest of code from above
<MainAppBottomTabs.Screen
name="DetailsStackNavigator"
component={DetailsStackNavigator}
options={({ route }) => ({
tabBarStyle: { display: getTabBarVisibility(route) }
})}
/>
</MainAppBottomTabs.Navigator>
);
};
and then in my custom tab bar:
const TabBar = (screenProps: ScreenProps): JSX.Element => {
const focusedOptions = screenProps.descriptors[screenProps.state.routes[screenProps.state.index].key].options;
if (focusedOptions?.tabBarStyle?.display === 'none') {
return null;
}
return <View>{RenderTabs(screenProps)}</View>;
};
Not sure what else to try. Seems to be way too difficult for what I would assume would be a pretty common scenario.
I want to create dynamic stack navigator but it return's empty.
My stack navigator is like that
const homeScreenStack = ({ navigation }) => {
const [UserType, SetUserType] = useState('');
useEffect(() => {
getUserType()
return () => { };
}, []);
const getUserType = async () => {
const user_type = await AsyncStorage.getItem('user_type');
SetUserType(user_type)
}
return (
<Stack.Navigator initialRouteName="Dashboard" screenOptions={{ unmountOnBlur: true }}>
<Stack.Screen ...
headerRight: () => (
<View>
{
UserType == "Teacher" &&
<View>
<Text>Teacher</Text>
</View>
}
</View>
/>
If condition doesn't work but if I remove condition and write just
<Text>{UserType} </Text>
Output is "Teacher" Then why my condition doesn't satisfy
The left gif shows the flickering of the screen when the Switch is pressed and the right image shows the screen at the moment when the flickering happens. That means that my Stack.Screen from #react-navigation/native-stack, that displays the Settings page gets removed for a small moment when the Switch is pressed, which results in the flicker effect.
The function that gets called when the Switch is pressed is my toggleLocalAuth function. This is an asynchronous function, so my guess would be that the screen flickers when the toggleLocalAuth is running.
const toggleLocalAuth = async () => {
await SecureStorage.save('local-auth', JSON.stringify(!usesLocalAuth))
setUsesLocalAuth((usesLocalAuth) => !usesLocalAuth)
}
This is my Security component which contains the Switch and the "Security" subheader.
const Security: React.FC = () => {
const theme = useTheme()
const { usesLocalAuth, toggleLocalAuth } = useAuth()
return (
<Box>
<Text variant="subheader" paddingHorizontal="m" paddingBottom="xs">
{i18n.t('settings.security')}
</Text>
<Paper>
<Box
flexDirection="row"
alignItems="center"
justifyContent="space-between"
>
<Box flexDirection="row">
<Ionicons
name="finger-print"
size={24}
color={theme.colors.icon}
style={{ paddingRight: theme.spacing.m }}
/>
<Text variant="subtitle">
{Platform.OS === 'ios' || Platform.OS === 'macos'
? i18n.t('settings.touchId')
: i18n.t('settings.fingerprint')}
</Text>
</Box>
<Switch value={usesLocalAuth} onValueChange={toggleLocalAuth} />
</Box>
</Paper>
</Box>
)
}
This is my whole screen that is shown in the image, which is a component of a StackNavigator Screen from #react-navigation/native-stack.
const Settings = ({
navigation,
}: {
navigation: NativeStackNavigationProp<SessionParamList, 'Settings'>
}) => {
const theme = useTheme()
return (
<ScrollView style={{ paddingVertical: theme.spacing.m }}>
<Profile
onPersonalPress={() => navigation.push('PersonalData')}
onResidencePress={() => navigation.push('Residence')}
onContactPress={() => navigation.push('ContactInformation')}
/>
<Preferences />
<Security />
</ScrollView>
)
}
export default Settings
This is the StackNavigator Screen which holds the Settings page.
const Stack = createNativeStackNavigator<SessionParamList>()
const Tab = createBottomTabNavigator()
function SettingsStack() {
return (
<Stack.Navigator>
<Stack.Screen
options={{
...stackOptions,
headerTitle: i18n.t('settings.settings'),
}}
name="Settings"
component={Settings}
/>
</Stack.Navigator>
)
}
My guess is that the Settings Stack.Screen gets removed for a small moment when the async toggleLocalAuth function is called. The background and Tabbar do not flicker because they are defined as screenOptions on my Tab.Navigator which sits above the Settings page in the component tree.
Edit:
I use useContext and createContext to gloablly manage the authentication state.
export type ContextType = {
isAuthenticated: boolean
usesLocalAuth: boolean
jwt: string
logout: () => void
login: (jwt: string) => void
toggleLocalAuth: () => void
}
const AuthContext = createContext<ContextType>({
isAuthenticated: false,
jwt: '',
usesLocalAuth: false,
logout: () => console.warn('Not Auth provider above component'),
login: () => console.warn('Not Auth provider above component'),
toggleLocalAuth: () => console.warn('Not Auth provider above component'),
})
export const useAuth = () => useContext(AuthContext)
These are the relevant excerpts of my App.tsx file:
export default function App() {
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false)
const [usesLocalAuth, setUsesLocalAuth] = useState<boolean>(false)
const [jwt, setJwt] = useState('')
function logout() {
setIsAuthenticated(false)
setDid(null)
}
function login(jwt: string) {
setIsAuthenticated(true)
setJwt(jwt)
}
const authProvider = useMemo(
() => ({
isAuthenticated: isAuthenticated,
jwt: jwt,
usesLocalAuth: usesLocalAuth,
logout: logout,
login: login,
toggleLocalAuth: toggleLocalAuth,
}),
[isAuthenticated, usesLocalAuth, did, jwt, logout, login, toggleLocalAuth],
...
return (
<AuthContext.Provider value={authProvider}>
...
</AuthContext.Provider>
)
)
I'm trying to use deep linking in my react native app for ios. I've been following this document by React Navigation.
I added what was needed to my AppDelegate.m and to Xcode:
However I'm confused on how to implement this in my App.js:
const AuthStack = createStackNavigator();
const AuthStackScreen = () => (
<AuthStack.Navigator >
<AuthStack.Screen
name="Ingresar"
component={Login}
options = {{
headerShown: false,
title: "Bienvenido"
}}/>
<AuthStack.Screen name="CrearCuenta"
component={Signup}
options={{title: "Crear cuenta"}}/>
</AuthStack.Navigator>
);
const Tabs = createBottomTabNavigator();
const HomeStack = createStackNavigator();
const DonationsStack = createStackNavigator();
const DonateStack = createStackNavigator();
const InstitutionsStack = createStackNavigator();
const ProfileStack = createStackNavigator();
const DonationsStackScreen = () => (
<DonationsStack.Navigator>
<DonationsStack.Screen name="Pedidos" component={DonationsScreen}/>
<DonationsStack.Screen name="Realizar donación" component={DonationFormScreen}/>
<DonationsStack.Screen name="Donación confirmada" component={DonationConfirmationScreen}
options = {{
headerShown: false
}}/>
</DonationsStack.Navigator>
);
// I omit some of the stack screens here.
const TabsScreen = () => (
<Tabs.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) =>
{
let iconName;
if (route.name === 'Home') {
iconName = focused ? 'home' : 'home-outline';
return <MaterialCommunityIcons name={iconName} size={size} color={color}/>;
} else if (route.name === 'Pedidos') {
iconName = focused ? 'ios-list-box' : 'ios-list';
}
else if (route.name === 'Donar') {
iconName = focused ? 'gift' : 'gift-outline';
return <MaterialCommunityIcons name={iconName} size={size} color={color}/>;
} else if (route.name === 'Perfil') {
iconName = focused ? 'user-alt'
: 'user';
return <FontAwesome5 name={iconName} size={size-4} color={color}/>;
} else if (route.name === 'Instituciones') {
iconName = 'md-business';
}
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
tabBarOptions={{
activeTintColor: '#013773',
inactiveTintColor: 'gray',
}}>
<Tabs.Screen name="Home" component={HomeStackScreen}/>
<Tabs.Screen name="Pedidos" component={DonationsStackScreen}/>
<Tabs.Screen name="Donar" component={DonateStackScreen}/>
<Tabs.Screen name="Instituciones" component={InstitutionsStackScreen}/>
<Tabs.Screen name="Perfil" component={ProfileStackScreen}/>
</Tabs.Navigator>
);
const RootStack = createStackNavigator();
const RootStackScreen = ({user}) => (
<RootStack.Navigator headerMode="none">
{user ? (
<RootStack.Screen
name="App"
component={TabsScreen}
options={{animationEnabled: false}}/>
) : (
<RootStack.Screen
name="Auth"
component={AuthStackScreen}
options={{animationEnabled: false}}/>
)}
</RootStack.Navigator>
);
export default function ponte() {
const [isLoading, setIsLoading] = React.useState(false);
const [user, setUser] = React.useState(null);
// Handle user state changes
function onAuthStateChanged(user) {
setUser(user);
if (isLoading) setIsLoading(false);
}
if (isLoading) return null;
useEffect(() => {
SplashScreen.hide();
const subscriber = auth().onAuthStateChanged(onAuthStateChanged);
return subscriber; // unsubscribe on unmount
}, []);
const authContext = React.useMemo(() => {
return {
signIn: (email, password) =>
auth()
.signInWithEmailAndPassword(email.email, password.password)
.then(res => {
setIsLoading(false);
setUser(res.user.uid);
})
,
signUp: () => {
setIsLoading(false);
setUser("test");
},
signOut: () => {
setIsLoading(false);
auth().signOut().then(() => console.log('User signed out!'));
setUser(null);
}
};
}, []);
/*
Here comes my deeplinking.
What I need to do is that when the user clicks on my URL
it goes to Pedidos --> Realizar donación with a parameter called request.
All of this happens inside the DonationsStack
*/
const deepLinking = {
prefixes: ['https://demoApp.com', 'demoApp://'],
config: {
Home: 'HomePath',
RealizarDonacion: {
path: 'Pedidos/Realizar donación/:request',
params: {
request: null,
}
}
} ,
};
return(
<AuthContext.Provider value={authContext}>
<NavigationContainer linking={deepLinking}>
<RootStackScreen user={user} />
</NavigationContainer>
</AuthContext.Provider>
);
}
As you see at the end I provide my deepLinking component to the NavigationContainer.
However when I type demoApp://Pedidos/Realizar donación/44 in Safari I don't get redirected to my app.
I'm not convinced about that space inside the path, however it doesn't work when I create a deepLink to Pedidos only either.
I need my URL to open my app and inside the DonationsStack navigate to Pedidos (DonationsScreen) --> Realizar donación (DonationFormScreen)
Update: My app opens but I don't get redirected to any screen.