Navigation and named routes in Flutter

Samuel Wahome
4 min readSep 18, 2021

You are probably familiar with how one is able to navigate from one page to another when using apps eg. navigating from the home page to the settings page, or even when using websites. In Flutter, this functionality, as crucial as it is, can easily be achieved, so therefore there is definitely no need for panic.

In Flutter, screens and pages are referred to as routes, hence that is the terminology that will be used here henceforth. It is important to note that in Android, a route is simply another Activity, while in iOS, a route refers to a UIViewController. In Flutter, a route simply refers to a widget.

Named Routes.

In this post, we’ll focus more on named routes, as they are mostly used in instances where you need to navigate to the same screen in many parts of your app, plus I do find this approach neater and more maintainable.

The official docs pretty much recommend this approach for apps that scale. To start off, we’ll need to create a dart file where we’ll store all the routes of our application, in order to make accessing them that much easier:

///app_routes.dartclass AppRoutes{
static const home = '/home';
static const login= '/login';
static const register = '/register';
static const settings = '/settings';
static const profile = '/profile';
}

Once this is done, one would need to go back to the main dart file and add an initialRoute property to the MaterialApp widget, to which we will attach a String method that returns the initial route the application should navigate to after initialization. We’ll also need to add an onGeneratedRoute property whose logic we shall define soon enough:

/// main.dartclass MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);

// This widget is the root of your application.
@override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Sample App',
initialRoute: getInitialPage(),
onGenerateRoute: RouteGenerator.generateRoute,
);
}

String getInitialPage() => AppRoutes.login;
}

Once this is done, we’ll need another dart file where the actual routing logic will be implemented, hence making all navigations common to that file alone:

/// route_generator.dartclass RouteGenerator {
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case AppRoutes.login:
return buildRoute(const LoginScreen(), settings: settings);
case AppRoutes.register:
return buildRoute(const Register(), settings: settings);
case AppRoutes.profile:
final arguments = settings.arguments as ProfileArguments; return buildRoute(const Profile(arguments: arguments), settings: settings);
case AppRoutes.settings:
return buildRoute(const Settings(), settings: settings);
default:
return _errorRoute();
}
}

static MaterialPageRoute buildRoute(Widget child,
{required RouteSettings settings}) {
return MaterialPageRoute(
settings: settings, builder: (BuildContext context) => child);
}

static Route<dynamic> _errorRoute() {
return MaterialPageRoute(builder: (_) {
return Scaffold(
appBar: AppBar(
backgroundColor: kBlue,
title: const Text(
'ERROR!!',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
centerTitle: true,
),
body: Center(
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: 450.0,
width: 450.0,
child: Lottie.asset('assets/lottie/error.json'),
),
const Text(
'Seems the route you\'ve navigated to doesn\'t exist!!',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w600,
),
textAlign: TextAlign.center,
),
],
),
),
),
);
});
}
}

The settings.nameparameter simply refers to the route name. In this file, we define a method generateRoute, where we include a switch-case statement and add all the necessary Widgets that our application will be routed to. The default route is simply an error page that will appear only when the user navigates to a route that does not exist.

We wrap our individual widgets in a buildRoute method that returns a MaterialPageRoute, hence we are able to abstract our actual navigation login to one method. In this method, we supply a Widget and RouteSettings parameter, which will then be used in the actual generateRoute function.

The fun part comes in when we are trying to navigate to a route that requires many parameters. In this case, for the profile route, I have created an additional ProfileArguments class where we’ll define all the arguments that this specific route will need:

///profile_arguments.dartclass ProfileArguments {
final String userUID;
final String userDisplayName;
final String username;
final String userBio;
final String userImage;

const ProfileArguments(
{required this.userUID,
required this.userDisplayName,
required this.username,
required this.userBio,
required this.userImage});
}

To make all the logic we’ve written work, we just have to add a few more lines of code and we will be done.

Navigation.

In order to navigate using named routes, we will be using the Navigator.pushNamed() function. The code below demonstrates how to navigate to a named route with arguments:

GestureDetector(
onTap: () {
final args = AltProfileArguments(
userUID: feedDoc['userUID'],
userImage: snapshot.data!['photoUrl'],
username: snapshot.data!['username'],
userDisplayName: snapshot.data!['displayName'],
userBio: snapshot.data!['bio'],
);
Navigator.pushNamed(
context,
AppRoutes.profile,
arguments: args,
);

},
child: RichText(
overflow: TextOverflow.fade,
text: TextSpan(
style: GoogleFonts.josefinSans(
textStyle: const TextStyle(
fontSize: 15.0,
color: Colors.black,
),
),
children: [
TextSpan(
text: '@' + snapshot.data!['username'],
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
TextSpan(
text:' liked your post.',
),
],
),
),
),

The code below demonstrates how to navigate to a named route without any arguments:

TextButton(
onPressed: () {
Navigator.pushNamed(context, AppRoutes.settings);
},
child: const Text(
'SETTINGS',
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all<Color>(Colors.white),
backgroundColor: MaterialStateProperty.all<Color>(kBlue),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0),
side: const BorderSide(color: kBlue),
),
),
),
),

Navigating back to a previous screen is as simple as using the Navigator.pop() function.

And that is all I’ve had to share on using named routes in navigation. To all readers, cheers to code🥂, and have a blessed day.

--

--

Samuel Wahome

A Software developer || Very curious guy || Ardent reader.