Bottom nav bar is not persistent across all pages in flutter app

60 views Asked by At

I have a custom bottom navigation bar that works pretty well in my flutter app.

However, on the calendar screen I have a button that navigates to the "Add Apointment" screen. I am not navigating to this screen using the Bottom Navigation Bar but just a button.

When I go to the "Add Appointment" screen, the Bottom Navigation Bar disappears and when I go back to the calendar screen the Bottom Navigation Bar is still missing.

How do I make this persistent across all screens?

Here is my Bottom Navigation Bar code:

class CustomAppBar extends StatefulWidget implements PreferredSizeWidget {
  CustomAppBar({Key? key})
      : preferredSize = const Size.fromHeight(kToolbarHeight),
        super(key: key);

  @override
  final Size preferredSize; // default is 56.0

  @override
  _CustomAppBarState createState() => _CustomAppBarState();
}

class _CustomAppBarState extends State<CustomAppBar> {
  final auth = FirebaseAuth.instance; // Need this to logout

  Future<void> signOut() async {
    await auth.signOut();
  }

  @override
  Widget build(BuildContext context) {
    return AppBar(
      //automaticallyImplyLeading: false, // removes the back button in appbar
      title: const Text("Deal Diligence"),
      actions: [
        IconButton(
          onPressed: () {
            signOut();
            Navigator.push(context,
                MaterialPageRoute(builder: (context) => LoginScreen()));
          },
          icon: const Icon(Icons.logout),
        )
      ],
    );
  }
}

Here is the code where I implement the Bottom Navigation Bar:

class MainScreenState extends State<MainScreen> {
  final auth = FirebaseAuth.instance;
  int _pageIndex = 0;

  final List<Widget> appScreens = [
    const CompanyDashboardScreen(),
    const TransactionDetailScreen(),
    const AppointmentCalendarScreen(),
    const UserProfileScreen(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      //appBar: CustomAppBar(),
      body: Container(
        child: appScreens.elementAt(_pageIndex),
      ),
      bottomNavigationBar: BottomNav(
        // Navigation Bar widget
        selectedPage: _pageIndex,
        onDestinationSelected: (index) {
          setState(() {
            _pageIndex = index;
          });
        },
      ),
    );
  }

  // Added this for BottomNavigationBar sync
  void setIndex(int index) {
    if (mounted) setState(() => this._pageIndex = index);
  }
}

Thanks for any help you can give me.

UPDATE:

I changed my code to something like the first answer but I still get the same behavior as in the original post. Here is the new code:

class MainScreenState extends State<MainScreen> {
  final auth = FirebaseAuth.instance;
  int _pageIndex = 0;

  final List<Widget> appScreens = [
    const CompanyDashboardScreen(),
    const TransactionDetailScreen(),
    const AppointmentCalendarScreen(),
    const UserProfileScreen(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: appScreens[_pageIndex],
      bottomNavigationBar: BottomNavigationBar(
        backgroundColor: Colors.blueAccent,
        selectedIconTheme: const IconThemeData(color: Colors.white),
        selectedItemColor: Colors.white,
        unselectedItemColor: Colors.black45,
        type: BottomNavigationBarType.fixed,
        currentIndex: _pageIndex,
        onTap: (value) {
          setState(() {
            _pageIndex = value;
          });
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
          BottomNavigationBarItem(
              icon: Icon(Icons.add_business_outlined), label: "Trxn"),
          BottomNavigationBarItem(
              icon: Icon(Icons.calendar_today), label: "Calendar"),
          BottomNavigationBarItem(icon: Icon(Icons.people), label: "Profile"),
        ],
      ),
    );
  }

UPDATE #2:

Here is the code where I click the button and go to the "Add Event" page:

return Scaffold(
      appBar: CustomAppBar(),
      backgroundColor: Colors.white,
      resizeToAvoidBottomInset: false,
      body: Column(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          StreamBuilder<QuerySnapshot>(
            stream: _db
                .collection('users')
                .doc(ref.read(globalsNotifierProvider).currentUserId)
                .collection('events')
                .where('eventDate', isGreaterThanOrEqualTo: kFirstDay)
                .where('eventDate', isLessThanOrEqualTo: kLastDay)
                .snapshots(),
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              // Handle any errors
              if (snapshot.hasError) {
                return Center(
                  child: Text('Error fetching data: ${snapshot.error}'),
                );
              }

              // Handle loading data
              debugPrint('Data: ${snapshot.data}');
              if (snapshot.connectionState == ConnectionState.waiting) {
                return const Center(child: CircularProgressIndicator());
              } else {
                if (snapshot.hasData && snapshot.data!.docs.isNotEmpty) {
                  List<Events> eventsList = [];
                  for (var snapshotEvent in snapshot.data!.docs) {
                    Events event =
                        Events.fromJson(snapshotEvent.id, snapshotEvent.data());
                    eventsList.add(event);
                  }
                  return _buildTableCalendar(eventsList);
                } else {
                  return _buildTableCalendar();
                }
              }
            },
          ),
          const SizedBox(height: 8.0),
          //_buildButtons(),
          ElevatedButton(
            onPressed: () async {
              setState(() {
                showSpinner = true;
              });
              try {
                ref.read(globalsNotifierProvider.notifier).updatenewEvent(true);

                Navigator.of(context).pushReplacement(
                    MaterialPageRoute(builder: (context) => AddEventScreen()));

                setState(() {
                  showSpinner = false;
                });
              } catch (e) {
                // todo: add better error handling
                print(e);
              }
            },
            child: const Text('Add Event'),
          ),
          const SizedBox(height: 8.0),
          Expanded(child: _buildEventList()),
        ],
      ),
    );

