在 React Native Expo 中配置 TabBar(底部 / 顶部标签栏),核心依赖 expo-router 的 Tabs 组件(推荐)或 React Navigation 的 createBottomTabNavigator,以下是 expo-router 主流方案(适配 iOS / 安卓、支持自定义样式 / 图标 / 交互)的完整配置指南:
一、核心前提
- 确保已安装依赖(Expo 项目默认集成,无需额外安装):
expo install expo-router @expo/vector-icons react-native-screens react-native-safe-area-context
- 基础文件结构(
expo-router推荐的路由分组)
app/
├── (tabs)/ # Tabs 分组(括号避免路由路径污染)
│ ├── _layout.tsx # Tabs 配置文件(核心)
│ ├── home.tsx # 首页 Tab
│ ├── explore.tsx # 发现 Tab
│ └── profile.tsx # 我的 Tab
├── _layout.tsx # 根布局(Stack 包裹 Tabs)
└── index.tsx # 入口页(可选,可重定向到 Tabs)
二、基础配置(默认样式)
1. Tabs 核心配置文件(app/(tabs)/_layout.tsx)
import { Tabs } from "expo-router";
import { Ionicons } from "@expo/vector-icons"; // 推荐使用 Expo 内置图标
import { Platform, StyleSheet, TouchableOpacity } from "react-native";
export default function TabsLayout() {
return (
<Tabs
// 全局 TabBar 配置(所有 Tab 共享)
screenOptions={{
// 1. 基础样式
tabBarStyle: styles.tabBar, // TabBar 容器样式
tabBarItemStyle: styles.tabItem, // 单个 Tab 项样式
tabBarLabelStyle: styles.tabLabel, // Tab 文字样式
tabBarIconStyle: styles.tabIcon, // Tab 图标样式
// 2. 交互/视觉
tabBarActiveTintColor: "#007AFF", // 选中态颜色(文字/图标)
tabBarInactiveTintColor: "#8E8E93", // 未选中态颜色
tabBarShowLabel: true, // 是否显示文字(默认 true)
tabBarAllowFontScaling: false, // 禁用字体缩放(安卓适配)
headerShown: false, // 隐藏每个 Tab 页的顶部导航栏(如需显示可单独开启)
// 安卓取消水波纹效果
tabBarButton: (props) => (
// @ts-ignore
<TouchableOpacity
activeOpacity={1}
style={[props.style, { backgroundColor: 'transparent' }]}
{...props}
/>
)
}}
>
{/* 首页 Tab */}
<Tabs.Screen
name="home" // 对应 app/(tabs)/home.tsx
options={{
title: "首页", // Tab 文字
// 自定义图标(根据选中状态切换)
tabBarIcon: ({ color, size }) => (
<Ionicons name="home-outline" color={color} size={size} />
),
// 单独覆盖当前 Tab 的样式(可选)
tabBarLabelStyle: { fontSize: 12 },
}}
/>
{/* 发现 Tab */}
<Tabs.Screen
name="explore"
options={{
title: "发现",
tabBarIcon: ({ color, size }) => (
<Ionicons name="compass-outline" color={color} size={size} />
),
// 可选:隐藏当前 Tab 的文字(仅显示图标)
// tabBarShowLabel: false,
}}
/>
{/* 我的 Tab */}
<Tabs.Screen
name="profile"
options={{
title: "我的",
tabBarIcon: ({ color, size }) => (
<Ionicons name="person-outline" color={color} size={size} />
),
// 可选:自定义选中态颜色(覆盖全局)
// tabBarActiveTintColor: "#FF3B30",
}}
/>
</Tabs>
);
}
// 样式封装(适配 iOS/安卓)
const styles = StyleSheet.create({
// TabBar 容器
tabBar: {
height: Platform.OS === "ios" ? 60 : 66, // 适配安卓高度
paddingVertical: 5, // 内容垂直居中
justifyContent: "center", // 子元素居中
borderTopWidth: 0, // 移除顶部边框(可选)
elevation: 0, // 移除安卓阴影(可选)
shadowOpacity: 0, // 移除 iOS 阴影(可选)
},
// 单个 Tab 项
tabItem: {
paddingVertical: 0, // 清空默认内边距(避免内容偏移)
justifyContent: "center", // 垂直居中
},
// Tab 文字
tabLabel: {
fontSize: 12,
marginTop: 2, // 图标与文字间距
fontWeight: "400",
},
// Tab 图标
tabIcon: {
size: 24,
},
});
2. 根布局包裹 Tabs(app/_layout.tsx)
import { Stack } from "expo-router";
export default function RootLayout() {
return (
<Stack>
{/* 包裹 Tabs 分组,隐藏根导航栏(如需显示可调整) */}
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
{/* 其他页面(如模态页、详情页) */}
<Stack.Screen name="detail/[id]" options={{ title: "详情页" }} />
</Stack>
);
}
三、进阶配置(自定义增强)
1. 自定义 TabBar 组件(完全自定义样式)
若默认样式无法满足需求,可替换为自定义 TabBar 组件(支持任意布局 / 交互):
// app/(tabs)/_layout.tsx
import { Tabs, useRouter } from "expo-router";
import { View, TouchableOpacity, Text, StyleSheet } from "react-native";
import { Ionicons } from "@expo/vector-icons";
// 自定义 TabBar 组件
const CustomTabBar = ({ state, descriptors, navigation }) => {
const router = useRouter();
// Tab 列表(与页面名一一对应)
const tabs = [
{ name: "home", title: "首页", icon: "home-outline" },
{ name: "explore", title: "发现", icon: "compass-outline" },
{ name: "profile", title: "我的", icon: "person-outline" },
];
return (
<View style={styles.customTabBar}>
{tabs.map((tab, index) => {
// 当前 Tab 是否选中
const isFocused = state.index === index;
const { options } = descriptors[`/(tabs)/${tab.name}`];
// 点击 Tab 跳转
const onPress = () => {
const event = navigation.emit({
type: "tabPress",
target: tab.name,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
router.push(`/(tabs)/${tab.name}`);
}
};
return (
<TouchableOpacity
key={tab.name}
onPress={onPress}
style={styles.customTabItem}
>
{/* 图标 */}
<Ionicons
name={tab.icon}
size={24}
color={isFocused ? "#007AFF" : "#8E8E93"}
/>
{/* 文字 */}
<Text
style={[
styles.customTabLabel,
{ color: isFocused ? "#007AFF" : "#8E8E93" },
]}
>
{tab.title}
</Text>
</TouchableOpacity>
);
})}
</View>
);
};
export default function TabsLayout() {
return (
<Tabs
// 替换为自定义 TabBar
tabBar={(props) => <CustomTabBar {...props} />}
screenOptions={{
headerShown: false,
}}
>
<Tabs.Screen name="home" options={{ title: "首页" }} />
<Tabs.Screen name="explore" options={{ title: "发现" }} />
<Tabs.Screen name="profile" options={{ title: "我的" }} />
</Tabs>
);
}
const styles = StyleSheet.create({
customTabBar: {
flexDirection: "row",
height: 60,
backgroundColor: "#fff",
borderTopWidth: 1,
borderTopColor: "#eee",
justifyContent: "space-around",
alignItems: "center",
},
customTabItem: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
customTabLabel: {
fontSize: 12,
marginTop: 2,
},
});
2. 配置顶部 TabBar(而非底部)
只需修改 screenOptions 中的 tabBarPosition:
<Tabs
screenOptions={{
tabBarPosition: "top", // 改为顶部 TabBar
tabBarStyle: {
height: 50, // 顶部 TabBar 高度
borderBottomWidth: 1,
borderTopWidth: 0,
},
// 其他配置不变
}}
>
{/* ... Tab 项 ... */}
</Tabs>
3. 隐藏 / 显示指定 Tab
- 临时隐藏:通过
tabBarButton设置为() => null:
<Tabs.Screen
name="profile"
options={{
tabBarButton: () => null, // 隐藏该 Tab(仍可通过路由跳转)
}}
/>
- 永久移除:直接删除对应的
Tabs.Screen配置。
4. 适配安卓底部安全区
import Constants from "expo-constants";
const styles = StyleSheet.create({
tabBar: {
height: Platform.OS === "ios" ? 60 : 66,
paddingBottom: Platform.OS === "android" ? Constants.statusBarHeight / 2 : 0,
// 其他样式
},
});
四、常见问题与避坑
-
TabBar 文字 / 图标不居中:
- 清空
tabBarItemStyle.paddingVertical,设置justifyContent: center; - 安卓需调整
tabBar.height,避免默认内边距导致偏移。
- 清空
-
Tab 跳转无响应:
- 确保
Tabs.Screen的name与页面文件名完全一致(如home对应home.tsx); - 自定义 TabBar 需绑定
onPress事件并调用router.push。
- 确保
-
iOS 底部 TabBar 有阴影:
- 设置
tabBarStyle.shadowOpacity: 0和elevation: 0。
- 设置
-
TabBar 样式覆盖不生效:
- 页面级
options优先级 > 全局screenOptions,检查是否有局部覆盖; - 自定义 TabBar 需完全自己控制样式,默认配置不再生效。
- 页面级
五、扩展功能(可选)
1. 带角标的 Tab
<Tabs.Screen
name="home"
options={{
tabBarBadge: 99, // 角标数字
tabBarBadgeStyle: { backgroundColor: "#FF3B30" }, // 角标样式
}}
/>
2. 切换 Tab 时的动画
<Tabs
screenOptions={{
tabBarAnimation: "fade", // 切换动画:fade/slide/none
}}
>
{/* ... */}
</Tabs>
3. 禁止 Tab 重复点击
在自定义 TabBar 的 onPress 中判断是否已选中:
const onPress = () => {
if (isFocused) return; // 已选中则不处理
// 跳转逻辑
};
总结
Expo Router 的 Tabs 组件是配置 TabBar 的最优方式,核心步骤:
- 按
(tabs)分组组织页面; - 在
(tabs)/_layout.tsx中配置全局样式和 Tab 项; - 按需自定义 TabBar 组件(完全控制样式 / 交互);
- 适配 iOS / 安卓的高度、安全区、样式差异。
以上配置覆盖 90% 的业务场景,如需更复杂的交互(如中间凸起按钮、渐变样式),可基于自定义 TabBar 组件扩展。