Seamless Transitions: Enhancing Bottom Navigation Bar Stability in Flutter
In mobile applications, navigation can be as essential as a compass for a sailor. BottomNavigationBar in Flutter serves as that compass, guiding users through different views. But what if it vanishes when users sail deeper into individual pages? This article explores this challenge and introduces our solutions.
Identifying and Understanding the Problem
In Flutter, a BottomNavigationBar typically allows transitions between various pages of the application. But a problem arises: suppose we have three items in the BottomNavigationBar. In this case, the BottomNavigationBar continues to exist as we navigate between these three items. However, when we go to any of these three pages and are redirected to another internal page, the BottomNavigationBar disappears.
This problem stems from Flutter’s navigation stack concept. Each time we go to a new page, a new Scaffold is essentially created and the previous Scaffold remains within the stack. Therefore, the BottomNavigationBar only stays within the current Scaffold and disappears when we switch to a new page.
Solution: Reusability with CustomScaffold
To solve this problem, we first need to create a CustomScaffold that will use the same BottomNavigationBar on all pages. This CustomScaffold can be used as a Scaffold on each page, thus ensuring the constant visibility of the same BottomNavigationBar. Our CustomScaffold class is as follows:
class CustomScaffold extends StatefulWidget {
final Widget body; // body property
const CustomScaffold({
Key? key,
required this.body
}) : super(key: key);
@override
State<CustomScaffold> createState() => _CustomScaffoldState();
}
class _CustomScaffoldState extends State<CustomScaffold> {
int index = 1; // active index of the bottom navigation bar
List<Widget> pages = [const Page1(), const Page2(), const Page3()];
// A list of pages to display when a BottomNavigationBar item is selected
@override
Widget build(BuildContext context) {
return RepaintBoundary(
child: Scaffold(
//... other scaffold properties
body: widget.body, // using the provided body, different at every page
bottomNavigationBar: BottomNavigationBar(
currentIndex: index, // index of the active item
onTap: (newIndex) { // when an item is clicked
setState(() {
index = newIndex; // update the index
});
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => pages[index],
)
);
},
//... other bottom navigation bar properties
),
),
);
}
}
In this code block, we created a list called pages
. This list contains the pages corresponding to each item in the BottomNavigationBar. Then, we updated the onTap
function so that when we click on an item, we are redirected to the page corresponding to this item. With each click, we also update the index of the selected item.
Using CustomScaffold
To use CustomScaffold, it is necessary to replace the current Scaffold with CustomScaffold on any particular page. This ensures the stability of the BottomNavigationBar across different pages. Additionally, we need to provide a body for each instance of the CustomScaffold. This body, which could be any widget, represents the content of the page above the BottomNavigationBar. Let’s consider the following example:
class HomePage extends StatefulWidget {
const HomePage({Key? key}): super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return CustomScaffold(
body: Center(
child: Text(
"Home Page",
style: TextStyle(color: Colors.white, fontSize: 20),
),
),
);
}
}
In this example, we have used CustomScaffold for the HomePage widget. We provide a Center widget that contains a Text widget as the body. This body content is what will be displayed on the HomePage, above the BottomNavigationBar. As you see, using the CustomScaffold enables us to maintain the same BottomNavigationBar while having different body contents on different pages.
However, there is another problem: When we click on another item in the BottomNavigationBar, we go to that page, but the selected item of the BottomNavigationBar returns to its initial value of 1 each time.
Solution: Persistence with LocalStorage
Every new page opened uses the CustomScaffold widget, and every time a new CustomScaffold is created; thus, the index variable you define in the CustomScaffold resets to its initial value of 1 each time. Therefore, to maintain the selected element in the submenu, we need to carry the index value to a parent widget or keep it globally.
To solve this problem, we’re using the GetStorage
package. This package offers a simple, fast, and efficient way to store data persistently in Flutter apps. It provides a convenient way to store simple data, like the selected index of the BottomNavigationBar, across app restarts or transitioned from one page to another. The implementation in our CustomScaffold class looks like this:
final localStorage = GetStorage();
class _CustomScaffoldState extends State<CustomScaffold> {
int index = 1; // active index of the bottom navigation bar
List<Widget> pages = [const Page1(), const Page2(), const Page3()];
@override
void initState() {
super.initState();
// If there's a previously saved index, read it from localStorage
if (localStorage.read('index') != null) {
index = localStorage.read('index');
}
}
@override
Widget build(BuildContext context) {
return RepaintBoundary(
child: Scaffold(
//... other scaffold properties
bottomNavigationBar: BottomNavigationBar(
currentIndex: index, // index of the active item
onTap: (newIndex) { // when an item is clicked
setState(() {
index = newIndex; // update the index
localStorage.write('index', index);
// and save it to localStorage
});
},
//... other bottom navigation bar properties
),
),
);
}
}
In this way, each time a page transition or application restart occurs, we read the selected index of the BottomNavigationBar from localStorage and set it to the current index value. This ensures that the BottomNavigationBar consistently remembers the user’s last selection.
Summary
Throughout this article, we have addressed the issue of maintaining the stability of BottomNavigationBar in Flutter and enhancing the navigation experience. We have addressed the problem of the BottomNavigationBar becoming invisible when navigating between pages, a problem brought about by Flutter’s navigation structure.
At the heart of this solution is the use of CustomScaffold, which allows the same BottomNavigationBar to be used while navigating between the various pages of the application. In addition, we have used localStorage to keep track of the selected item on the screen with each page transition.
Conclusion
In conclusion, using CustomScaffold and localStorage, we have provided a more user-friendly and continuous BottomNavigationBar experience. These are just a few of the tools we can use to improve navigation and user experience in Flutter. These solutions are scalable and extensible for more complex applications. Using these tools, you can make your users’ interactions with your application smoother and more seamless.