Here is the code of the next page, "Add Event":

return Scaffold(
      appBar: CustomAppBar(),
      backgroundColor: Colors.white,
      body: SafeArea(
        child: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(20.0),
            child: Column(
              children: <Widget>[
                const Text(
                  'Event Editor',
                  style: TextStyle(
                    fontSize: 30,
                    fontWeight: FontWeight.w800,
                  ),
                ),
                const SizedBox(
                  height: 30.0,
                ),
                TextField(
                  controller: eventNameController,
                  autofocus: true,
                  keyboardType: TextInputType.text,
                  textAlign: TextAlign.center,
                  onChanged: (value) {
                    ref
                        .read(eventsNotifierProvider.notifier)
                        .updateEventname(value);
                  },
                  decoration: const InputDecoration(
                      hintText: 'Event Name', labelText: 'Event Name'),
                ),
                const SizedBox(
                  height: 8.0,
                ),
                TextField(
                  controller: eventDurationController,
                  textAlign: TextAlign.center,
                  onChanged: (value) {
                    ref
                        .read(eventsNotifierProvider.notifier)
                        .updateEventDuration(value);
                  },
                  decoration: const InputDecoration(
                      hintText: 'Duration', labelText: 'Duration'),
                ),
                const SizedBox(
                  height: 8.0,
                ),
                TextField(
                  controller: eventDateController,
                  keyboardType: TextInputType.datetime,
                  textAlign: TextAlign.center,
                  onTap: () async {
                    DateTime? _datePicked = await showDatePicker(
                        context: context,
                        initialDate: _selectedDate!,
                        firstDate: DateTime(2023),
                        lastDate: DateTime(2026));
                    if (_date != null && _date != _datePicked) {
                      setState(() {
                        eventDateController.text =
                            DateFormat("MM/dd/yyyy").format(_datePicked!);
                        ref
                            .read(eventsNotifierProvider.notifier)
                            .updateEventDate(_datePicked);
                        _selectedDate = _datePicked;
                        //DateFormat("MM/dd/yyyy").format(_date));
                      });
                    }
                  },
                  onChanged: (value) {
                    ref
                        .read(eventsNotifierProvider.notifier)
                        .updateEventDate(_date);
                  },
                  decoration: const InputDecoration(
                    hintText: 'Date',
                    labelText: 'Date',
                  ),
                ),
                const SizedBox(
                  height: 8.0,
                ),
                TextField(
                  controller: eventStartTimeController,
                  keyboardType: TextInputType.text,
                  textAlign: TextAlign.center,
                  onTap: () async {
                    final TimeOfDay? _timePicked = await showTimePicker(
                      context: context,
                      initialTime: TimeOfDay.now(),
                    );
                    if (_timePicked != null) {
                      _dt = DateTime(
                        _selectedDate.year,
                        _selectedDate.month,
                        _selectedDate.day,
                        _timePicked.hour,
                        _timePicked.minute,
                      );
                      setState(() {
                        eventStartTimeController.text =
                            DateFormat('h:mm a').format(_dt);
                        ref
                            .read(eventsNotifierProvider.notifier)
                            .updateeventStartTime(_dt);
                      });
                    }
                  },
                  onChanged: (value) {
                    ref
                        .read(eventsNotifierProvider.notifier)
                        .updateeventStartTime(_dt);
                  },
                  decoration: const InputDecoration(
                    hintText: 'Start Time',
                    labelText: 'Start Time',
                  ),
                ),
                const SizedBox(
                  height: 8.0,
                ),
                TextField(
                  controller: eventDescriptionController,
                  keyboardType: TextInputType.text,
                  textAlign: TextAlign.center,
                  onChanged: (value) {
                    // ref
                    //     .read(eventsNotifierProvider.notifier)
                    //     .updateEventDescription(value);
                  },
                  decoration: const InputDecoration(
                      hintText: 'Description', labelText: 'Description'),
                ),
                const SizedBox(
                  height: 8.0,
                ),
                RoundedButton(
                  title: 'Save Event',
                  colour: Colors.blueAccent,
                  onPressed: () async {
                    setState(() {
                      showSpinner = true;
                    });
                    try {
                      ref
                          .read(globalsNotifierProvider.notifier)
                          .updatenewEvent(true);
                      if (currentEventId != null && currentEventId != '') {
                        ref.read(eventsNotifierProvider.notifier).saveEvent(
                            ref.read(eventsNotifierProvider), currentEventId);
                      } else {
                        ref
                            .read(eventsNotifierProvider.notifier)
                            .updateEventDescription(
                                eventDescriptionController.text);

                        ref.read(eventsNotifierProvider.notifier).saveEvent(
                            ref.read(eventsNotifierProvider),
                            eventDescriptionController
                                .text); // Create new event
                      }
                      Navigator.of(context).pushReplacement(MaterialPageRoute(
                          builder: (context) =>
                              const AppointmentCalendarScreen()));

                      setState(() {
                        showSpinner = false;
                      });
                    } catch (e) {
                      // todo: add better error handling
                      print(e);
                    }
                  },
                ),
                const SizedBox(
                  height: 8.0,
                ),
                (widget != null)
                    ? RoundedButton(
                        title: 'Delete',
                        colour: Colors.red,
                        onPressed: () async {
                          setState(() {
                            showSpinner = true;
                          });
                          try {
                            ref
                                .read(eventsNotifierProvider.notifier)
                                .deleteEvent(currentEventId);
                            Navigator.of(context).pushReplacement(
                                MaterialPageRoute(
                                    builder: (context) =>
                                        AppointmentCalendarScreen()));

                            setState(() {
                              showSpinner = false;
                            });
                          } catch (e) {
                            // todo: add better error handling
                            print(e);
                          }
                        },
                      )
                    : Container()
              ],
            ),
          ),
        ),
      ),
    );

