React Native集成material-top-tabs报错:已用NavigationContainer仍提示未注册导航器
在React Native小组项目中,想要实现符合设计要求的可定制顶部标签栏,使用@react-navigation/native和@react-navigation/material-top-tabs时,始终出现如下报错:
Uncaught Error: Couldn't register the navigator. Have you wrapped your app with 'NavigationContainer'?
尽管已经在./app/index.tsx中用NavigationContainer包裹了代码,以下是相关代码:
标签栏_layout文件代码
import { Tabs, withLayoutContext } from "expo-router"; import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs' const TopTabs = withLayoutContext(createMaterialTopTabNavigator().Navigator); export default function MapNavigator() { return <TopTabs />; }
./app/index.tsx文件代码
/** @format */ import React, { useState } from "react"; import { Text, View, StatusBar, Pressable, FlatList, Image, useWindowDimensions, } from "react-native"; import { LinearGradient } from "expo-linear-gradient"; import Bg_1 from "../../assets/images/GetStartedBackgroundImages/getStartedBg(1).png"; import Bg_2 from "../../assets/images/GetStartedBackgroundImages/getStartedBg(2).png"; import Bg_3 from "../../assets/images/GetStartedBackgroundImages/getStartedBg(3).png"; import Bg_4 from "../../assets/images/GetStartedBackgroundImages/getStartedBg(4).png"; import { Link } from "expo-router"; import { NavigationContainer } from "@react-navigation/native"; //Mock Data const Slides = [ { id: 1, topic: "Grow More With Less", subTopic: "Increase your farm yields and profit with minimal effort smart farming solutions and best practices.", image: Bg_1, }, { id: 2, topic: "Get Alerts, Stay Safe!", subTopic: "Receive alerts and act quickly to prevent risks as they arise.", image: Bg_2, }, { id: 3, topic: "Get Alart, Shine Ya Eyes", subTopic: "We go give you quick update so that you go fit comot before yawa gas.", image: Bg_3, }, { id: 4, topic: "Small Power, Boku Harvest!!!", subTopic: "No too stress yourself, you don jam beta solution wey go give you money yanfu yanfu", image: Bg_4, }, ]; export default function Index() { const [compareMappedItemsIndex, setCompareMappedItemsIndex] = useState(0); const { width } = useWindowDimensions(); const Slide = ({ item }: any) => { return ( <View> <Image source={item.image} className="w-[100vw] h-[100vh]" resizeMode="cover" /> <LinearGradient colors={["rgba(0,44,17,0)", "rgba(0,44,17,7)", "rgba(0,44,17,1)"]} className="absolute bottom-0 w-full h-[300px]" > <View className="w-[100%] absolute bottom-[40px] h-[204px]"> <View className="mb-[18px]"> <Text className="text-white mb-[10px] font-semibold m-auto text-center text-[24px]"> {item.topic} </Text> <Text className="text-[14.22px] leading-[21.3px] text-center tracking-wide m-auto text-white w-[304.89]"> {item.subTopic} </Text> </View> </View> </LinearGradient> </View> ); }; const SlideFooter = () => { return ( <View className="absolute bottom-[5px] w-full pb-5"> <View className="gap-2 flex-row justify-center mb-[30px]"> {Slides.map((_, index) => ( <View key={index} className={`${ compareMappedItemsIndex == index && "bg-white" } w-[8px] bg-gray-400 rounded-full h-[8px]`} /> ))} </View> <Link href={'/Find Agents'} asChild> <Pressable className="w-[304.89px] m-auto px-[102px] py-[16px] rounded-[40px] bg-[#FFFCFC]"> <Text className="text-[#005921] text-center">Get Started</Text> </Pressable> </Link> </View> ); }; const getSlideIndex = (e: any) => { const contentOffset_X = e.nativeEvent.contentOffset.x; const presentSlideIndex = Math.round(contentOffset_X / width); setCompareMappedItemsIndex(presentSlideIndex); }; return ( <NavigationContainer> <View className="relative flex-1"> <FlatList pagingEnabled onMomentumScrollEnd={getSlideIndex} data={Slides} showsHorizontalScrollIndicator={false} renderItem={Slide} horizontal /> <SlideFooter /> <StatusBar backgroundColor="transparent" barStyle="light-content" /> </View> </NavigationContainer> ); }
核心问题
你混用了expo-router的导航系统和原生react-navigation的导航器,且expo-router布局文件写法不符合规范,导致导航器无法正确注册。
解决步骤
移除多余的
NavigationContainerexpo-router框架会自动处理导航容器,无需手动包裹。直接从index.tsx中删除import { NavigationContainer } from "@react-navigation/native";及对应的包裹代码。修改后的
index.tsx返回部分:return ( <View className="relative flex-1"> <FlatList pagingEnabled onMomentumScrollEnd={getSlideIndex} data={Slides} showsHorizontalScrollIndicator={false} renderItem={Slide} horizontal /> <SlideFooter /> <StatusBar backgroundColor="transparent" barStyle="light-content" /> </View> );用
expo-router的Tabs组件实现顶部标签栏expo-router的Tabs组件已集成顶部标签栏能力,无需手动调用createMaterialTopTabNavigator。修改_layout.tsx文件如下:import { Tabs } from "expo-router"; import { MaterialIcons } from "@expo/vector-icons"; // 可选,用于自定义图标 export default function MapNavigator() { return ( <Tabs screenOptions={{ tabBarPosition: "top", tabBarStyle: { backgroundColor: "#fff", elevation: 2, }, tabBarLabelStyle: { fontSize: 14, fontWeight: "500", }, tabBarIndicatorStyle: { backgroundColor: "#005921", height: 3, }, }} > {/* 对应./app下的页面文件 */} <Tabs.Screen name="page1" options={{ title: "标签1", tabBarIcon: ({ color, size }) => ( <MaterialIcons name="home" size={size} color={color} /> ), }} /> <Tabs.Screen name="page2" options={{ title: "标签2", tabBarIcon: ({ color, size }) => ( <MaterialIcons name="search" size={size} color={color} /> ), }} /> </Tabs> ); }确保页面文件存在
需在./app目录下创建与Tabs.Screen中name属性对应的页面文件(如page1.tsx、page2.tsx),否则路由无法匹配页面。
高度定制标签栏方案
如果需要完全自定义顶部标签栏,可通过tabBar属性传入自定义组件:
import { View, Text, Pressable } from "react-native"; const CustomTopTabBar = ({ state, descriptors, navigation }) => { return ( <View style={{ flexDirection: "row", backgroundColor: "#fff", padding: 10 }}> {state.routes.map((route, index) => { const { options } = descriptors[route.key]; const label = options.title || route.name; const isFocused = state.index === index; const onPress = () => { const event = navigation.emit({ type: "tabPress", target: route.key, canPreventDefault: true, }); if (!isFocused && !event.defaultPrevented) { navigation.navigate(route.name); } }; return ( <Pressable key={route.key} onPress={onPress} style={{ flex: 1, alignItems: "center", paddingVertical: 8, borderBottomWidth: isFocused ? 2 : 0, borderBottomColor: "#005921", }} > <Text style={{ color: isFocused ? "#005921" : "#666" }}> {label} </Text> </Pressable> ); })} </View> ); }; export default function MapNavigator() { return ( <Tabs screenOptions={{ tabBar: (props) => <CustomTopTabBar {...props} />, }} > <Tabs.Screen name="page1" options={{ title: "标签1" }} /> <Tabs.Screen name="page2" options={{ title: "标签2" }} /> </Tabs> ); }
内容的提问来源于stack exchange,提问作者Adebayo Nurudeen

