必须彻底删除pages.json中的tabBar对象(含color等所有字段),否则custom:true不生效;需用fixed容器+env(safe-area-inset-bottom)适配,凸起按钮须absolute定位并设z-index,tab跳转须手动接管且状态统一管理。

pages.json 里必须删干净 tabBar 配置
只要 tabBar 对象还留在 pages.json 里,哪怕只留一个 color 字段,custom: true 就不会生效——uni-app 会悄悄撑开一个 50px 高的不可见占位层,导致你写的底部操作栏被顶高、凸起按钮错位、安全区计算全乱。
常见现象:custom-tab-bar/index.vue 渲染出来了,但底部总有一段“看不见的空白”,或者中间加号按钮在 iOS 上偏上、安卓上偏下。
- 打开
pages.json,找到整个tabBar块(含list、color、backgroundColor等所有字段) - 整块删除,或用
/* ... */注释掉,别留任何残留 - HBuilderX 不报错,静默失败是常态,删之前建议备份原文件
fixed 容器 + env(safe-area-inset-bottom) 是唯一可靠方案
别用 margin-bottom 或 transform: translateY() 模拟底部悬浮,滚动时按钮会跟着页面动,真机上完全不可控。
正确做法是把整个操作栏容器设为 position: fixed; bottom: 0; left: 0; right: 0;,再单独抽离凸起按钮为 position: absolute; 子元素。
- 容器加
padding-bottom: env(safe-area-inset-bottom),避免被 iPhone 底部圆角/刘海截断 - 鸿蒙端
env(safe-area-inset-bottom)可能返回0,建议 fallback:padding-bottom: max(34px, env(safe-area-inset-bottom)) - 凸起按钮用
bottom: 20px; left: 50%; transform: translateX(-50%);居中上浮,别靠margin或flex模拟 - 固定容器总高度(如
height: 60px),并设z-index: 10,确保盖住页面内容
uni.switchTab 在 custom 模式下会静默失败
uni.switchTab() 在 custom: true 后,只对原 tabBar.list 里声明过的页面有效;你已删了 tabBar,它就直接不跳转、也不报错。
当前激活项状态不能依赖 uni.getTabBar()(H5 返回 undefined),也不能靠 DOM class 切换——得用全局状态管理(Pinia / Vuex)或 data 存储。
- 每个 tab 项点击时,统一走
uni.getPages().find()查栈,存在则uni.navigateTo({ url: 'xxx' }),不存在才uni.reLaunch() - 凸起按钮点击事件不触发
uni.onTabItemTap,得单独绑定@click并手动处理逻辑 - 页面
onShow里要同步更新当前 tab 状态,否则返回时图标高亮错乱
凸起按钮位置在不同平台差异极大
iOS 安全区高度动态变化(比如键盘弹起时),安卓各厂商 bottom 值不一致(华为/小米/OPPO 返回值常差 8–12px),纯 CSS 很难兜住。
最稳的做法是:容器用 fixed + env(safe-area-inset-bottom),凸起按钮用绝对定位 + 固定像素偏移,并预留 fallback。
- 不要用百分比或视口单位(
vh)控制按钮垂直位置 - 凸起区域高度写死(如
60px),并在父容器设对应height和z-index - 真机调试必须覆盖 iOS 16+、Android 12–14、鸿蒙 NEXT,模拟器不可信
实际复杂点不在样式,而在状态同步和平台差异的缝合——尤其是凸起按钮点击后,如何让其他 tab 页感知状态、如何避免多页栈里重复 push、以及鸿蒙端安全区 fallback 的时机判断,这些地方稍一松懈就会在灰度阶段暴雷。



















