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
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
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