I hope this helps Thanks

1

There are 1 answers

5
Usama Jamil On

In bottom navigation bar all pages is child of main container. If you stay on parent container you can access bottom navigation from any child. I'm sharing you a example

class BottomNavigationExample extends StatefulWidget {
  const BottomNavigationExample({Key? key}) : super(key: key);

  @override
  _BottomNavigationExampleState createState() =>
      _BottomNavigationExampleState();
}

class _BottomNavigationExampleState extends State {
  int _selectedTab = 0;

  List _pages = [
    HomePage(),
    AboutPage(),
    ProductPage(),
    ContactPage(),
    SettingsPage(),
  ];

  _changeTab(int index) {
    setState(() {
      _selectedTab = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: _pages[_selectedTab],
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _selectedTab,
        onTap: (index) => _changeTab(index),
        selectedItemColor: Colors.red,
        unselectedItemColor: Colors.grey,
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: "About"),
          BottomNavigationBarItem(
              icon: Icon(Icons.grid_3x3_outlined), label: "Product"),
          BottomNavigationBarItem(
              icon: Icon(Icons.contact_mail), label: "Contact"),
          BottomNavigationBarItem(
              icon: Icon(Icons.settings), label: "Settings"),
        ],
      ),
    );
  }
}

Don't use "Navigator" to navigate bottom navigation page. Bottom navigation is navigate through view change as in example

Don't use push replacement while navigating to other screen . Push replacement replaces the current screen to target screen and you cannot back to parent screen. Only use push like below example

Navigator.of(context).push(
                    MaterialPageRoute(builder: (context) => AddEventScreen()));