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.tsx
export default function RootLayout() {
  return (
    <Stack>
      <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      <Stack.Screen name="+not-found" />
    </Stack>
  );
}
app/(tabs)/_layout.tsx
export 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.tsx
export default function HomeLayout() {
  return (
    <Stack>
      <Stack.Screen
        name="index"
        options={{
          headerShown: true,
          headerLargeTitle: true,
          headerTitle: "Home",
        }}
      />
    </Stack>
  );
}
app/(tabs)/index/index.tsx
import { 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.tsx
export default function SettingsLayout() {
  return (
    <Stack>
      <Stack.Screen
        name="index"
        options={{
          headerShown: true,
          headerLargeTitle: true,
          headerTitle: "Settings",
        }}
      />
    </Stack>
  );
}
app/(tabs)/settings/index.tsx
import { 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