ios - Expo Router v3 Fliknavigering med stack, sökning och stor titel
Jag försöker efterlikna Apple Contacts navigeringshuvud med sökfunktion och stor titel, men endast på hemskärmen. Alla andra flikar bör ha sina egna inställningar, såsom ingen annan titel eller ibland till och med dold navigeringsfält. Jag tror att jag har fått det till en stabil grund, men inställningarna för sökningen och stor titel har ingen effekt. Det reproducerbara repositoriet finns här.
Generellt är min struktur en ingångspunkt som pekar på /apps/_layout.tsx:
import { Stack } from 'expo-router';
export default function Layout() {
return (
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="(tabs)" />
</Stack>
);
}
Rotlayouten pekar på den första stacken som är en tabbvy som innehåller huvudappen, den finns på /apps/(tabs)/_layout.tsx och är som följer:
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>
);
}
Den första fliken är indexsidan på /apps/(tabs)/index.tsx och är som följer:
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>
);
}
Detta gav mig en bra, naturlig grund där innehållet sträcker sig under rubriken och bottenflikarna och innehållet scrollas under det.
Det fungerar till och med i liggande läge vilket är ett gott tecken:
Nu försöker jag göra titeln stor och lägga till en sökfält i navigeringsfältet som i Apple Contacts-appen:
Jag försökte lägga till följande i olika områden men det har ingen effekt:
headerLargeTitle: true,
headerSearchBarOptions: {
placeholder: 'Search'
}
Dokumentationen är ganska tunn, och exemplen var föråldrade eller använde inte den filbaserade Expo Router v3. Den har avvikit från React Navigation, så dokumentationen där är inte i linje med användningen av Expo Router. Har jag min struktur korrekt eller vad gör jag fel? All hjälp eller insikt skulle uppskattas!
Svar #1
@TruMan1:s förslag är korrekt. Det fungerade för mig. Här är ett exempel på en implementation som använder Expos filbaserade routing.
Mappstruktur
app
├── (tabs)
│ ├── _layout.tsx
│ ├── index
│ │ ├── _layout.tsx
│ │ └── index.tsx
│ └── settings
│ ├── _layout.tsx
│ └── index.tsx
├── +not-found.tsx
└── _layout.tsx
Filer
app/_layout.tsxexport default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
</Stack>
);
}
app/(tabs)/_layout.tsxexport default function TabLayout() {
return (
<Tabs
screenOptions={{
headerShown: false,
tabBarStyle: { position: "absolute", elevation: 0 },
tabBarBackground: () => (
<BlurView
intensity={80}
tint={"prominent"}
style={StyleSheet.absoluteFill}
/>
),
}}
>
<Tabs.Screen name="index" options={{ title: "Home" }} />
<Tabs.Screen name="settings" options={{ title: "Settings" }} />
</Tabs>
);
}
app/(tabs)/index/_layout.tsxexport default function HomeLayout() {
return (
<Stack>
<Stack.Screen
name="index"
options={{
headerShown: true,
headerLargeTitle: true,
headerTitle: "Home",
}}
/>
</Stack>
);
}
app/(tabs)/index/index.tsximport { useBottomTabBarHeight } from "@react-navigation/bottom-tabs";
export default function HomeScreen({ children }: Props) {
const bottomTabHeight = useBottomTabBarHeight();
return (
<ScrollView contentInsetAdjustmentBehavior="automatic">
<View style={{ paddingBottom: bottomTabHeight }}>{children}</View>
</ScrollView>
);
}
app/(tabs)/settings/_layout.tsxexport default function SettingsLayout() {
return (
<Stack>
<Stack.Screen
name="index"
options={{
headerShown: true,
headerLargeTitle: true,
headerTitle: "Settings",
}}
/>
</Stack>
);
}
app/(tabs)/settings/index.tsximport { useBottomTabBarHeight } from "@react-navigation/bottom-tabs";
export default function SettingsScreen({ children }: Props) {
const bottomTabHeight = useBottomTabBarHeight();
return (
<ScrollView contentInsetAdjustmentBehavior="automatic">
<View style={{ paddingBottom: bottomTabHeight }}>{children}</View>
</ScrollView>
);
}
Svar #2
Expo-router verkar begränsad eftersom den är ny - åtminstone är dokumentationen ofullständig beträffande den. För att korrekt uppnå det du försöker göra behöver du vända strukturen så att stackarna finns i fliken istället för en stack med en flik i:
<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>
);
}
Mer information kan hittas direkt i React Navigations dokumentation, som är en delmängd av Expo Router: https://reactnavigation.org/docs/tab-based-navigation#a-stack-navigator-for-each-tab


