flutterdartbloc

BottomNavigationBar not rebuilding as part of a BlocConsumer


Hey there I'm pretty new to flutter. I'm trying to get my BottomNavigationBar running. I'm using the bloc package for state changes, sending out an event when tapping on a BottomNavigationBar. I have 2 BlocConsumers... 1 in my root page to swap out the page and 1 BlocConsumer in my CustomBottomNavBar widget. When testing they separately (so when only having 1 BlocConsumer listening to my event) it works. Also when I have both BlocConsumers the listeners work, I see my custom message printed to the console, but the BottomNavigationBar for some reason is not rebuilding. Any idea why that is? Thanks a lot for your help!

VideoLink to proof that the Bottom Bar does not rebuild:

https://youtu.be/bgxWTxGCZ5Q

root.dart:

import 'package:auto_route/auto_route.dart';
import 'package:cook_meet_repeat/application/auth/auth/auth_bloc.dart';

import 'package:cook_meet_repeat/application/root/root_bloc.dart';
import 'package:cook_meet_repeat/presentation/custom_bottom_nav_bar.dart';
import 'package:cook_meet_repeat/presentation/root/events/events_page.dart';
import 'package:cook_meet_repeat/presentation/root/host_events/host_events.dart';
import 'package:cook_meet_repeat/presentation/routes/router.gr.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import '../../constants/icons.dart';

@RoutePage()
class RootPage extends StatefulWidget {
  const RootPage({super.key});

  @override
  State<RootPage> createState() => _RootPageState();
}

class _RootPageState extends State<RootPage> {
  final pages = [EventsPage(), HostEventsPage(), Placeholder(), Placeholder(), Placeholder()];
  var selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    return MultiBlocListener(
      listeners: [
        BlocListener<AuthBloc, AuthState>(listener: (context, state) {
          if (state is AuthStateAuthenticated) {
            AutoRouter.of(context).push(const SignupRoute());
          }
        }),
      ],
      child: BlocConsumer<RootBloc, RootBottomNavigationState>(
          listener: (context, state) {
        selectedIndex = state.selectedIndex;
        print("RootPage selectedIndex $selectedIndex");
      }, builder: (context, state) {
        return Scaffold(
            body: pages[selectedIndex],
            bottomNavigationBar: CustomBottomNavBar());
      }),
    );
  }
}

custom_bottom_nav_bar.dart:

import 'package:cook_meet_repeat/application/root/root_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class CustomBottomNavBar extends StatelessWidget {
  const CustomBottomNavBar({super.key});

  @override
  Widget build(BuildContext context) {
    final themeData = Theme.of(context);

    int selectedIndex = 0;

    return BlocConsumer<RootBloc, RootBottomNavigationState>(
        listener: (context, state) {
          selectedIndex = state.selectedIndex;
          print("CustomBottomNavBar selectedIndex: $selectedIndex");
        },
      builder: (context, state) {
        return BottomNavigationBar(
            type: BottomNavigationBarType.fixed,
            backgroundColor: themeData.primaryColor,
            unselectedFontSize: 14,
            items: const <BottomNavigationBarItem>[
              BottomNavigationBarItem(
                icon: Icon(Icons.set_meal_rounded),
                label: 'Events',
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.groups),
                label: 'Community',
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.add_circle),
                label: 'Host',
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.message_rounded),
                label: 'Messages',
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.person_4_rounded),
                label: 'Profile',
              ),
            ],
            currentIndex: selectedIndex,
            selectedItemColor: Colors.amber[800],
            selectedFontSize: 14,
            onTap: (index) {
              print("tapped on: $index");

              BlocProvider.of<RootBloc>(context).add(
                  RootBottomNavigationPressed(selectedIndex: index));
            }
        );
      });
  }
}

My Console Log which proofs that the listeners work

I/flutter (17333): tapped on: 2
I/flutter (17333): RootPage selectedIndex 2
I/flutter (17333): CustomBottomNavBar selectedIndex: 2
D/EGL_emulation(17333): app_time_stats: avg=11.14ms min=5.94ms max=45.80ms count=57
I/flutter (17333): tapped on: 3
I/flutter (17333): RootPage selectedIndex 3
I/flutter (17333): CustomBottomNavBar selectedIndex: 3

Tested the BlocConsumers seperately... they work separately


Solution

  • Just remove this line:

    int selectedIndex = 0;
    

    You don't need to store this value in widget because you should react on it changes getting it from your state:

    builder: (context, state) {
            final selectedIndex = state.selectedIndex;
            return BottomNavigationBar(
                type: BottomNavigationBarType.fixed,
                ...
                currentIndex: selectedIndex,
                ...