Flutter:使用OpenContainer动画时隐藏persistent_bottom_nav_bar的问题
使用 persistent_bottom_nav_bar: 6.2.1 实现底部导航,同时尝试通过 OpenContainer 实现带动画的页面跳转,但跳转后原页面的 AppBar 和 BottomNavigationBar 仍然可见。曾尝试组合 openContainer() 和 PersistentNavBarNavigator.pushNewScreen(),但导致页面被推送两次,不符合需求。
解决方案
问题根源是外层 Scaffold 包含了全局 AppBar 和 PersistentTabView,使用 OpenContainer 默认导航时,会在该 Scaffold 的内部 Navigator 栈中跳转,导致原导航栏组件仍处于上层上下文。以下两种方法可解决:
方法一:将 AppBar 移至 Tab 页面内部
这是最简洁的方案,符合 PersistentBottomNavBar 的设计规范:
- 移除外层 Scaffold 的 AppBar
修改_DashboardView的build方法:
@override Widget build(BuildContext context) { return Scaffold( // 移除全局 AppBar body: PersistentTabView( context, controller: _controller, screens: _buildScreens(), items: _navBarsItems(), // 保留原有的其他配置 handleAndroidBackButtonPress: true, resizeToAvoidBottomInset: true, stateManagement: true, hideNavigationBarWhenKeyboardAppears: true, padding: const EdgeInsets.only(top: 8), backgroundColor: ColorConstant.secondary, isVisible: true, animationSettings: const NavBarAnimationSettings( navBarItemAnimation: ItemAnimationSettings( duration: Duration(milliseconds: 400), curve: Curves.ease, ), screenTransitionAnimation: ScreenTransitionAnimationSettings( animateTabTransition: true, duration: Duration(milliseconds: 200), screenTransitionAnimationType: ScreenTransitionAnimationType.fadeIn, ), ), confineToSafeArea: true, navBarHeight: kBottomNavigationBarHeight, navBarStyle: NavBarStyle.style3, ), ); }
- 在每个 Tab 页面中添加 AppBar
以HomeScreen为例,将原全局的DynamicAppBar移至页面内部:
class HomeScreen extends StatelessWidget { final ScrollController scrollController; const HomeScreen({super.key, required this.scrollController}); @override Widget build(BuildContext context) { return Scaffold( appBar: DynamicAppBar( backgroundColor: ColorConstant.secondary, title: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Image.asset(AssetConstant.appLogo, width: 30), const SizedBox(width: 8), Text("GEBOK", style: TextStyle(color: ColorConstant.primary, fontWeight: FontWeight.bold, fontSize: 18)), ], ), trailingWidgets: [ // 修正搜索按钮的 OpenContainer 导航 OpenContainer( transitionType: ContainerTransitionType.fadeThrough, transitionDuration: Duration(milliseconds: 600), closedElevation: 0, closedShape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), openBuilder: (context, _) => SearchScreen(), closedBuilder: (context, openContainer) { return IconButton( icon: const Icon(Icons.search, color: ColorConstant.gray), onPressed: openContainer, ); }, ), // 保留原 PopupMenuButton PopupMenuButton<String>( offset: const Offset(0, 60), color: ColorConstant.secondary, tooltip: "Menu", onSelected: (val) async { if (val == "logout") { await AuthLocal().clearAuthData(); Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (context) => LoginScreen()), (route) => false); } else if (val == "info") { Navigator.push( context, PageTransition( type: PageTransitionType.rightToLeft, duration: const Duration(milliseconds: 500), child: const InfoAppScreen(), ), ); } }, itemBuilder: (BuildContext context) => const <PopupMenuEntry<String>>[ PopupMenuItem<String>( value: 'logout', child: Row(children: [Icon(Icons.exit_to_app_outlined), SizedBox(width: 4), Text('Logout')]), ), PopupMenuItem<String>( value: 'info', child: Row(children: [Icon(Icons.info_outline), SizedBox(width: 4), Text('Info App')]), ), ], ), ], ), body: // 原 HomeScreen 的页面内容 ); } }
- 修正书籍卡片的 OpenContainer
此时直接使用OpenContainer的默认导航即可,跳转后会自动脱离原 Tab 上下文,隐藏底部导航栏和当前页面的 AppBar:
OpenContainer( transitionType: ContainerTransitionType.fadeThrough, transitionDuration: const Duration(milliseconds: 500), openBuilder: (context, _) => BookDetailScreen(bookId: item.id), closedElevation: 0, closedColor: Colors.transparent, closedShape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), closedBuilder: (context, openContainer) { return Material( color: Colors.transparent, child: InkWell( onTap: openContainer, borderRadius: BorderRadius.circular(8), child: <my content>, ), ); }, ),
方法二:结合 PersistentNavBarNavigator 与 OpenContainer 动画
若不想调整页面结构,可通过 PersistentNavBarNavigator 处理导航,同时保留 OpenContainer 的转场动画:
修改书籍卡片的 OpenContainer 代码:
OpenContainer( transitionType: ContainerTransitionType.fadeThrough, transitionDuration: const Duration(milliseconds: 500), closedElevation: 0, closedColor: Colors.transparent, closedShape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), closedBuilder: (context, openContainer) { return Material( color: Colors.transparent, child: InkWell( onTap: () { // 使用 PersistentBottomNavBar 的导航方法,禁用默认页面动画 PersistentNavBarNavigator.pushNewScreen( context, screen: OpenContainer( openBuilder: (_, __) => BookDetailScreen(bookId: item.id), closedBuilder: (_, ___) => Container(), transitionType: ContainerTransitionType.fadeThrough, transitionDuration: const Duration(milliseconds: 500), openElevation: 0, ), withNavBar: false, pageTransitionAnimation: PageTransitionAnimation.none, ); }, borderRadius: BorderRadius.circular(8), child: <my content>, ), ); }, openBuilder: (context, _) => Container(), ),
说明
- 方法一逻辑清晰,符合组件分层设计,是推荐方案
- 方法二无需改动现有页面布局,但代码复杂度较高,适合临时场景
内容的提问来源于stack exchange,提问作者VerdIXI
相关产品推荐
相关产品推荐

