What to expect from this tutorial?
This is a simple tutorial that covers creating a bottom navigation bar with the auto_route
library
auto_route
is one of the most common Flutter navigation packages, it allows for strongly typed arguments passing, and effortless deep-linking and it uses code generation to simplify route setup.
bottom navigation bar is very popular in modern apps due to its simplicity and wide acceptance from users. this article will cover implementing it in our Flutter app.
The complete picture!
We want to build a bottom navigation bar with three navigation buttons. The screen code should be independent of the navigation code. So, we will start by creating our three screens, just as empty screens, and then connect them through the navigation bar as planned. let's also allow child screens to hide the bottom navigation bar as most apps do.
We have already initiated the auto_route
library in previous article, refer to Screen routing with auto_route
paragraph in the tutorial for basic library initiation.
Create navigation screens
Let's start by creating three screens; we will call them home
, memories
, and profile
screens.
We will use our previously designed home
and memories
screens. simplest profile screen would look somewhat like
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
@RoutePage()
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
return Container(
child: Text("profile"),
);
}
}
lib/presentation/screens/profile/profile_screen.dart
PS. We can use similarly simple home
and memories
screens too
notice that we are using RoutePage
annotation for all screens, so make sure to generate the router code after creating screens by issuing
dart run build_runner build - delete-conflicting-outputs
next step is to create the bottom navigation screen itself, there are multiple ways to do so as auto_route
documentation mentioned, but I found the easiest way is to use AutoTabsScaffold
import 'package:alive_diary_app/config/router/app_router.dart';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
@RoutePage()
class HomeNavScreen extends StatelessWidget {
const HomeNavScreen({super.key});
@override
Widget build(BuildContext context) {
return AutoTabsScaffold(
routes: [
HomeRoute(),
const MemoriesRoute(),
const ProfileRoute(),
],
bottomNavigationBuilder: (context, tabsRouter) {
return BottomNavigationBar(
currentIndex: tabsRouter.activeIndex,
onTap: tabsRouter.setActiveIndex,
backgroundColor: Theme.of(context).primaryColor,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.white54,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: "home"),
BottomNavigationBarItem(icon: Icon(Icons.menu_book_sharp), label: "memories"),
BottomNavigationBarItem(icon: Icon(Icons.person), label: "profile"),
],
);
},
);
}
}
lib/presentation/screens/home_nav/home_nav_screen.dart
the two main parameters for AutoTabsScaffold
are routes
, which indicate the child screen routes, and bottomNavigationBuilder
, which builds the bottom navigation bar. we can also build the top navigation bar using appBarBuilder
, but I recommend customizing it per screen (each screen has its own bar).
Navigation config
after building the navigation screen itself, it is time to config it in the app_router
file
import 'package:alive_diary_app/domain/repositories/preferences_repository.dart';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import '../../presentation/screens/home/home_screen.dart';
import '../../presentation/screens/home_nav/home_nav_screen.dart';
import '../../presentation/screens/login/login_screen.dart';
import '../../presentation/screens/memories/memories_screen.dart';
import '../../presentation/screens/profile/profile_screen.dart';
import '../dependencies.dart';
part 'app_router.gr.dart';
@AutoRouterConfig()
class AppRouter extends _$AppRouter {
bool invalidToken() => locator<PreferencesRepository>().invalidToken();
@override
List<AutoRoute> get routes => [
AutoRoute(page: LoginRoute.page, initial: invalidToken()),
AutoRoute(
page: HomeNavRoute.page,
initial: !invalidToken(),
children: [
AutoRoute(page: HomeRoute.page),
AutoRoute(page: MemoriesRoute.page),
AutoRoute(page: ProfileRoute.page),
],
),
];
}
final appRouter = AppRouter();
lib/config/router/app_router.dart
this is the complete app router config file, notice how we set the tab screens as children of the main navigation screen. we can also pass the screen title to the AutoRoute
if preferable.
We have already explained the token validation previously. in case of an invalid token, we are showing the login screen, otherwise, we are showing the main screen.
great, let's run the app now
home | memories | profile |
---|---|---|
home and memories screens look great, since we have already implemented their own top navigation bar, but what about our new simple profile
screen then??
I would recommend creating a layout widget that can be easily reused for all your apps
Layout widget
How to create a layout widget? it is simple actually, we basically need access to the page title and maybe the floating button only, why keep the headache of the full scaffold every time!
import 'package:flutter/material.dart';
class LayoutWidget extends StatelessWidget {
final Widget child;
final String title;
final Widget? floatingActionButton;
final List<Widget>? actions;
const LayoutWidget({
super.key,
required this.child,
this.title="home",
this.floatingActionButton,
this.actions,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
iconTheme: const IconThemeData(
color: Colors.white,
),
backgroundColor: Theme
.of(context)
.colorScheme
.inversePrimary,
actions: actions,
),
body: Container(
child: child,
),
floatingActionButton: floatingActionButton,
);
}
}
lib/presentation/widgets/layout_widget.dart
we are wrapping the scaffold in our widget, with the ability to add actions, a floating action button, and the main screen body. Let's use this widget for our newly created profile screen
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import '../../widgets/layout_widget.dart';
@RoutePage()
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
return LayoutWidget(
title: "Profile",
child: Container(
child: Text("profile"),
),
);
}
}
lib/presentation/screens/profile/profile_screen.dart
it should look somewhat like
looking great! we should use it for both the home and memories screens too. using it everywhere allows us to change the styles of the app navigation bar from one place.
That is it! great job. we will continue working on the app on the next articles so stay tuned
PS. To avoid repeating some ideas, and since I'm building on the same skeletal project, I might jump over some details, please don't hesitate to ask if you found any issues implementing this tutorial for your app.
as always...
Stay tuned 😎
Top comments (0)