如何在Flutter Bloc中实现搭配底部导航栏的AppBar Drawer功能
Flutter Bloc 实现侧边抽屉+底部导航栏方案
1. 添加依赖
首先在pubspec.yaml中引入需要的库:
dependencies: flutter: sdk: flutter flutter_bloc: ^8.1.5 equatable: ^2.0.5
2. 定义导航事件与状态
用统一的状态管理两类导航的选中状态:
import 'package:equatable/equatable.dart'; // 导航触发事件 abstract class NavEvent extends Equatable { const NavEvent(); @override List<Object> get props => []; } class SwitchTab extends NavEvent { final int index; const SwitchTab(this.index); @override List<Object> get props => [index]; } // 导航状态 class NavState extends Equatable { final int currentIndex; const NavState(this.currentIndex); @override List<Object> get props => [currentIndex]; }
3. 实现导航Bloc
统一处理页面切换逻辑:
import 'package:bloc/bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class NavBloc extends Bloc<NavEvent, NavState> { NavBloc() : super(const NavState(0)) { on<SwitchTab>((event, emit) { emit(NavState(event.index)); }); } }
4. 搭建主页面结构
同时集成AppBar、侧边抽屉、底部导航栏:
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return BlocProvider( create: (context) => NavBloc(), child: MaterialApp( home: const MainPage(), theme: ThemeData(primarySwatch: Colors.blue), ), ); } } class MainPage extends StatelessWidget { const MainPage({super.key}); // 所有页面对应列表,两类导航统一复用 final List<Widget> pages = const [ HomePage(), CategoryPage(), ProfilePage(), SettingsPage(), // 可添加仅侧边抽屉有的页面 ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("导航示例")), // 侧边抽屉 drawer: Drawer( child: ListView( padding: EdgeInsets.zero, children: [ const DrawerHeader( decoration: BoxDecoration(color: Colors.blue), child: Text("侧边导航", style: TextStyle(color: Colors.white, fontSize: 24)), ), ListTile( leading: const Icon(Icons.home), title: const Text("首页"), onTap: () { context.read<NavBloc>().add(const SwitchTab(0)); Navigator.pop(context); }, ), ListTile( leading: const Icon(Icons.category), title: const Text("分类"), onTap: () { context.read<NavBloc>().add(const SwitchTab(1)); Navigator.pop(context); }, ), ListTile( leading: const Icon(Icons.person), title: const Text("我的"), onTap: () { context.read<NavBloc>().add(const SwitchTab(2)); Navigator.pop(context); }, ), ListTile( leading: const Icon(Icons.settings), title: const Text("设置"), onTap: () { context.read<NavBloc>().add(const SwitchTab(3)); Navigator.pop(context); }, ), ], ), ), // 主体内容 body: BlocBuilder<NavBloc, NavState>( builder: (context, state) => pages[state.currentIndex], ), // 底部导航栏 bottomNavigationBar: BlocBuilder<NavBloc, NavState>( builder: (context, state) { return BottomNavigationBar( // 跳转到抽屉独有页面时,底部导航默认选中首页,可自定义逻辑 currentIndex: state.currentIndex < 3 ? state.currentIndex : 0, type: BottomNavigationBarType.fixed, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: "首页"), BottomNavigationBarItem(icon: Icon(Icons.category), label: "分类"), BottomNavigationBarItem(icon: Icon(Icons.person), label: "我的"), ], onTap: (index) => context.read<NavBloc>().add(SwitchTab(index)), ); }, ), ); } } // 示例页面组件 class HomePage extends StatelessWidget {const HomePage({super.key});@override Widget build(BuildContext context)=>const Center(child: Text("首页"));} class CategoryPage extends StatelessWidget {const CategoryPage({super.key});@override Widget build(BuildContext context)=>const Center(child: Text("分类页"));} class ProfilePage extends StatelessWidget {const ProfilePage({super.key});@override Widget build(BuildContext context)=>const Center(child: Text("我的页"));} class SettingsPage extends StatelessWidget {const SettingsPage({super.key});@override Widget build(BuildContext context)=>const Center(child: Text("设置页"));}
关键说明
- 两类导航共用同一个
NavBloc管理状态,不会出现选中状态不同步的问题 - 所有切换逻辑都在Bloc层处理,后续新增导航入口不需要修改页面层代码
- 如果需要跳转到抽屉独有页面时隐藏底部导航,只需要在BlocBuilder中判断currentIndex,符合条件时将
bottomNavigationBar赋值为null即可
内容的提问来源于stack exchange,提问作者Vinay Bakle
相关产品推荐
相关产品推荐

