You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 14:42:00