Flutter Navigator 2.0 with Router

In this article, We are going to understand how to implement flutter navigator 2.0 with router in flutter applications. You can also check out flutter navigator 2.0 without router at HERE.

The Router API is used to provide underlying platform to handle routes in flutter apps. We can parse browser URL to display page using Router. In this flutter example, we are displaying football clubs names in the list UI. On click of list item, we will display details screen.

Page is used to set navigator’s history stack. RouteInformtionParser is used to take RouteInformation from RouteInformationProvider and parse into user defined datatype.

RouterDelegate is used to detect changes in app state and responds them. It builds navigator with current list of pages. BackButtonDispatcher is used to report back button press to router.

 

Create Model Class

class SoccerClub {
final String clubName;
final String countryName;

SoccerClub(this.clubName, this.countryName);
}

 
 

Create App Class for Flutter Navigator

import ‘package:flutter/material.dart’;
import ‘soccer_club_route_information_parser.dart’;
import ‘soccer_club_router_delegate.dart’;

class SoccerClubsApp extends StatefulWidget {
@override
State<StatefulWidget> createState() => _SoccerClubsAppState();
}

class _SoccerClubsAppState extends State<SoccerClubsApp> {
SoccerClubRouterDelegate soccerClubRouterDelegate = SoccerClubRouterDelegate();
SoccerClubRouteInformationParser soccerClubRouteInformationParser =
SoccerClubRouteInformationParser();

@override
Widget build(BuildContext context) {
return MaterialApp.router(
debugShowCheckedModeBanner: false,
routerDelegate: soccerClubRouterDelegate,
routeInformationParser: soccerClubRouteInformationParser,
);
}
}

 
 

Create Pages for Flutter Navigator

import ‘package:flutter/material.dart’;
import ‘soccer_club.dart’;
import ‘soccer_club_details_page.dart’;
import ‘soccer_club_route_path.dart’;
import ‘soccer_clubs_list_screen.dart’;
import ‘unknown_screen.dart’;

class SoccerClubRouterDelegate extends RouterDelegate<SoccerClubRoutePath>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<SoccerClubRoutePath> {
final GlobalKey<NavigatorState> navigatorKey;

SoccerClub selectedSoccerClub;
bool show404 = false;

List<SoccerClub> listSoccerClubs = [
SoccerClub(‘Real Madrid’, ‘Spain’),
SoccerClub(‘Paris Saint-Germain’, ‘France’),
SoccerClub(‘Manchester City’, ‘England’),
SoccerClub(‘Bayern Munich’, ‘Germany’),
];

SoccerClubRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>();

SoccerClubRoutePath get currentConfiguration {
if (show404) {
return SoccerClubRoutePath.unknown();
}
return selectedSoccerClub == null
? SoccerClubRoutePath.home()
: SoccerClubRoutePath.details(listSoccerClubs.indexOf(selectedSoccerClub));
}

@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
pages: [
MaterialPage(
key: ValueKey(‘SoccerClubsListPage’),
child: SoccerClubsListScreen(
listSoccerClubs: listSoccerClubs,
onTapped: handleSoccerClubTapped,
),
),
if (show404)
MaterialPage(key: ValueKey(‘UnknownPage’), child: UnknownScreen())
else if (selectedSoccerClub != null)
SoccerClubDetailsPage(soccerClub: selectedSoccerClub)
],
onPopPage: (route, result) {
if (!route.didPop(result)) {
return false;
}

selectedSoccerClub = null;
show404 = false;
notifyListeners();

return true;
},
);
}

@override
Future<void> setNewRoutePath(SoccerClubRoutePath path) async {
if (path.isUnknown) {
selectedSoccerClub = null;
show404 = true;
return;
}

if (path.isDetailsPage) {
if (path.id < 0 || path.id > listSoccerClubs.length – 1) {
show404 = true;
return;
}

selectedSoccerClub = listSoccerClubs[path.id];
} else {
selectedSoccerClub = null;
}

show404 = false;
}

void handleSoccerClubTapped(SoccerClub soccerClub) {
selectedSoccerClub = soccerClub;
notifyListeners();
}
}

 
 

Create List Screen

import ‘package:flutter/material.dart’;
import ‘soccer_club.dart’;

class SoccerClubsListScreen extends StatelessWidget {
final List<SoccerClub> listSoccerClubs;
final ValueChanged<SoccerClub> onTapped;

SoccerClubsListScreen({
@required this.listSoccerClubs,
@required this.onTapped,
});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(‘Flutter Navigator 2.0 Sample’),
),
body: ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.black,
),
itemCount: listSoccerClubs.length,
itemBuilder: (context, index) => GestureDetector(
child: Container(
padding: EdgeInsets.all(15),
child: Column(
children: [
Text(listSoccerClubs[index].clubName, style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold,),),
SizedBox(height: 15,),
Text(listSoccerClubs[index].countryName, style: TextStyle(fontSize: 20),),
],
),
),
onTap: () => onTapped(listSoccerClubs[index]),
)),
);
}
}

 
 

Create Details Screen

import ‘package:flutter/material.dart’;
import ‘soccer_club.dart’;
import ‘soccer_club_details_screen.dart’;

class SoccerClubDetailsPage extends Page {
final SoccerClub soccerClub;

SoccerClubDetailsPage({
this.soccerClub,
}) : super(key: ValueKey(soccerClub));

Route createRoute(BuildContext context) {
return MaterialPageRoute(
settings: this,
builder: (BuildContext context) {
return SoccerClubDetailsScreen(soccerClub: soccerClub);
},
);
}
}

 
import ‘package:flutter/material.dart’;
import ‘soccer_club.dart’;

class SoccerClubDetailsScreen extends StatelessWidget {
final SoccerClub soccerClub;

SoccerClubDetailsScreen({
@required this.soccerClub,
});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(child: Text(soccerClub.clubName, style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold))),
);
}
}

 
 

Create Route Path Class for Flutter Navigator

class SoccerClubRoutePath {
final int id;
final bool isUnknown;

SoccerClubRoutePath.home()
: id = null,
isUnknown = false;

SoccerClubRoutePath.details(this.id) : isUnknown = false;

SoccerClubRoutePath.unknown()
: id = null,
isUnknown = true;

bool get isHomePage => id == null;

bool get isDetailsPage => id != null;
}

 
 

Create Route Information Parser Class

import ‘package:flutter/material.dart’;
import ‘soccer_club_route_path.dart’;

class SoccerClubRouteInformationParser extends RouteInformationParser<SoccerClubRoutePath> {
@override
Future<SoccerClubRoutePath> parseRouteInformation(
RouteInformation routeInformation) async {
final uri = Uri.parse(routeInformation.location);

if (uri.pathSegments.length == 0) {
return SoccerClubRoutePath.home();
}

if (uri.pathSegments.length == 2) {
if (uri.pathSegments[0] != ‘soccer_club’) return SoccerClubRoutePath.unknown();
var remaining = uri.pathSegments[1];
var id = int.tryParse(remaining);
if (id == null) return SoccerClubRoutePath.unknown();
return SoccerClubRoutePath.details(id);
}

return SoccerClubRoutePath.unknown();
}

@override
RouteInformation restoreRouteInformation(SoccerClubRoutePath path) {
if (path.isUnknown) {
return RouteInformation(location: ‘/404’);
}
if (path.isHomePage) {
return RouteInformation(location: ‘/’);
}
if (path.isDetailsPage) {
return RouteInformation(location: ‘/soccer_club/${path.id}’);
}
return null;
}
}

 
 
flutter navigator
 
 

3 thoughts on “Flutter Navigator 2.0 with Router

Leave a Reply