Issue
I'm trying to mimic the Apple Contacts navigation header with search with large title but only on the Home Screen. All other tabs should have their own settings, such as no different title or sometimes even hidden navigation bar. I got it to a solid foundation I think, but the search and large title settings aren't having any effect. The reproducible repo is here.
Generally my structure is an entry point that points to /apps/_layout.tsx
:
import { Stack } from 'expo-router';
export default function Layout() {
return (
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="(tabs)" />
</Stack>
);
}
The root layout points to the first stack which is a tab view that houses the main app, it is at /apps/(tabs)/_layout.tsx
and as follows:
import { BlurView } from 'expo-blur';
import { Tabs } from 'expo-router';
import { SafeAreaProvider } from 'react-native-safe-area-context';
export default function TabsLayout() {
return (
<SafeAreaProvider>
<Tabs
screenOptions={{
tabBarStyle: { position: 'absolute', elevation: 0 },
tabBarBackground: () =>
<BlurView tint="prominent" intensity={100} style={StyleSheet.absoluteFill} />
}}>
<Tabs.Screen name="index" />
<Tabs.Screen name="tab2" />
</Tabs>
</SafeAreaProvider>
);
}
The first tab is the index page at /apps/(tabs)/index.tsx
and as follows:
import { BottomTabBarHeightContext, useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
import { useHeaderHeight } from '@react-navigation/elements';
import { BlurView } from 'expo-blur';
import { Stack } from 'expo-router';
import { ScrollView } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Text, StyleSheet } from 'react-native';
export default function Home() {
const items = Array.from({ length: 60 }, (_, index) => index);
const headerHeight = useHeaderHeight();
const bottomTabBarHeight = useBottomTabBarHeight();
return (
<ScrollView style={{ flex: 1, backgroundColor: 'blue' }}>
<Stack.Screen
options={{
headerShown: true,
headerTransparent: true,
headerBackground: () =>
<BlurView tint="prominent" intensity={100} style={StyleSheet.absoluteFill} />
}}
/>
<SafeAreaView
edges={['left', 'right']}
style={{
flex: 1,
backgroundColor: 'green',
paddingTop: headerHeight,
paddingBottom: bottomTabBarHeight
}}
>
{items.map((item, index) => (
<Text key={index} style={{...}}>{`Item ${item}`}</Text>
))}
</SafeAreaView>
</ScrollView>
);
}
This got me to a good, native foundation where the content extends under the header and bottom tabs and the content scrolls under it.
It even works in landscape orientation which is a good sign:
Now I'm trying to make the title large and add a search bar in the nav bar like the Apple Contact app:
I tried adding the following in different areas but it has no effect:
headerLargeTitle: true,
headerSearchBarOptions: {
placeholder: 'Search'
}
The documentation is pretty light, and examples were outdated or not using the file-based Expo Router v3. It is has diverged from React Navigation so the docs there are not aligned with Expo Router's usage. Do I have my structure correct or what am I doing wrong? Any help or insight would be appreciated!
Solution
The Expo Router seems limited since it's new - at the very least, the documentation is incomplete regarding it. To properly achieve what you're trying to do, you need to flip the structure so the stacks are in the tab instead of stack with a tab in it:
<SafeAreaProvider>
<NavigationContainer>
<Tab.Navigator
screenOptions={{
headerShown: false,
...(Platform.OS === 'ios'
? {
tabBarStyle: { position: 'absolute', elevation: 0 },
tabBarBackground: () => (
<BlurView tint="prominent" intensity={100} style={StyleSheet.absoluteFill} />
)
}
: undefined)
}}
>
<Tab.Screen name="HomeTab" component={HomeStack} />
<Tab.Screen name="SettingsTab" component={SettingsStack} />
<Tab.Screen name="MoreTab" component={MoreStack} />
</Tab.Navigator>
</NavigationContainer>
</SafeAreaProvider>
const Stack = createNativeStackNavigator();
export function Home({ navigation }: NativeStackScreenProps<ParamListBase>) {
const data = Array.from({ length: 50 });
useLayoutEffect(() => {
navigation.setOptions({
headerSearchBarOptions: {
onChangeText: (text) => console.log(text)
}
});
}, [navigation]);
return (
<View style={{ flex: 1, backgroundColor: 'yellow' }}>
<Text>Home screen</Text>
<Button title="Go to Details" onPress={() => navigation.navigate('Details')} />
{data.map((_, index) => (
<Text key={index} style={{ padding: 10, fontSize: 18, fontWeight: 'bold', color: 'blue' }}>
Item {index + 1}
</Text>
))}
</View>
);
}
export function HomeStack() {
return (
<Stack.Navigator
screenOptions={{
headerTransparent: Platform.OS === 'ios',
headerBlurEffect: 'systemThickMaterial'
}}
>
<Stack.Screen name="Home" component={withScrollStackView(Home)} options={{ headerLargeTitle: true }} />
<Stack.Screen name="Details" component={withScrollStackView(Details)} />
</Stack.Navigator>
);
}
export default function Details() {
return (
<View style={{ flex: 1, backgroundColor: 'yellow' }}>
<Text>Details screen</Text>
</View>
);
}
More details can be found directly on the React Navigation documentation, which is a subset of Expo Router: https://reactnavigation.org/docs/tab-based-navigation#a-stack-navigator-for-each-tab
Answered By - TruMan1
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.