You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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布局文件写法不符合规范,导致导航器无法正确注册。

解决步骤

  1. 移除多余的NavigationContainer
    expo-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>
    );
    
  2. 用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>
      );
    }
    
  3. 确保页面文件存在
    需在./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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 17:54:53