Flutter中使用BottomNavigationBar切换屏幕时消除页面堆叠的方法
问题根源
你的问题出在Dashboard页面的结构设计:你在Dashboard的Scaffold body里用Stack同时渲染了pages.elementAt(activeIndex)和Dashboard专属的头部/内容区域。这意味着当切换到其他页面(比如HomePage)时,Dashboard的头部UI并没有被移除,而是和目标页面的内容叠加显示,导致两个AppBar/页面内容同时出现。
解决方案:重构页面结构
核心思路是让每个页面独立,主页面(带BottomNavigationBar的容器)直接根据activeIndex渲染对应页面,而非在Dashboard里嵌套整个页面列表。
步骤1:调整主页面(带BottomNavigationBar的容器)
让主页面直接承载导航逻辑,body区域仅渲染当前选中的页面:
class MainNavigationScreen extends StatefulWidget { const MainNavigationScreen({super.key}); @override State<MainNavigationScreen> createState() => _MainNavigationScreenState(); } class _MainNavigationScreenState extends State<MainNavigationScreen> { int activeIndex = 0; final List<Widget> pages = const [ Dashboard(), HomePage(checklistData: [], numberofForms: 0, userData: {},), ScannerPage(title: 'Scan QR Code',), Notifications(), Login(), ]; @override Widget build(BuildContext context) { return Scaffold( body: pages[activeIndex], // 直接渲染当前选中的页面,无嵌套 bottomNavigationBar: BottomNavigationBar( currentIndex: activeIndex, onTap: (index) { setState(() { activeIndex = index; }); }, // 你的导航项配置 items: const [ BottomNavigationBarItem(icon: Icon(Icons.dashboard), label: 'Dashboard'), BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'), BottomNavigationBarItem(icon: Icon(Icons.qr_code_scanner), label: 'Scanner'), BottomNavigationBarItem(icon: Icon(Icons.notifications), label: 'Notifications'), BottomNavigationBarItem(icon: Icon(Icons.login), label: 'Login'), ], ), ); } }
步骤2:修改Dashboard页面,移除嵌套逻辑
让Dashboard仅展示自己的专属内容,不再嵌套其他页面:
class Dashboard extends StatelessWidget { const Dashboard({super.key}); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Constants.primaryColor, body: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: kToolbarHeight), Container( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ GestureDetector( onTap: () { Navigator.pop(context); }, child: const Icon( FlutterIcons.keyboard_backspace_mdi, color: Colors.white, ), ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ RichText( text: TextSpan( children: [ TextSpan( text: "Welcome back,\n", style: Theme.of(context) .textTheme .headline6 ?.copyWith(color: Colors.white), ), TextSpan( text: "Agent Wesley!", style: Theme.of(context) .textTheme .headline6 ?.copyWith( color: Colors.white, fontWeight: FontWeight.w600, ), ) ], ), ), ], ) ], ), ), const SizedBox(height: 50.0), Container( width: double.infinity, constraints: BoxConstraints( minHeight: MediaQuery.of(context).size.height - 200.0, ), decoration: const BoxDecoration( borderRadius: BorderRadius.only( topLeft: Radius.circular(30.0), topRight: Radius.circular(30.0), ), color: Constants.scaffoldBackgroundColor, ), padding: const EdgeInsets.symmetric(vertical: 24.0), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( padding: EdgeInsets.symmetric(horizontal: 24.0), child: Text( "Agent Services", style: TextStyle( color: Color.fromRGBO(19, 22, 33, 1), fontSize: 18.0, ), ), ), const SizedBox(height: 7.0), Container( height: 100.0, child: const Center( // child: ServiceSlider(), ), ), ], ), ) ], ), ), ); } }
原理说明
通过上述修改,每个页面都是独立的Scaffold组件,主页面仅负责根据activeIndex切换渲染目标页面,彻底避免了页面内容嵌套叠加的问题。切换导航项时,旧页面会被完全移除,仅显示当前选中的页面内容。
内容的提问来源于stack exchange,提问作者Kambale
相关产品推荐
相关产品推荐

