0% found this document useful (0 votes)
434 views100 pages

S. Sinha - Beginning Flutter 3.0 With Dart. A Beginner To Pro. Learn How To Build Advanced Flutter Apps (2022) (0401-0500)

The document discusses how to use routes and navigation in Flutter. It explains that routes represent screens or pages and the Navigator widget is used to navigate between them. MaterialPageRoute is used to define routes that push new pages onto the stack. On the first page, tapping a button calls Navigator.push to push the second page route onto the stack. This allows navigating between pages while passing the BuildContext. The back button pops the second route off the stack, returning to the first page. Data can be passed between routes via constructor parameters in the model classes that define the data.

Uploaded by

Michel Müller
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
434 views100 pages

S. Sinha - Beginning Flutter 3.0 With Dart. A Beginner To Pro. Learn How To Build Advanced Flutter Apps (2022) (0401-0500)

The document discusses how to use routes and navigation in Flutter. It explains that routes represent screens or pages and the Navigator widget is used to navigate between them. MaterialPageRoute is used to define routes that push new pages onto the stack. On the first page, tapping a button calls Navigator.push to push the second page route onto the stack. This allows navigating between pages while passing the BuildContext. The back button pops the second route off the stack, returning to the first page. Data can be passed between routes via constructor parameters in the model classes that define the data.

Uploaded by

Michel Müller
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 100

9.

Everything about Flutter Navigation and Route 391

How do you use onGenerateRoute in Flutter?


To show how we use onGenerateRoute in Flutter, we must have two pages. The first page and the
second page.
Consequently, when the app opens up, it takes us to the second page. If we want to get back to the
first page, we just tick the cross mark and it navigates back to the first page.
However, everything starts with MaterialApp. This convenience widget builds upon a WidgetsApp
and it adds material design specific functionalities.

How to use a dynamic initial route?


The MaterialApp design maintains an order to configure the top navigator. Of course, Flutter uses
the home property to decide where to go first.
If we use the home property, it automatically navigates to that page.
Order-wise, next, the route tables are used. It checks whether there is any entry for the route.
Otherwise, it calls onGenerateRoute. In that case, we need to provide the route.

1 import 'dart:ui';
2
3 import 'package:flutter/material.dart';
4
5 class MaterialDesign extends StatelessWidget {
6 const MaterialDesign({Key? key}) : super(key: key);
7
8 @override
9 Widget build(BuildContext context) {
10 return MaterialApp(
11 title: 'Better Flutter - Essential Widgets',
12 home: MDFirstPage(),
13 initialRoute: '/second',
14 onGenerateRoute: _getSecondPageFirst,
15 );
16 }
17
18 Route<dynamic>? _getSecondPageFirst(RouteSettings settings) {
19 if (settings.name != '/second') {
20 return null;
21 }
22
9. Everything about Flutter Navigation and Route 392

23 return MaterialPageRoute<void>(
24 settings: settings,
25 builder: (BuildContext context) => MDSecondPage(),
26 fullscreenDialog: true,
27 );
28 }
29 }

Watch the above code. Although the home property indicates to the first page, the initialRoute
property navigates to the second page.
However, we need to be careful about one thing. It should return a non-null value.
The rest is quite simple. Now we can design our first page and second page.
In any case, the app will open the second page first.
If we want to make this page a log in and registration page, we can design that too.
In addition, if the user doesn’t want to log in or register, she can touch the cross icon. In that case,
the home property comes into effect, opening the first page as usual.

What is Flutter Navigation and how does Flutter


Navigator work?
What is Flutter Navigation? Moreover, what is Flutter Navigator? Are they same? Or, they are
different? Above all, the answer lies in a widget.
Route.
Although Route is a widget, still it actually represents a new screen. Or, a new page.
For example, we navigate to the second route with the help of Navigator.
However, we need to understand the context.
What is the context?
Moreover, why we need it?
Most apps use different screens. And, for that we need router widget.

What is a navigator and routes in Flutter?


To begin with, we’ll see how a navigator and routes work in Flutter.
Firstly, we want a simple example. So the beginners can understand. Moreover, a better flutter
developer must understand how routes work.
9. Everything about Flutter Navigation and Route 393

Secondly, to keep it simple, we navigate from first screen to the second.


Further, we’ll learn how we can handle multiple routes. Above all, how we can pass data while
navigating to a second page.
As we have said, route is a widget. Navigator is also another widget.

How do you deal with navigation in Flutter?


To deal with navigation we need a first page. Right?
As a result, we can move to the second page. And come back. Navigation deals with that.
Let us see our first chunk of code first:

1 import 'package:flutter/material.dart';
2
3 void main() {
4 runApp(const MyApp());
5 }
6
7 class MyApp extends StatelessWidget {
8 const MyApp({Key? key}) : super(key: key);
9
10 // This widget is the root of your application.
11 @override
12 Widget build(BuildContext context) {
13 return MaterialApp(
14 routes: <String, WidgetBuilder>{
15 '/': (BuildContext context) => const HomePage(),
16 },
17 title: 'Flutter Demo',
18 theme: ThemeData(
19 primaryColor: const Color(0xFF3EBACE),
20 backgroundColor: const Color(0xFFF3F5F7),
21 primarySwatch: Colors.indigo,
22 ),
23 );
24 }
25 }

As we can see, the route widget takes us to the Home Page. However, it also carries the context.
Next, we need to go to the second page.
9. Everything about Flutter Navigation and Route 394

How Does Flutter Navigator work?


Therefore, our first page must have that mechanism.

1 class HomePage extends StatelessWidget {


2 const HomePage({Key? key}) : super(key: key);
3
4 @override
5 Widget build(BuildContext context) {
6 return Scaffold(
7 appBar: AppBar(
8 title: const Text('First Page'),
9 ),
10 body: GestureDetector(
11 onTap: () {
12 Navigator.push(
13 context,
14 MaterialPageRoute(builder: (context) => const SecondPage()),
15 );
16 },
17 child: Container(
18 margin: const EdgeInsets.all(10.0),
19 padding: const EdgeInsets.all(10.0),
20 child: ClipRRect(
21 borderRadius: const BorderRadius.only(
22 topLeft: Radius.circular(15.0),
23 topRight: Radius.circular(15.0),
24 bottomLeft: Radius.circular(15.0),
25 bottomRight: Radius.circular(15.0),
26 ),
27 child: Image.network(
28 'https://sanjibsinha.com/wp-content/uploads/2021/07/Can-you-code-in-\
29 WordPress-How-do-I-learn-WordPress-coding-.jpg'),
30 ),
31 ),
32 ),
33 );
34 }
35 }

The on tap method in the above code shows an exemplary behavior.


Why?
9. Everything about Flutter Navigation and Route 395

Because, it uses the Navigator widget push method. And after that, it passes the same context.

How Does Flutter Navigator push work?


At the first page, if we we can use the Gesture Detector, we can now tap the image. And that takes
us to the second page.
Although we’ve not used Navigator pop method in the second page, yet we can come back to the
first page using the back button in the App Bar.

1 class SecondPage extends StatelessWidget {


2 const SecondPage({Key? key}) : super(key: key);
3
4 @override
5 Widget build(BuildContext context) {
6 return Scaffold(
7 appBar: AppBar(
8 title: const Text('Second Page'),
9 ),
10 body: Center(
11 child: Container(
12 margin: const EdgeInsets.all(10.0),
13 padding: const EdgeInsets.all(10.0),
14 child: ClipRRect(
15 borderRadius: const BorderRadius.only(
16 topLeft: Radius.circular(15.0),
17 topRight: Radius.circular(15.0),
18 bottomLeft: Radius.circular(15.0),
19 bottomRight: Radius.circular(15.0),
20 ),
21 child: Image.network(
22 'https://sanjibsinha.com/wp-content/uploads/2021/06/What-is-toList-f\
23 lutter-What-is-map-in-Dart-.jpg'),
24 ),
25 ),
26 ),
27 );
28 }
29 }
9. Everything about Flutter Navigation and Route 396

How do you pass data from one class to another in


flutter?
Passing data from one class to another in Flutter is not difficult. However, we need to understand
the basic concepts of route and navigator first.
Route is a widget. So the Navigator. Moreover, for passing data we need to use these two widgets.
With the help of these two widgets we can pass data using class constructors in Flutter.
To use class constructors we first need model classes and dummy data. Because the data flow from
models and we can use them to display on the screen.
Certainly, to do that we also need the help of controller.

How do you use route in flutter?


To use route widget we have to return material app widget where we define the screens or pages.
Suppose on the first page we have a bunch of categories. And clicking those categories take us to
the detail page.
Therefore, at a first glance, it gives us all categories.
The above image displays categories of different type of news. If we click any one of the category,
it will take us to the detail page.
To achieve this, in model folder we have category class.

1 import 'package:flutter/material.dart';
2
3 class Category {
4 final String id;
5 final String title;
6 final Color color;
7
8 const Category({
9 required this.id,
10 required this.title,
11 this.color = Colors.orangeAccent,
12 });
13 }
14 And with the category class, we need some dummy category data like the following cod\
15 e.
16
17 import 'package:flutter/material.dart';
9. Everything about Flutter Navigation and Route 397

18
19 import 'category.dart';
20
21 const DUMMY_CATEGORIES = const [
22 Category(
23 id: 'c1',
24 title: 'Health',
25 color: Colors.red,
26 ),
27 Category(
28 id: 'c2',
29 title: 'Wellness',
30 color: Colors.deepOrange,
31 ),
32 Category(
33 id: 'c3',
34 title: 'Politics',
35 color: Colors.black54,
36 ),
37 Category(
38 id: 'c4',
39 title: 'Travel',
40 color: Colors.green,
41 ),
42 Category(
43 id: 'c5',
44 title: 'Internet',
45 color: Colors.yellow,
46 ),
47 Category(
48 id: 'c6',
49 title: 'Lifestyle',
50 color: Colors.indigo,
51 ),
52 Category(
53 id: 'c7',
54 title: 'Headlines',
55 color: Colors.pink,
56 ),
57 Category(
58 id: 'c8',
59 title: 'Sports',
60 color: Colors.orange,
9. Everything about Flutter Navigation and Route 398

61 ),
62 Category(
63 id: 'c9',
64 title: 'Science',
65 color: Colors.blueAccent,
66 ),
67 Category(
68 id: 'c10',
69 title: 'Environemnt',
70 color: Colors.redAccent,
71 ),
72 ];

To display these categories in the opening page, we need route widget which defines the navigation
pattern.

How do you get the current route in Flutter?


To get the current route we need to return material app widget.

1 class FirstPage extends StatelessWidget {


2 const FirstPage({Key? key}) : super(key: key);
3
4 @override
5 Widget build(BuildContext context) {
6 return MaterialApp(
7 title: 'Routing test',
8 debugShowCheckedModeBanner: false,
9 initialRoute: '/',
10 routes: {
11 '/': (context) => const FirstPageBody(),
12 '/categories': (context) => SecondPage(),
13 },
14 );
15 }
16 }

Since the related code is too long, please visit the respective GitHub repository. Above all this
repository also connects you to the book Better Flutter in Leanpub.
This repository will also give an idea how from the current route we move to the detail page.
As we can see that the current route takes us to the detail page where the data are coming from the
dummy News class.
If we take a look at the News class, we will understand how it works.
9. Everything about Flutter Navigation and Route 399

1 enum Nature {
2 hard,
3 soft,
4 }
5
6 class News {
7 final String id;
8 final List<String> categories;
9 final String title;
10 final String detail;
11 final String imageURL;
12 final Nature nature;
13
14 const News({
15 required this.id,
16 required this.categories,
17 required this.title,
18 required this.detail,
19 required this.imageURL,
20 required this.nature,
21 });
22 }

At the same time, we pass the data from page one to the second page like this:

1 body: GridView(
2 gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
3 maxCrossAxisExtent: 200,
4 crossAxisSpacing: 20.0,
5 mainAxisSpacing: 20.0,
6 ),
7 children: DUMMY_CATEGORIES.map(
8 (e) {
9 return AllCategories(
10 id: e.id,
11 title: e.title,
12 color: e.color,
13 );
14 },
15 ).toList(),
16 ),
17
18 // code is incomplete for brevity, please consult the GitHub repository
9. Everything about Flutter Navigation and Route 400

How do you pass context in flutter?


We pass the whole data as a part of context. Because we have defined route where the anonymous
function takes the parameter context

1 routes: {
2 '/': (context) => const FirstPageBody(),
3 '/categories': (context) => SecondPage(),
4 },

As a result it becomes easier for us to catch the data in the second page.

1 Widget build(BuildContext context) {


2 final Map arguments = ModalRoute.of(context)!.settings.arguments as Map;
3 final id = arguments['id'];
4 final title = arguments['title'];
5 final color = arguments['color'];
6 final categoryBooks = DUMMY_NEWS.where((book) {
7 return book.categories.contains(id);
8 }).toList();
9
10 // code is incomplete, please consult respective GitHub repository

To get the data the modal route class uses a static method “of” that passes the context. And the
context also defines the location of this widget in the widget tree.
In the coming flutter tutorials we’ll discuss more about passing data through route and context.

What is enum in Dart flutter? How to use enum in


Flutter?
To use enum in Dart or Flutter is not a difficult task. However, before we use Enumerated Types or
enum, we need to understand it.
Dart added enum as a feature in 1.8 release. As a result, Flutter also starts using enum.
Since we have been developing a News app, in the previous post we’ve learned how to pass data
through class using route and navigator widgets.
Before that, we’ve also seen the basic route and navigation.
Subsequently, we’re going to build the News app and this time we’ll use enum as a special type.
We’ll also learn how to pass enum along with other data while we navigate to another page.
9. Everything about Flutter Navigation and Route 401

How do you use Enums in Navigation?


As we have just said, Enums are a list of constant values that we can use either as a numeric data,
or String data.
If you’ve already read the previous posts you’re aware that the full code is available in my GitHub
repository.
As we’ve progressed in building the News app, we have seen that the front page displays categories
of News Items.
Clicking any News item takes us to the detail screen or page. To accomplish this, we have two
different classes. Category and News. Also we have a set of dummy data.
However, there is no enum then. Up to that part in our class we have not added any Enums. As a
result it has not displayed what kind of story is this.
This story could be an event based hard news story. Or, it could be a soft News story that people
might save and read later.
Our Enums will serve that purpose. So we add that to the News class first.

1 enum Nature {
2 hard,
3 soft,
4 }
5
6 class News {
7 final String id;
8 final List<String> categories;
9 final String title;
10 final String detail;
11 final String imageURL;
12 final Nature nature;
13
14 const News({
15 required this.id,
16 required this.categories,
17 required this.title,
18 required this.detail,
19 required this.imageURL,
20 required this.nature,
21 });
22 }

Next, we’ll use the Enums in the display page as string data. To use Enums as string data we need
to use switch case.
9. Everything about Flutter Navigation and Route 402

1 class SecondPage extends StatelessWidget {


2 final int id;
3 final String title;
4 final Color color;
5 final Nature nature;
6
7 const SecondPage({
8 Key? key,
9 required this.id,
10 required this.title,
11 required this.color,
12 required this.nature,
13 }) : super(key: key);
14
15 String get natureText {
16 switch (nature) {
17 case Nature.hard:
18 return 'Event based Latest Hard News >>';
19 case Nature.soft:
20 return 'Take time and read Soft News Story >>';
21 default:
22 return 'Unknown';
23 }
24 }
25
26 // code is incomplete, for full code please visit GitHub repository

Now, at the top of the display page, we can use that Enums like the following images.

How do you find the enum value in flutter?


Finding enum value is not difficult either. As we’ve seen the switch case, each news story displays
the nature. So the reader will at a glimpse know whether she should read it now or save it to read
later.
As you can see, according to the id of the News, it’s an event based latest hard news. However, for
any other story this nature might change.
Let’s take a look at the above image that belongs to the Enums of soft news story.
Certainly, to get the nature of the story we’ve used enum. But, to display it as a String data we need
to pass it.
Moreover, we need to place it over the image as Text widget.
9. Everything about Flutter Navigation and Route 403

1 body: ListView.builder(
2 itemBuilder: (context, index) {
3 return Column(
4 children: [
5 Container(
6 margin: const EdgeInsets.all(10.0),
7 padding: const EdgeInsets.all(10.0),
8 child: Text(
9 natureText,
10 style: const TextStyle(
11 fontSize: 20.0,
12 ),
13 ),
14 ),
15
16 // code is incomplete for brevity

Hopefully, this makes sense. Moreover, we’ll not stop here. We’ll build the News app so that it looks
more attractive than this.
That means, we’ll design our Flutter News app better than this one.
And at the same time, to pass data we’ll use route and navigation in a different way.

How do you change the theme on Flutter?


To change the theme on Flutter we must do it at the root level. The root level means when we create
the Material App, we need to define the custom theme.
However, it’s not easy. Because Flutter takes care of the theme. Since in Flutter everything is widget,
the custom theme also belongs to the same concept.
To change the theme, we need to reflect it and display the new look on the screen.
With reference to that , we’ve already written about the route and navigation before. We’ve also
shown how we can pass data from one class to another without changing its theme.
Finally, to maintain the same theme throughout the whole Flutter app, we’ve used enum to add more
characteristic to it.
And our Flutter app is almost ready.
Can we change this look further? Certainly, to change the look we must adhere to a custom theme.
As a result, in our main dart file, we need to declare the custom theme while creating the Material
App.
9. Everything about Flutter Navigation and Route 404

1 theme: ThemeData(
2 primarySwatch: Colors.pink,
3 primaryColor: Colors.amber,
4 canvasColor: const Color.fromRGBO(255, 254, 229, 1),
5 fontFamily: 'Raleway',
6 textTheme: ThemeData.light().textTheme.copyWith(
7 bodyText2: const TextStyle(
8 color: Color.fromRGBO(20, 51, 51, 1),
9 ),
10 bodyText1: const TextStyle(
11 color: Color.fromRGBO(20, 51, 51, 1),
12 ),
13 headline6: const TextStyle(
14 fontSize: 20,
15 fontFamily: 'RobotoCondensed',
16 fontWeight: FontWeight.bold,
17 )),
18 ),

What is theme Flutter?


As we know, themes are an integral part of User Interface or UI of any flutter app. To make the app
more presentable, we need to design the fonts and colors.
Above all, we need to define them at the Theme Data widget. As we’ve done in the above code.
Now the change reflects on our flutter app’s look.
Our News app looks different due to the change in the custom theme. In the next article we’ll see
how we can develop our flutter app with the help of this new theme.

How do you name a route in Flutter?


To name a route in Flutter we need to have two screens. Moreover, we need to navigate from one
screen to another.
That is the basic design of navigation in Flutter. To go from one screen to other, we use Navigator
widget. And we also use a static method push Named.
Subsequently, to come back to the first screen, we use the Navigator widget again. To make it
complete we use pop method.
However, we can name a route in a different way too. To do that, we need to declare it as a constant
static property inside that class.
9. Everything about Flutter Navigation and Route 405

For brevity, we cannot display the full code snippet that involves route name and passing arguments.
Please visit the respective code repository in GitHub.

1 class CategoryNewsScreen extends StatelessWidget {


2 static const routeName = '/category-news';
3
4 // code is incomplete

How do we can use this route name? To do that we need to use the route widget in our Material
App widget.

1 initialRoute: '/', // default is '/'


2 routes: {
3 '/': (ctx) => const CategoriesScreen(),
4 CategoryNewsScreen.routeName: (ctx) => const CategoryNewsScreen(),
5 NewsDetailScreen.routeName: (ctx) => const NewsDetailScreen(),
6 },

In case, our route name doesn’t work, we can also use a fallback.

1 onUnknownRoute: (settings) {
2 return MaterialPageRoute(
3 builder: (ctx) => const CategoriesScreen(),
4 );
5 },

As a result, if we cannot go the Categories Detail Screen page, and our route name fails, it always
stays in the Home page.
In our News App we can display every category using a dummy data.

How do you pass arguments to named route in Flutter?


To pass arguments to named route in Flutter is not difficult either. Because, we can get the arguments
at the page where we have declared our static route name property.
9. Everything about Flutter Navigation and Route 406

1 class CategoryNewsScreen extends StatelessWidget {


2 static const routeName = '/category-news';
3
4 const CategoryNewsScreen({Key? key}) : super(key: key);
5
6 @override
7 Widget build(BuildContext context) {
8 final routeArgs =
9 ModalRoute.of(context)!.settings.arguments as Map<String, String>;
10 final categoryTitle = routeArgs['title'];
11 final categoryId = routeArgs['id'];
12 final categoryNews = dummyNews.where((news) {
13 return news.categories.contains(categoryId);
14 }).toList();
15 return Scaffold(
16 appBar: AppBar(
17 title: Text(categoryTitle!),
18 ),
19
20 // code is incomplete for brevity

In the updated Flutter version, the null checking is mandatory. So keep that in mind. We need to
add an exclamatory sign after the Modal route of method that passes the context.
As a result, whenever we click any category, it takes us to static name of route that we’ve defined
inside the class.
As we can see, in the Health category, we have two news items. If we have a look at the dummy
data, we’ll find that.

1 const dummyNews = [
2 News(
3 id: 'b1',
4 categories: [
5 'c1',
6 'c4',
7 ],
8 title: 'Global Worming fuels disaster',
9
10 ....
11
12 News(
13 id: 'b5',
14 categories: [
9. Everything about Flutter Navigation and Route 407

15 'c1',
16 'c5',
17 ],
18 title: 'Take more outdoor walks',
19
20 // code is incomplete

As in two news items we find this category, therefore, it displays them on the category news screen.
However, to display the route items in the right manner, we take help from a controller.

1 class CategoryItem extends StatelessWidget {


2 final String id;
3 final String title;
4 final Color color;
5
6 const CategoryItem({
7 required this.id,
8 required this.title,
9 required this.color,
10 });
11
12 void selectCategory(BuildContext ctx) {
13 Navigator.of(ctx).pushNamed(
14 CategoryNewsScreen.routeName,
15 arguments: {
16 'id': id,
17 'title': title,
18 },
19 );
20 }
21
22 // code is incomplete

As a matter of fact, we define the select category method here and push the route name and
arguments here.
To make the whole News App complete we need to do many more things. Consequently that also
makes the route name and passing arguments process complete.
9. Everything about Flutter Navigation and Route 408

How do you pass data from one screen to another in


flutter?
How we can pass data from one screen to another in Flutter? How we can manage this complex
process?
Moreover, what concepts we should understand before we proceed?
Firstly, we need route widget. Secondly we need Navigator widget. And finally, we need to know
how list and map work in Flutter.
Therefore let us make these points clear at the beginning.

1 Route
2 Navigator
3 List and Map

Where should we mention the route? At our Material App widget. The route indicates where we
want to go from the home screen or page.
Not only that, when we move from one screen to another, we’ll also pass the data. So, in the second
screen, we display that data.
Above all, when we go from the home screen, we must use a method that will send us along with
the data. And based on that data we can display other data on the second screen.
To explain the whole process of passing data from one screen to another, we need to show some
code. However, for brevity, we cannot show the full code.
For the full code snippets, please visit the respective GitHub repository.

1 void main() => runApp(const NewsApp());

In the News App stateless widget we define our Material App widget where we use the route widget.

1 class NewsApp extends StatelessWidget {


2 const NewsApp({Key? key}) : super(key: key);
3
4 @override
5 Widget build(BuildContext context) {
6 return MaterialApp(
7 title: 'Daily News',
8 theme: ThemeData(
9 primarySwatch: Colors.pink,
10 primaryColor: Colors.amber,
9. Everything about Flutter Navigation and Route 409

11 canvasColor: const Color.fromRGBO(255, 254, 229, 1),


12 textTheme: ThemeData.light().textTheme.copyWith(
13 bodyText2: const TextStyle(
14 color: Color.fromRGBO(20, 51, 51, 1),
15 ),
16 bodyText1: const TextStyle(
17 color: Color.fromRGBO(20, 51, 51, 1),
18 ),
19 headline6: const TextStyle(
20 fontSize: 20,
21 fontWeight: FontWeight.bold,
22 )),
23 ),
24 // home: CategoriesScreen(),
25 initialRoute: '/', // default is '/'
26 routes: {
27 '/': (ctx) => const CategoriesScreen(),
28 CategoryNewsScreen.routeName: (ctx) => const CategoryNewsScreen(),
29 NewsDetailScreen.routeName: (ctx) => const NewsDetailScreen(),
30 },
31
32 onUnknownRoute: (settings) {
33 return MaterialPageRoute(
34 builder: (ctx) => const CategoriesScreen(),
35 );
36 },
37 );
38 }
39 }

As we can see, the initial route is Categories Screen stateless widget. This widget must return some
data. Here all the categories of the News Items like Health, Politics, Internet and many more.
To get that data we need Category class and dummy data in our model folder. Please visit the GitHub
repository to have an idea.

How do you pass data through navigation?


Based on that dummy categories data, now we can return another widget in the Categories Screen
widget.
9. Everything about Flutter Navigation and Route 410

1 body: GridView(
2 padding: const EdgeInsets.all(25),
3
4 /// the first page displays CategoryItem controller
5 children: dummyCategories
6 .map(
7 (catData) => CategoryItem(
8 id: catData.id,
9 title: catData.title,
10 color: catData.color,
11 ),
12 )
13 .toList(),
14
15 ...
16 // code is not complete

The dummy categories are a few constant data where we’ve defined the id, title and the color of the
category.

1 const dummyCategories = [
2 Category(
3 id: 'c1',
4 title: 'Health',
5 color: Colors.red,
6 ),
7 Category(
8 id: 'c2',
9 title: 'Wellness',
10 color: Colors.deepOrange,
11 ),
12 ....
13 // code is not complete

Actually, the Category Item widget will help to display all the categories and at the same time it will
let us allow to click each category to see what kind of news items belong to that category.
9. Everything about Flutter Navigation and Route 411

1 class CategoryItem extends StatelessWidget {


2 final String id;
3 final String title;
4 final Color color;
5
6 const CategoryItem({
7 Key? key,
8 required this.id,
9 required this.title,
10 required this.color,
11 }) : super(key: key);
12
13 void selectCategory(BuildContext ctx) {
14 Navigator.of(ctx).pushNamed(
15 CategoryNewsScreen.routeName,
16 arguments: {
17 'id': id,
18 'title': title,
19 },
20 );
21 }
22 ...
23 // code is not complete

We’ve passed all data through the Class Constructor and then we define a method select Category.
Inside that method, Navigator widget uses a chain of static methods through which we pass the
context and a list of arguments.
Because of this widget we can now see all the categories.
However, if we click any category the method fires and push the Navigator to another stateless
widget Category News Screen.
Let us see a few part of Category News Screen widget code.
9. Everything about Flutter Navigation and Route 412

1 Widget build(BuildContext context) {


2 final routeArgs =
3 ModalRoute.of(context)!.settings.arguments as Map<String, String>;
4 final categoryTitle = routeArgs['title'];
5 final categoryId = routeArgs['id'];
6 final categoryNews = dummyNews.where((news) {
7 return news.categories.contains(categoryId);
8 }).toList();
9
10 ...
11 // code is not complete

We’ve already sent the arguments as a List. Where we have sent ID and Title of Category class and
dummy category data.
Now according to the News class and dummy data we can now show the News item that belongs
to that category.
Now we can click the title or the special enum to view the News in detail.

How do you make a Flutter app from scratch?


To make a flutter app from scratch is easy. But it depends on the perspective. As long as we build a
small app it’s really easy.
However, it becomes difficult if we want more features.
What kind of features need more skill? In fact, there are plenty.
Firstly, the design part. The User Interface should look good. Moreover, it must be user friendly.
Secondly, we need to maintain state. The state remains active between different screen. It involves
navigation and route widgets.
However, we cannot allow more widget rebuilds. So we should maintain state in an economic way.
So the Flutter app speeds up while it runs.
Furthermore, there is Flutter data structures. List and Map.
In my opinion, this is the most difficult part of any Flutter app that we build from scratch.
In our previous post we’ve seen how we can build a News App that runs locally. We provide dummy
data and pass them through Class Constructor.
For building such Flutter App please visit the respective GitHub repository.
Next, we click any category and reach the News Item that belongs to that Category.
Now we wan to see the News detail page.
9. Everything about Flutter Navigation and Route 413

How do you start learning Flutter from scratch?


To learn Flutter from scratch is another thing. But building an app like this involves some Flutter
skill and knowledge about Dart object oriented programming.
Moreover, we need take a close look at the Flutter data structures that mostly use List and Map.
To sum up, in this News App, we see this page because we have a stateless widget Category News
Screen.

1 class CategoryNewsScreen extends StatelessWidget {


2 static const routeName = '/category-news';
3
4 const CategoryNewsScreen({Key? key}) : super(key: key);
5
6 /// returns NewsItem controller
7 @override
8 Widget build(BuildContext context) {
9 final routeArgs =
10 ModalRoute.of(context)!.settings.arguments as Map<String, String>;
11 final categoryTitle = routeArgs['title'];
12 final categoryId = routeArgs['id'];
13 final categoryNews = dummyNews.where((news) {
14 return news.categories.contains(categoryId);
15 }).toList();
16 return Scaffold(
17 appBar: AppBar(
18 title: Text(categoryTitle!),
19 ),
20 body: ListView.builder(
21 itemBuilder: (ctx, index) {
22 return NewsItem(
23 id: categoryNews[index].id,
24 title: categoryNews[index].title,
25 imageUrl: categoryNews[index].imageURL,
26 nature: categoryNews[index].nature,
27 );
28 },
29 itemCount: categoryNews.length,
30 ),
31 );
32 }
33 }
9. Everything about Flutter Navigation and Route 414

The above widget again returns a controller called News Item.


Now this controller or widget again displays the second image inside a Column widget.
As a children we get the respective image, title and the enum that we’ve defined already in the
dummy data.

1 child: Column(
2 children: <Widget>[
3 Stack(
4 children: <Widget>[
5 ClipRRect(
6 borderRadius: const BorderRadius.only(
7 topLeft: Radius.circular(15),
8 topRight: Radius.circular(15),
9 ),
10 child: Image.network(
11 imageUrl,
12 height: 250,
13 width: double.infinity,
14 fit: BoxFit.cover,
15 ),
16 ),
17 Positioned(
18 bottom: 20,
19 right: 10,
20 child: Container(
21 width: 300,
22 color: Colors.black54,
23 padding: const EdgeInsets.symmetric(
24 vertical: 5,
25 horizontal: 20,
26 ),
27 child: Text(
28 title,
29 style: const TextStyle(
30 fontSize: 26,
31 color: Colors.white,
32 ),
33 softWrap: true,
34 overflow: TextOverflow.fade,
35 ),
36 ),
37 )
9. Everything about Flutter Navigation and Route 415

38 ],
39 ),
40 Padding(
41 padding: const EdgeInsets.all(20),
42 child: Row(
43 mainAxisAlignment: MainAxisAlignment.spaceAround,
44 children: <Widget>[
45 Row(
46 children: <Widget>[
47 const Icon(
48 Icons.work,
49 ),
50 const SizedBox(
51 width: 6,
52 ),
53 Text(natureText),
54 ],
55 ),
56 ],
57 ),
58 ),
59 ],
60 ),

However, there is still a feature lacks in this Flutter app.


What is that?
We must be able to click this News Item to get the detail.
One can manage that only with a method that would push the arguments to a new screen, where
we can display the detail of the News.
Moreover, we need to know the role of route and Navigator widget.
Because that deals with the List and Map and finally sends the correct data.
9. Everything about Flutter Navigation and Route 416

1 void selectNews(BuildContext context) {


2 Navigator.of(context).pushNamed(
3 NewsDetailScreen.routeName,
4 arguments: id,
5 );
6 }
7
8 @override
9 Widget build(BuildContext context) {
10 return InkWell(
11 onTap: () => selectNews(context),
12
13 ...

The above method with the help of “on Tap” void function return the “select News” method with
the context as its parameter.

Is Flutter Easy to Learn?


What do you think now? Is Flutter easy to learn?
Like any software toolkit Flutter also needs constant practice and a fair grasp of basic conceptions.
The above select News method in the News Item widget, actually sends us to another screen. News
Detail Screen.
In that screen, we catch the data and display the detail of the News Item correctly.

1 class NewsDetailScreen extends StatelessWidget {


2 static const routeName = '/news-detail';
3
4 const NewsDetailScreen({Key? key}) : super(key: key);
5
6 Widget buildSectionTitle(BuildContext context, String text) {
7 return Container(
8 margin: const EdgeInsets.symmetric(vertical: 10),
9 child: Text(
10 text,
11 style: Theme.of(context).textTheme.headline6,
12 ),
13 );
14 }
15
16 Widget buildContainer(Widget child) {
9. Everything about Flutter Navigation and Route 417

17 return Container(
18 decoration: BoxDecoration(
19 color: Colors.white,
20 border: Border.all(color: Colors.grey),
21 borderRadius: BorderRadius.circular(10),
22 ),
23 margin: const EdgeInsets.all(10),
24 padding: const EdgeInsets.all(10),
25 height: 150,
26 width: 300,
27 child: child,
28 );
29 }
30
31 @override
32 Widget build(BuildContext context) {
33 final mealId = ModalRoute.of(context)!.settings.arguments as String;
34 final selectedNews = dummyNews.firstWhere((meal) => meal.id == mealId);
35 return Scaffold(
36 appBar: AppBar(
37 title: Text(selectedNews.title),
38 ),
39 body: SingleChildScrollView(
40 child: Column(
41 children: <Widget>[
42 SizedBox(
43 height: 300,
44 width: double.infinity,
45 child: Image.network(
46 selectedNews.imageURL,
47 fit: BoxFit.cover,
48 ),
49 ),
50 buildSectionTitle(context, 'News Detail'),
51 Text(selectedNews.detail),
52 ],
53 ),
54 ),
55 );
56 }
57 }
10. More on Flutter UI, List, Map, and
Provider Best Practices
In this chapter we’ll take a close look at everything we’ve learned so far. However, we must remember
one thing, Flutter changes with the time.
The upgraded Flutter comes with more features and at the same time, discards many faetures that
we’ve used before.
Consequently, we’re trying to evolve our knowledge with the time.

How do you use decoration in a container in Flutter?


To have a look at the screenshots we’ve used in this section, please visit this LINK²³
We always want our flutter app to look not only better, but also unique. To accomplish that, we can
use decorations in a container in Flutter.
Container widget is one of the main layout widgets in flutter, however, it has many properties that
can change its look entirely. One of such properties is decoration.
The decoration property returns another class BoxDecoration.
What does it do?
The BoxDecoration class helps us with variety of ways to draw a box. As a result, we can use
decorations to change the look of a container.
We’ve built our flutter News App, and that works fine. Meanwhile we can check how it looks like.

How have we built a flutter app from scratch


The above image follows a simple decoration strategy. The code is simple.

²³https://sanjibsinha.com/decoration-in-a-container-in-flutter/
10. More on Flutter UI, List, Map, and Provider Best Practices 419

1 decoration: BoxDecoration(
2 gradient: LinearGradient(
3 colors: [
4 color.withOpacity(0.7),
5 color,
6 ],
7 begin: Alignment.topLeft,
8 end: Alignment.bottomRight,
9 ),
10 borderRadius: BorderRadius.circular(15),
11 ...
12 // the code is incomplete for brevity

How do you customize a container in Flutter?


We can customize a container widget in flutter in many ways.
There are many properties that Container widget supplies us to give it an unique impression.
How about the following look?
With some slight tweak in decorations, we can completely change the look of the containers that
display the news categories on the front page of our News App,

1 How do you color a container in Flutter?


2 Not only we will color this container, but we also add some more properties.

How do you outline a container in Flutter?


To change our News App front page completely, we not only outline each container, but also use
some features available in Flutter.
Let us go through the code snippet that are particularly responsible for this make-over.
10. More on Flutter UI, List, Map, and Provider Best Practices 420

1 Container(
2 padding: const EdgeInsets.all(15),
3 child: Text(
4 title,
5 style: Theme.of(context).textTheme.headline6,
6 ),
7 /* decoration: BoxDecoration(
8 gradient: LinearGradient(
9 colors: [
10 color.withOpacity(0.7),
11 color,
12 ],
13 begin: Alignment.topLeft,
14 end: Alignment.bottomRight,
15 ),
16 borderRadius: BorderRadius.circular(15),
17 ), */
18 alignment: Alignment.center,
19 //color: Colors.blue,
20 // we can adjust width and height
21 // to do that we need to commented out the constraints
22 width: 350.00,
23 height: 350.00,
24 // to skew the container
25 // transform: Matrix4.rotationZ(0.1),
26 decoration: BoxDecoration(
27 color: Colors.blue,
28 border: Border.all(
29 color: Colors.red,
30 width: 2.0,
31 style: BorderStyle.solid,
32 ),
33 borderRadius: const BorderRadius.all(Radius.circular(40.0)),
34 boxShadow: const [
35 BoxShadow(
36 color: Colors.black54,
37 blurRadius: 20.0,
38 spreadRadius: 20.0,
39 ),
40 ],
41 gradient: const LinearGradient(
42 begin: Alignment.centerLeft,
43 end: Alignment.centerRight,
10. More on Flutter UI, List, Map, and Provider Best Practices 421

44 colors: [
45 Colors.red,
46 Colors.white,
47 ],
48 ),
49 ),
50 ),
51 ...
52 // the code is incomplete for brevity

To outline, we’ve used border property and supply the color outline, which is red.
In the same vein, we’ve also modified gradient property, changed the box shadow and used border
radius.
However, we’ve commented out the transform property only.
Why?
Because we will use that to tweak the look later.

What is a Transform in Flutter?


Transform is a widget in flutter. It does many thing.
It changes the shape, size, position and orientation.
Only one line of code inside the Container widget has made it possible.

1 transform: Matrix4.rotationZ(0.1),

You can read more about the transform class and widget in Flutter documentation.

What is a RichText in flutter?


In Flutter, RichText is a widget that helps us to add more styling to a paragraph of text. Otherwise
we cannot do that.
However, with Text widget we cannot achieve the same effect.
In RichText widget, we can return several TextSpan widgets and, moreover, in each TextSpan we
can add different type of styling.
Not only that, the TextSpan is a subclass of InLineSpan class that is actually an immutable span of
inline content, or text that forms a part of a paragraph.
10. More on Flutter UI, List, Map, and Provider Best Practices 422

In addition, the TextSpan widget has a Gesture Detector property called recognizer. Using recognizer
we can easily build a navigation link in that particular part of paragraph.
In short, RichText widget gives us multiple advantages to style a paragraph of Text.
How we can do that?
We’ll see in a minute.

How do you add RichText on flutter?


Before we add RichText on Flutter, let’s revisit our News App, where we last decorated our categories
screen.
However, the Favorites page remained the same. We didn’t change the styling after that.
Now, we can change this Favorites screen completely by adding the RichText widget and several
TextSpan widgets.
Not only that, we’ll also display navigation link inside the paragraph, so that users can click or tap
on the link and go to another page.
The code snippet of this page now looks like the following. We’ve already learned that a RichText
widget can return a children of TextSpan widgets.

1 import 'package:flutter/gestures.dart';
2 import 'package:flutter/material.dart';
3 import 'package:news_app_extended/views/local_news.dart';
4
5 class FavoritesScreen extends StatelessWidget {
6 const FavoritesScreen({Key? key}) : super(key: key);
7
8 @override
9 Widget build(BuildContext context) {
10 return _richTextController(context);
11 }
12 }
13
14 Widget _richTextController(BuildContext context) => Container(
15 color: Colors.black12,
16 padding: const EdgeInsets.all(10),
17 child: Center(
18 child: RichText(
19 text: TextSpan(
20 text: 'This News App is inspired by the principle of free'
21 ' Journalism. You can select ',
10. More on Flutter UI, List, Map, and Provider Best Practices 423

22 style: const TextStyle(


23 color: Colors.black,
24 fontSize: 20,
25 fontWeight: FontWeight.bold,
26 ),
27 children: [
28 TextSpan(
29 text: 'Local',
30 style: const TextStyle(
31 color: Colors.deepOrange,
32 fontSize: 20,
33 fontWeight: FontWeight.bold,
34 decoration: TextDecoration.underline,
35 ),
36 recognizer: TapGestureRecognizer()
37 ..onTap = () {
38 Navigator.of(context).pushNamed(LocalNews.routeName);
39 },
40 ),
41 const TextSpan(
42 text: ' to ',
43 style: TextStyle(
44 color: Colors.grey,
45 fontSize: 20,
46 fontWeight: FontWeight.bold,
47 ),
48 ),
49 TextSpan(
50 text: 'Global,',
51 style: const TextStyle(
52 color: Colors.deepOrange,
53 fontSize: 20,
54 fontWeight: FontWeight.bold,
55 decoration: TextDecoration.underline,
56 ),
57 recognizer: TapGestureRecognizer()
58 ..onTap = () {
59 //
60 },
61 ),
62 const TextSpan(
63 text: ' news, and not only that, you can take part as a'
64 ' Citizen Journalist to publish your story.',
10. More on Flutter UI, List, Map, and Provider Best Practices 424

65 style: TextStyle(
66 color: Colors.green,
67 fontSize: 20,
68 fontWeight: FontWeight.bold,
69 ),
70 )
71 ],
72 ),
73 ),
74 ),
75 );
76
77 // Although we have the full code snippet of Favorites screen, but to understand the\
78 architecture of News App, please visit the respective GitHub repository

If we don’t get a glimpse of this page, we cannot understand how RichText widget helps us to add
styling to a whole paragraph of text.

How do you span text in flutter?


We not only span the text of the screen but with the help of TextSpan widget, we’ve added several
functionality.
In addition, we’ve changed the background color, a part of the paragraph is in black and the other
is in green.
We’ve also changed the navigation color, adding an underline.

1 TextSpan(
2 text: 'Local',
3 style: const TextStyle(
4 color: Colors.deepOrange,
5 fontSize: 20,
6 fontWeight: FontWeight.bold,
7 decoration: TextDecoration.underline,
8 ),
9 recognizer: TapGestureRecognizer()
10 ..onTap = () {
11 Navigator.of(context).pushNamed(LocalNews.routeName);
12 },
13 ),

The above code snippet allows us to add the navigation link to another page, or screen.
10. More on Flutter UI, List, Map, and Provider Best Practices 425

As an example, we’ve only created a Local News Page, where we can navigate from this page.
To do that, we have added a route name in Material Design page first.

1 routes: {
2 '/': (ctx) => const TabsScreen(),
3 CategoryNewsScreen.routeName: (ctx) => const CategoryNewsScreen(),
4 NewsDetailScreen.routeName: (ctx) => const NewsDetailScreen(),
5 LocalNews.routeName: (ctx) => const LocalNews(),
6 },

After that, we’ve added a static route name property in the Local News screen widget.

How do you use TapGestureRecognizer flutter?


In fact, without the TapGestureRecognizer class we cannot navigate to the Local News page.
Isn’t it?

1 recognizer: TapGestureRecognizer()
2 ..onTap = () {
3 Navigator.of(context).pushNamed(LocalNews.routeName);
4 },

Now we can tap the link and the TapGestureRecognizer handles the event.
And, finally we reach to a new screen.
To sum up, with the help of RichText widget and by returning several TextSpan widgets we’ve added
more styling to our News App.
To have a look at the screenshots we’ve used in this section, please visit this LINK²⁴

Stateful vs Stateless Flutter


To have a look at the screenshots we’ve used in this section, please visit this LINK²⁵
Stateless widgets are immutable. Since their properties cannot change, all values are final. On the
contrary, stateful widgets maintain state.
For that reason, widgets are rebuilt with the change of the state.
Does that affect flutter application?
Certainly, that rebuilds widgets and affects the performance.
Then what should we do to solve that problem?
²⁴https://sanjibsinha.com/richtext-in-flutter/
²⁵https://sanjibsinha.com/stateless-vs-stateful-flutter/
10. More on Flutter UI, List, Map, and Provider Best Practices 426

When should I use provider Flutter?


We should use provider package to solve that problem.
Why should we use provider package?
The main reason is, provider package will rebuild only the widget that needs the value. The widget
that needs the value is known as consumer.
As a result, that consumer widget only rebuilds itself. For example, consider a simple counter. As
we press the button to change the state, each pressing changes the state and rebuilds the widget.
For a stateful widget, that rebuild affects the whole tree of widgets.
In Android Studio, we can watch the flutter performance.
Firstly, we see the counter example with the stateful widget.

1 import 'package:flutter/material.dart';
2
3 void main() {
4 runApp(MyApp());
5 }
6
7 class MyApp extends StatelessWidget {
8 // This widget is the root of your application.
9 @override
10 Widget build(BuildContext context) {
11 return MaterialApp(
12 title: 'Flutter Demo',
13 theme: ThemeData(
14 primarySwatch: Colors.blue,
15 ),
16 home: MyHomePage(title: 'Flutter Demo Home Page'),
17 );
18 }
19 }
20
21 class MyHomePage extends StatefulWidget {
22 MyHomePage({Key? key, required this.title}) : super(key: key);
23
24 final String title;
25
26 @override
27 _MyHomePageState createState() => _MyHomePageState();
28 }
10. More on Flutter UI, List, Map, and Provider Best Practices 427

29
30 class _MyHomePageState extends State<MyHomePage> {
31 int _counter = 0;
32
33 void _incrementCounter() {
34 setState(() {
35 _counter++;
36 });
37 }
38
39 @override
40 Widget build(BuildContext context) {
41
42 return Scaffold(
43 appBar: AppBar(
44 title: Text(widget.title),
45 ),
46 body: Center(
47 child: Column(
48 mainAxisAlignment: MainAxisAlignment.center,
49 children: <Widget>[
50 Text(
51 'You have pushed the button this many times:',
52 ),
53 Text(
54 '$_counter',
55 style: Theme.of(context).textTheme.headline4,
56 ),
57 ],
58 ),
59 ),
60 floatingActionButton: FloatingActionButton(
61 onPressed: _incrementCounter,
62 tooltip: 'Increment',
63 child: Icon(Icons.add),
64 ),
65 );
66 }
67 }

Let us press the button for five times and side by side watch the flutter performance.
Now, let us take a closer look and the screenshot looks like the following image.
10. More on Flutter UI, List, Map, and Provider Best Practices 428

It clearly shows that after pressing the button for five times, on the far right side of the screen each
widget shows the number 6.
That means a stateful way always requires more memory than the stateless way.
In fact that is clearly visible in the first image, because the red colored bar represents memory usage.
This is the reason why we should use a combination of Provider package and stateless widgets.
It makes our flutter app more performant.

How do you use Flutter provider package?


We use Flutter provider package like any other packages. Firstly, we add the provider package to the
dependencies section in our pubspec.yaml file.

1 dependencies:
2 provider: ^6.0.0

Secondly, we import the provider package just like any other packages.

1 import 'package:provider/provider.dart';

Finally, we need to notify the listeners that we’re going to change the state.
We can define that method in our model folder, from where our data come.

1 import 'package:flutter/foundation.dart';
2 import 'package:flutter/material.dart';
3 import 'package:provider/provider.dart';
4
5 class Counter with ChangeNotifier {
6 int _count = 0;
7
8 int get count => _count;
9
10 void increment() {
11 _count++;
12 notifyListeners();
13 }
14 }

Next, to run the app we must place providers above the Counter App. Not only that, we need to use
other methods so that we can read and watch the change.
10. More on Flutter UI, List, Map, and Provider Best Practices 429

1 import 'package:flutter/foundation.dart';
2 import 'package:flutter/material.dart';
3 import 'package:provider/provider.dart';
4 import 'package:shop_app/models/counter.dart';
5 void main() {
6 runApp(
7 MultiProvider(
8 providers: [
9 ChangeNotifierProvider(create: (_) => Counter()),
10 ],
11 child: const CounterApp(),
12 ),
13 );
14 }
15
16 class CounterApp extends StatelessWidget {
17 const CounterApp({Key? key}) : super(key: key);
18 @override
19 Widget build(BuildContext context) {
20 return const MaterialApp(
21 home: CounterHomePage(),
22 );
23 }
24 }
25
26 class CounterHomePage extends StatelessWidget {
27 const CounterHomePage({Key? key}) : super(key: key);
28 @override
29 Widget build(BuildContext context) {
30 final subtree = ConstantWidget(
31 child: const Text("Hello World")
32 );
33 final anotherSubtree = _widget();
34 return Scaffold(
35 appBar: AppBar(
36 title: const Text('A Stateless Counter App'),
37 ),
38 body: Center(
39 child: Column(
40 mainAxisSize: MainAxisSize.min,
41 mainAxisAlignment: MainAxisAlignment.center,
42 children: <Widget> [
43 Text('You have pushed the button this many times:'),
10. More on Flutter UI, List, Map, and Provider Best Practices 430

44 Text(
45 '${context.watch<Counter>().count}',
46 key: const Key('counterState'),
47 style: Theme.of(context).textTheme.headline4,
48 ),
49 SizedBox(width: 10.0,),
50 subtree,
51 SizedBox(width: 10.0,),
52 anotherSubtree,
53 ],
54 ),
55 ),
56 floatingActionButton: FloatingActionButton(
57 key: const Key('increment_floatingActionButton'),
58 onPressed: () => context.read<Counter>().increment(),
59 tooltip: 'Increment',
60 child: const Icon(Icons.add),
61 ),
62 );
63 }
64 /// Just to test rebuild
65 Widget _widget() => const Text('Hi');
66
67 ConstantWidget({required Text child}) {
68 /// Just to test rebuild
69 return Text('Hello');
70 }
71 }

In the above code, a few lines are extremely important.


Firstly, we’ve placed the Multi Provider above our counter app.
Secondly, when the context use read and watch methods, we’ve supplied the type Counter that we’ve
defined in the model folder.

1 '${context.watch<Counter>().count}',
2 onPressed: () => context.read<Counter>().increment(),

What is the difference between context.read and context.watch?


It’s a key concept while we’re trying to understand the role of Provider package.
To display the count number, we use context.watch.
Why?
10. More on Flutter UI, List, Map, and Provider Best Practices 431

We call context.watch to make the count property of Type Counter rebuild when the counter
changes.
However, inside the floating action button on press method we return context.read.
It stops the floating action button widget from rebuilding itself.
Now, as a result, we get a different screenshot when we press the button.
With the help of Provider package, we have kept the requirement of memory usage much lower than
the stateful widget.
As we can see, there are no red bars.
Not only that, now we can take a closer look at the flutter performance.
As we keep on pressing the button, only two widgets rebuild themselves without affecting the others.
Moreover, now the state change centers on only two text widgets.
The parent tree remains unaffected.

What is GridTile Flutter? How do you use grid tiles in


Flutter?
To have a look at the screenshots we’ve used in this section, please visit this LINK²⁶
The GridTile in Flutter is a subclass of Stateless Widget, which is a scrollable grid of tiles. And
GridTile typically uses GridTileBar Widget either in header or footer. Not both, at the same time.
Also GridTile must have a child. However, the GridTileBar stateless widget that returns an
IconButton widget.
Suppose we’re going to build a book buying e-cart flutter app.
In such a scenario, we can have a scrollable grid of tiles that will place the Book items first. Next,
moreover, on each book image we can have two GridTileBar widgets.

How do you make a grid Flutter?


Certainly, without the help of GridView widget, we couldn’t make that. So let us start from the
Material design section.

²⁶https://sanjibsinha.com/gridtile-flutter/
10. More on Flutter UI, List, Map, and Provider Best Practices 432

1 Widget build(BuildContext context) {


2 return ChangeNotifierProvider.value(
3 value: Books(),
4 child: MaterialApp(
5 title: 'Book Shop',
6 theme: ThemeData(
7 primarySwatch: Colors.purple,
8 primaryColor: Colors.deepOrange,
9 ),
10 home: const BooksOverviewScreen(),
11 routes: {
12 BookDetailScreen.routeName: (ctx) => const BookDetailScreen(),
13 },
14 ),
15 );
16 }
17 // the code is incomplete for brevity

As we can see, the Books overview screen displays the book items using three main stateless widgets.
They are GridTile, GridTileBar and GridView.
We’re going to the glimpse of the code snippets. For full code, please visit the respective GitHub
repository.
Firstly take a look at how we have used GridTile.

1 class BookItem extends StatelessWidget {


2 const BookItem({Key? key}) : super(key: key);
3
4 @override
5 Widget build(BuildContext context) {
6 final product = Provider.of<Book>(context, listen: false);
7 return ClipRRect(
8 borderRadius: BorderRadius.circular(10),
9 child: GridTile(
10 child: GestureDetector(
11 onTap: () {
12 Navigator.of(context).pushNamed(
13 BookDetailScreen.routeName,
14 arguments: product.id,
15 );
16 },
17 child: Image.network(
10. More on Flutter UI, List, Map, and Provider Best Practices 433

18 product.imageUrl,
19 fit: BoxFit.cover,
20 ),
21 ),
22 footer: GridTileBar(
23 backgroundColor: Colors.black87,
24 leading: Consumer<Book>(
25 builder: (ctx, product, _) => IconButton(
26 icon: Icon(
27 product.isFavorite ? Icons.favorite : Icons.favorite_border,
28 ),
29 color: Theme.of(context).primaryColor,
30 onPressed: () {
31 product.toggleFavoriteStatus();
32 },
33 ),
34 ),
35 title: Text(
36 product.title,
37 textAlign: TextAlign.center,
38 ),
39 trailing: IconButton(
40 icon: const Icon(
41 Icons.shopping_cart,
42 ),
43 onPressed: () {},
44 color: Theme.of(context).primaryColor,
45 ),
46 ),
47 ),
48 );
49 }
50 }

As we mentioned earlier, any GridTile must have a child and a GridTileBar widget either in header
or footer.
In the above code, we’ve placed it in the footer.
Inside the GridTileBar, we’ve used two IconButton widgets. One represents a favorite symbol and
the other displays a shopping cart.
Since we can use each IconButton to press and fire a function, therefore, we’ve used notify listeners
to change the color of favorite sign.
10. More on Flutter UI, List, Map, and Provider Best Practices 434

As a result, we can have an image like the following where we’ve clicked favorites sign for three
times.
We have managed the state in the stateless widgets by using Provider package.
Certainly we’ll discuss that in a separate article. But before that, let us see how we’ve used GridView
widget to display the clicked favorite book items.

1 class BooksGrid extends StatelessWidget {


2 final bool showFavs;
3
4 const BooksGrid({
5 Key? key,
6 required this.showFavs,
7 }) : super(key: key);
8
9 @override
10 Widget build(BuildContext context) {
11 final productsData = Provider.of<Books>(context);
12 final products = showFavs ? productsData.favoriteItems : productsData.items;
13 return GridView.builder(
14 padding: const EdgeInsets.all(10.0),
15 itemCount: products.length,
16 itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
17 // builder: (c) => products[i],
18 value: products[i],
19 child: const BookItem(
20 // products[i].id,
21 // products[i].title,
22 // products[i].imageUrl,
23 ),
24 ),
25 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
26 crossAxisCount: 2,
27 childAspectRatio: 3 / 2,
28 crossAxisSpacing: 10,
29 mainAxisSpacing: 10,
30 ),
31 );
32 }
33 }
10. More on Flutter UI, List, Map, and Provider Best Practices 435

What is change notifier provider in Flutter?


To have a look at the screenshots we’ve used in this section, please visit this LINK²⁷
ChangeNotifierProvider is a class that comes with the Provider package.
Although I’ve written about change notifier before, yet this article is all together different.
We’re going to build a Book Shopping Cart flutter app, and to maintain state we’ve used Provider
package for the best result and less widget rebuilds.
To sum up, ChangeNotifierProvider listens to a ChangeNotifier. Not only listening but it also expose
ChangeNotifier to its descendants and rebuilds dependents when the state changes.

When does state change?


As ChangeNotifier notifies its listeners, the state changes.

1 import 'package:flutter/foundation.dart';
2
3 class Book with ChangeNotifier {
4 final String id;
5 final String title;
6 final String description;
7 final double price;
8 final String imageUrl;
9 bool isFavorite;
10
11 Book({
12 required this.id,
13 required this.title,
14 required this.description,
15 required this.price,
16 required this.imageUrl,
17 this.isFavorite = false,
18 });
19
20 void toggleFavoriteStatus() {
21 isFavorite = !isFavorite;
22 notifyListeners();
23 }
24 }
²⁷https://sanjibsinha.com/change-notifier-provider-flutter/
10. More on Flutter UI, List, Map, and Provider Best Practices 436

The above code means when we click the favorite icon on the Book item, the ChangeNotifier notify
the listeners.
Now, question is what is the correct way to create a ChangeNotifier?
According to the documentation provided by the Provider package ChangeNotifierProvider, the
following way is the correct way.

1 void main() {
2 runApp(
3 MultiProvider(
4 providers: [
5 ChangeNotifierProvider(
6 create: (_) => Books(),
7 ),
8 ChangeNotifierProvider(
9 create: (_) => Cart(),
10 ),
11 ChangeNotifierProvider(
12 create: (_) => Orders(),
13 ),
14 ],
15 child: const BookApp(),
16 ),
17 );
18 }
19 class BookApp extends StatelessWidget {
20 const BookApp({Key? key}) : super(key: key);
21
22 @override
23 Widget build(BuildContext context) {
24 return MaterialApp(
25 title: 'Book Shop',
26 theme: ThemeData(
27 primarySwatch: Colors.purple,
28 primaryColor: Colors.deepOrange,
29 ),
30 home: const BooksOverviewScreen(),
31 routes: {
32 BookDetailScreen.routeName: (ctx) => const BookDetailScreen(),
33 },
34 );
35 }
36 }
10. More on Flutter UI, List, Map, and Provider Best Practices 437

37
38 // for full code please visit Book Shopping Cart flutter app

We must place the Providers above the Book App. As a result, we can read and watch the change of
state in Book Overview screen.

What is change notifier provider?


The ChangeNotifierProvider works as a provider-wrapper class that creates a ChangeNotifier and
automatically disposes it when ChangeNotifierProvider is removed from the widget tree.
By the way, create must not be null.
Now we can click the favorite icon and change the state automatically, because ChangeNotifier will
notify the listeners.
How it does that? It provides an existing ChangeNotifier.

1 Widget build(BuildContext context) {


2 final productsData = Provider.of<Books>(context);
3 final products = showFavs ? productsData.favoriteItems : productsData.items;
4 return GridView.builder(
5 padding: const EdgeInsets.all(10.0),
6 itemCount: products.length,
7 itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
8 // builder: (c) => products[i],
9 value: products[i],
10 child: const BookItem(
11 ),
12 ),
13 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
14 crossAxisCount: 2,
15 childAspectRatio: 3 / 2,
16 crossAxisSpacing: 10,
17 mainAxisSpacing: 10,
18 ),
19 );
20 }
21 // for full code please visit Book Shopping Cart flutter app

From Book overview screen we can go the book detail page quite easily now.
The book detail screen works on product ID.
10. More on Flutter UI, List, Map, and Provider Best Practices 438

1 class BookDetailScreen extends StatelessWidget {


2 static const routeName = '/product-detail';
3
4 const BookDetailScreen({Key? key}) : super(key: key);
5
6 @override
7 Widget build(BuildContext context) {
8 final productId =
9 ModalRoute.of(context)!.settings.arguments as String; // is the id!
10 final loadedProduct = Provider.of<Books>(
11 context,
12 listen: false,
13 ).findById(productId);
14 return Scaffold(
15 appBar: AppBar(
16 title: Text(loadedProduct.title),
17 ),
18 body: SingleChildScrollView(
19 child: Column(
20 children: <Widget>[
21 SizedBox(
22 height: 300,
23 width: double.infinity,
24 child: Image.network(
25 loadedProduct.imageUrl,
26 fit: BoxFit.cover,
27 ),
28 ),
29 const SizedBox(height: 10),
30 Text(
31 '\$${loadedProduct.price}',
32 style: const TextStyle(
33 color: Colors.grey,
34 fontSize: 20,
35 ),
36 ),
37 const SizedBox(
38 height: 10,
39 ),
40 Container(
41 padding: const EdgeInsets.symmetric(horizontal: 10),
42 width: double.infinity,
43 child: Text(
10. More on Flutter UI, List, Map, and Provider Best Practices 439

44 loadedProduct.description,
45 textAlign: TextAlign.center,
46 softWrap: true,
47 ),
48 )
49 ],
50 ),
51 ),
52 );
53 }
54 }
55 // for full code please visit Book Shopping Cart flutter app

If we take a detail look at the Book Shopping Cart flutter app, we will find how ChangeNotifier-
Provider creates the ChangeNotifier and how that notifies the listeners where it is needed.

How do you change the font on flutter?


To have a look at the screenshots we’ve used in this section, please visit this LINK²⁸
To change the font on Flutter and give our app a unique look, we need to follow a few steps. Here
we’ll quickly go through them.
We’ll also learn how to download unique fonts, where to place it and, in addition, how to add that
font name to our text style.
This article on flutter font, will be quick and brief.
So, let’s start.
Firstly, let us see our common problem and how to solve that problem.
The following image shows a detail screen of e-commerce flutter app, where we’ll get the details
about the product.
It’s a Book App. For the primary code snippet please visit respective GitHub Repository.
The above image displays the product detail screen. However, we’ve not added the unique flutter
font style yet.
So that was the first part. Secondly, we’ll add the unique flutter fonts to change this product detail
screen.
To do that, we need to download two unique Google fonts – Anton and Lato.
Keep them in a separate folder and name it “assets/fonts” in our flutter app directory.
Next, we’ll add the font path to our “pubspec.yaml” file.
²⁸https://sanjibsinha.com/flutter-font/
10. More on Flutter UI, List, Map, and Provider Best Practices 440

1 # To add custom fonts to your application, add a fonts section here,


2 # in this "flutter" section. Each entry in this list should have a
3 # "family" key with the font family name, and a "fonts" key with a
4 # list giving the asset and other descriptors for the font. For
5 # example:
6 fonts:
7 - family: Lato
8 fonts:
9 - asset: assets/fonts/Lato-Bold.ttf
10 # - asset: fonts/Schyler-Italic.ttf
11 # style: italic
12 - family: Anton
13 fonts:
14 - asset: assets/fonts/Anton-Regular.ttf
15 # - asset: fonts/TrajanPro_Bold.ttf
16 # weight: 700

Now, our unique flutter fonts has been added globally. We can add any one of these flutter font
anywhere in our app.
Consequently, we can add this font to our Book or product detail screen. What font has flutter?
If we don’t want to change the look, flutter manages it.
That’s not a problem.
But, if we want to change the flutter font, then we have to through the above steps that we’ve already
mentioned.
Moreover, to add some special flutter font effects to any page or screen of our flutter app, we need
to mention our unique flutter font name inside TestStyle widget.

1 import 'package:flutter/material.dart';
2
3 import 'package:provider/provider.dart';
4
5 import '../models/books.dart';
6
7 class BookDetailScreen extends StatelessWidget {
8 static const routeName = '/product-detail';
9
10 const BookDetailScreen({Key? key}) : super(key: key);
11
12 @override
13 Widget build(BuildContext context) {
14 final productId =
10. More on Flutter UI, List, Map, and Provider Best Practices 441

15 ModalRoute.of(context)!.settings.arguments as String; // is the id!


16 final loadedProduct = Provider.of<Books>(
17 context,
18 listen: false,
19 ).findById(productId);
20 return Scaffold(
21 appBar: AppBar(
22 title: Text(loadedProduct.title),
23 ),
24 body: SingleChildScrollView(
25 child: Column(
26 children: <Widget>[
27 SizedBox(
28 height: 300,
29 width: double.infinity,
30 child: Image.network(
31 loadedProduct.imageUrl,
32 fit: BoxFit.cover,
33 ),
34 ),
35 const SizedBox(height: 10),
36 Text(
37 '\$${loadedProduct.price}',
38 style: const TextStyle(
39 color: Colors.grey,
40 fontSize: 20,
41 ),
42 ),
43 const SizedBox(
44 height: 10,
45 ),
46 Container(
47 padding: const EdgeInsets.symmetric(horizontal: 10),
48 width: double.infinity,
49 child: Text(
50 loadedProduct.title,
51 textAlign: TextAlign.center,
52 softWrap: true,
53 style: TextStyle(
54 fontFamily: 'Lato',
55 fontSize: 30.0,
56 ),
57 ),
10. More on Flutter UI, List, Map, and Provider Best Practices 442

58 ),
59 const SizedBox(
60 height: 10,
61 ),
62 Container(
63 padding: const EdgeInsets.symmetric(horizontal: 10),
64 width: double.infinity,
65 child: Text(
66 loadedProduct.description,
67 textAlign: TextAlign.center,
68 softWrap: true,
69 style: TextStyle(
70 fontFamily: 'Anton',
71 fontSize: 20.0,
72 ),
73 ),
74 ),
75 ],
76 ),
77 ),
78 );
79 }
80 }
81 // for full code please visit respective GitHub Repository.

As we can see that in the above code snippet, these two Container widget are responsible to display
the flutter font Anton and Lato.
Watch this part carefully:

1 Container(
2 padding: const EdgeInsets.symmetric(horizontal: 10),
3 width: double.infinity,
4 child: Text(
5 loadedProduct.title,
6 textAlign: TextAlign.center,
7 softWrap: true,
8 style: TextStyle(
9 fontFamily: 'Lato',
10 fontSize: 30.0,
11 ),
12 ),
13 ),
10. More on Flutter UI, List, Map, and Provider Best Practices 443

14 // and this part


15
16 Container(
17 padding: const EdgeInsets.symmetric(horizontal: 10),
18 width: double.infinity,
19 child: Text(
20 loadedProduct.description,
21 textAlign: TextAlign.center,
22 softWrap: true,
23 style: TextStyle(
24 fontFamily: 'Anton',
25 fontSize: 20.0,
26 ),
27 ),
28 ),

For the product title we’ve used Lato. And, for the product description, we’ve used Anton.
As a result, we’ve got this unique look of the product detail screen.
Please compare this image with the previous image we’ve displayed in this page before.
The unique flutter font has changed the look of the product detail screen completely.

How do I store persistent data in Flutter?


To have a look at the screenshots we’ve used in this section, please visit this LINK²⁹
To store persistent data in Flutter we can adopt different approaches. Either we can use local storage
in key, value pair array; or, we can use SQLite database.
In addition we can use remote database also.
However, the concept revolves around state management, accessorized with Provider package.
The question is, how we can use the state management using Provider package between screens and
store persistent data in flutter.
One may ask, why should we use Provider package? We not stateful widget? After all, through
stateful widget, we can also store persistent data in flutter.
In addition, stateful widget comes with Flutter, an in-built feature to maintain state.
The one and only answer is, through Provider package we can avoid widget rebuilds. To get the
concept, and if you want an evidence please read my previous blog post on stateful vs stateless
widgets.
²⁹https://sanjibsinha.com/flutter-provider-persist/
10. More on Flutter UI, List, Map, and Provider Best Practices 444

Now, let us start learning how to store persistent data in flutter. At the very beginning, let me tell
you, the full code snippet is available in the respective GitHub Repository.
We cannot use full code snippets. As they are too long. Therefore, we must use part of them for
brevity.
Above all, we’ll follow MVC or Model-view-controller pattern.
So data comes from the models folder.

1 import 'package:flutter/foundation.dart';
2
3 class Book with ChangeNotifier {
4 final String id;
5 final String title;
6 final String description;
7 final double price;
8 final String imageUrl;
9 bool isFavorite;
10
11 Book({
12 required this.id,
13 required this.title,
14 required this.description,
15 required this.price,
16 required this.imageUrl,
17 this.isFavorite = false,
18 });
19
20 void toggleFavoriteStatus() {
21 isFavorite = !isFavorite;
22 notifyListeners();
23 }
24 }

In the above code, we have defined a Book class whose constructor would pass different properties
including a boolean value isFavorite.
We’ve used Dart mixin to use ChangeNotifier, so that we can use ChangeNotifierProvider from
provider package.
Now, we must have another data model which is actually our local storage. Based on the product
ID, we can retrieve data from that storage.
10. More on Flutter UI, List, Map, and Provider Best Practices 445

1 import 'package:flutter/material.dart';
2
3 import 'book.dart';
4
5 class Books with ChangeNotifier {
6 final List<Book> _items = [
7 Book(
8 id: 'p1',
9 title: 'Beginning Flutter With Dart',
10 description: 'You can learn Flutter as well Dart.',
11 price: 9.99,
12 imageUrl:
13 'https://cdn.pixabay.com/photo/2014/09/05/18/32/old-books-436498_960_720.jpg\
14 ',
15 ),
16 Book(
17 id: 'p2',
18 title: 'Flutter State Management',
19 description: 'Everything you should know about Flutter State.',
20 price: 9.99,
21 imageUrl:
22 'https://cdn.pixabay.com/photo/2016/09/10/17/18/book-1659717_960_720.jpg',
23 ),
24 ...
25 // code is incomplete for brevity

Now, the question is, how we will persist state in flutter?

How do you persist state in Flutter?


To persist state in Flutter, we must place our ChangeNotifierProvider on the top of the Book shopping
cart that we’ve been building.
The following code snippet from the main dart file will give you an idea.
10. More on Flutter UI, List, Map, and Provider Best Practices 446

1 void main() {
2 runApp(
3 MultiProvider(
4 providers: [
5 ChangeNotifierProvider(
6 create: (_) => Books(),
7 ),
8 ChangeNotifierProvider(
9 create: (_) => Cart(),
10 ),
11 ChangeNotifierProvider(
12 create: (_) => Orders(),
13 ),
14 ],
15 child: const BookApp(),
16 ),
17 );
18 }
19
20 class BookApp extends StatelessWidget {
21 const BookApp({Key? key}) : super(key: key);
22
23 @override
24 Widget build(BuildContext context) {
25 return MaterialApp(
26 title: 'Book Shop',
27 theme: ThemeData(
28 primarySwatch: Colors.purple,
29 primaryColor: Colors.deepOrange,
30 ),
31 home: const BooksOverviewScreen(),
32 routes: {
33 BookDetailScreen.routeName: (ctx) => const BookDetailScreen(),
34 //CartScreen.routeName: (ctx) => CartScreen(),
35 },
36 );
37 }
38 }

The home page route points to the books overview screen. However, with the help of other controllers
we will be able to navigate to the book detail page,or screen.
Now, in our material design we’ve defined the route, so one click on the image will take us to the
respective book detail screen.
10. More on Flutter UI, List, Map, and Provider Best Practices 447

In our controllers folder, we have two widgets that act as consumers who will listen to the
notification sent by the ChangeNotifier.
The following code snippet will give us an idea.

1 class _BooksOverviewScreenState extends State<BooksOverviewScreen> {


2 var _showOnlyFavorites = false;
3
4 @override
5 Widget build(BuildContext context) {
6 return Scaffold(
7 appBar: AppBar(
8 title: const Text('Book Shop'),
9 actions: <Widget>[
10 PopupMenuButton(
11 onSelected: (FilterOptions selectedValue) {
12 setState(() {
13 if (selectedValue == FilterOptions.Favorites) {
14 _showOnlyFavorites = true;
15 } else {
16 _showOnlyFavorites = false;
17 }
18 });
19 },
20 icon: const Icon(
21 Icons.more_vert,
22 ),
23 itemBuilder: (_) => [
24 const PopupMenuItem(
25 child: Text('Only Favorites'),
26 value: FilterOptions.Favorites,
27 ),
28 const PopupMenuItem(
29 child: Text('Show All'),
30 value: FilterOptions.All,
31 ),
32 ],
33 ),
34 ],
35 ),
36 body: BooksGrid(showFavs: _showOnlyFavorites),
37 );
38 }
39 }
10. More on Flutter UI, List, Map, and Provider Best Practices 448

As we can see, the body of the books overview screen points to books grid widget. Therefore, we
can have a look at that widget in controllers folder.

1 @override
2 Widget build(BuildContext context) {
3 final productsData = Provider.of<Books>(context);
4 final products = showFavs ? productsData.favoriteItems : productsData.items;
5 return GridView.builder(
6 padding: const EdgeInsets.all(10.0),
7 itemCount: products.length,
8 itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
9 // builder: (c) => products[i],
10 value: products[i],
11 child: const BookItem(
12
13 ),
14 ),
15 ..
16 // incomplete code for brevity

To persist data, we’ve used Provider.of method which passes context as parameter and uses the type
Book class.
Here that plays the crucial role.

1 final productsData = Provider.of<Books>(context);

Not only that, the following part of the above code also helps us to persist data.

1 itemBuilder: (ctx, i) => ChangeNotifierProvider.value(


2 // builder: (c) => products[i],
3 value: products[i],
4 child: const BookItem(
5
6 ),
7 ),

Although the Book App is in primary stage, still it’s worth taking a look at the GitHub Repository.
Moreover, we’ll keep building that e-commerce Book App in the future.
So, stay tuned and get in touch with all Flutter articles that always gives you updated information
on Flutter.
10. More on Flutter UI, List, Map, and Provider Best Practices 449

What is data model in Flutter?


To have a look at the screenshots we’ve used in this section, please visit this LINK³⁰
Model in flutter refers to data flow and it corresponds to the MVC or model view controller
architecture. Although there is no hard and fast rule, but if data flow comes from the models it
always works fine.
Otherwise, managing data becomes a tedious job.
In this brief introduction to data model in flutter, we’ll build a model class first. After that with the
help of local storage we’ll display that data on the home screen.
By the way, for this small app please visit this GitHub repository.
Suppose we’re going to build a shopping cart. In short, we’ll sell books.
As a result we need a good display of books on our home screen, like the following image.
The data displayed on the home screen, comes from a data model class. And that class defines the
id, title, description, price and the image URL.
Therefore, the model class looks like this:

1 class Book {
2 final String id;
3 final String title;
4 final String description;
5 final double price;
6 final String imageUrl;
7 bool isFavorite;
8
9 Book({
10 required this.id,
11 required this.title,
12 required this.description,
13 required this.price,
14 required this.imageUrl,
15 this.isFavorite = false,
16 });
17 }

We keep this data defining class in our models folder. Now, to add some using the constructor of
this Book class, we can adopt two ways.
Either we can keep that data in our models folder, or we can create a list of books in our stateless
book overview screen.
³⁰https://sanjibsinha.com/data-model-flutter/
10. More on Flutter UI, List, Map, and Provider Best Practices 450

How do you get data from model in Flutter?


There are many ways by which we can get data from model in flutter. Not only that, we can also
store persistent data in flutter.
Above all, retrieving data from a resource is our main target.
The resource could be a local storage or a remote source.
For the time being we prefer local storage.
Therefore, in the book overview screen, we define a list of books with all the named parameters.

1 import 'package:flutter/material.dart';
2 import 'package:shop_app_primary/data-model/controllers/book_item.dart';
3 import 'package:shop_app_primary/data-model/models/book.dart';
4
5 class BooksOverviewScreen extends StatelessWidget {
6 BooksOverviewScreen({Key? key}) : super(key: key);
7
8 final List<Book> books = [
9 Book(
10 id: 'p1',
11 title: 'Beginning Flutter With Dart',
12 description: 'You can learn Flutter as well Dart.',
13 price: 9.99,
14 imageUrl:
15 'https://cdn.pixabay.com/photo/2014/09/05/18/32/old-books-436498_960_720.jpg\
16 ',
17 ),
18 Book(
19 id: 'p2',
20 title: 'Flutter State Management',
21 description: 'Everything you should know about Flutter State.',
22 price: 9.99,
23 imageUrl:
24 'https://cdn.pixabay.com/photo/2016/09/10/17/18/book-1659717_960_720.jpg',
25 ),
26 Book(
27 id: 'p3',
28 title: 'WordPress Coding',
29 description:
30 'WordPress coding is not difficult, in fact it is interesting.',
31 price: 9.99,
32 imageUrl:
10. More on Flutter UI, List, Map, and Provider Best Practices 451

33 'https://cdn.pixabay.com/photo/2015/11/19/21/10/glasses-1052010_960_720.jpg',
34 ),
35 Book(
36 id: 'p4',
37 title: 'PHP 8 Standard Library',
38 description: 'PHP 8 Standard Library has made developers life easier.',
39 price: 9.99,
40 imageUrl:
41 'https://cdn.pixabay.com/photo/2015/09/05/21/51/reading-925589_960_720.jpg',
42 ),
43 Book(
44 id: 'p5',
45 title: 'Better Flutter',
46 description:
47 'Learn all the necessary concepts of building a Flutter App.',
48 price: 9.99,
49 imageUrl:
50 'https://cdn.pixabay.com/photo/2015/09/05/07/28/writing-923882_960_720.jpg',
51 ),
52 Book(
53 id: 'p6',
54 title: 'Discrete Mathematical Data Structures and Algorithm',
55 description:
56 'Discrete mathematical concepts are necessary to learn Data Structures and A\
57 lgorithm.',
58 price: 9.99,
59 imageUrl:
60 'https://cdn.pixabay.com/photo/2015/11/19/21/14/glasses-1052023_960_720.jpg',
61 ),
62 ];
63
64 @override
65 Widget build(BuildContext context) {
66 return Scaffold(
67 appBar: AppBar(
68 title: Text('MyShop'),
69 ),
70 body: GridView.builder(
71 padding: const EdgeInsets.all(10.0),
72 itemCount: books.length,
73 itemBuilder: (ctx, i) => BookItem(
74 books[i].id,
75 books[i].title,
10. More on Flutter UI, List, Map, and Provider Best Practices 452

76 books[i].imageUrl,
77 ),
78 gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
79 crossAxisCount: 2,
80 childAspectRatio: 3 / 2,
81 crossAxisSpacing: 10,
82 mainAxisSpacing: 10,
83 ),
84 ),
85 );
86 }
87 }

Although the code snippet is a little bit longer but that is due to the list of books we’ve added as our
local storage.
However, display of data has been managed by another custom widget BookItem.
Watch this part of code:

1 body: GridView.builder(
2 padding: const EdgeInsets.all(10.0),
3 itemCount: books.length,
4 itemBuilder: (ctx, i) => BookItem(
5 books[i].id,
6 books[i].title,
7 books[i].imageUrl,
8 ),

The item builder of Grid view builder passes the context and the index of the list. Next, through
BookItem stateless widget constructor we can pass that data.
Now, we keep that stateless custom widget BookItem in our controllers folder.
In addition, the BookItem widget extracts the data from the data model and sends that back to the
book overview screen.

How do you use lists in Flutter?


We have just seen how we can use lists of books in flutter. But extracting data from that list is the
main idea.
The BookItem widget in controllers folder handles this extraction and sends the data back to the
book overview screen in the views folder.
10. More on Flutter UI, List, Map, and Provider Best Practices 453

All together, the MVC structure in our small flutter app works fine. The data comes from the model
class, the controller extracts that data and sends that data to the view.
The picture of data flow becomes clear.
Finally we take a look at the controller, which extracts data from the model class.

1 import 'package:flutter/material.dart';
2
3 class BookItem extends StatelessWidget {
4 final String id;
5 final String title;
6 final String imageUrl;
7
8 BookItem(this.id, this.title, this.imageUrl);
9
10 @override
11 Widget build(BuildContext context) {
12 return ClipRRect(
13 borderRadius: BorderRadius.circular(10),
14 child: GridTile(
15 child: GestureDetector(
16 onTap: () {},
17 child: Image.network(
18 imageUrl,
19 fit: BoxFit.cover,
20 ),
21 ),
22 footer: GridTileBar(
23 backgroundColor: Colors.black87,
24 leading: IconButton(
25 icon: const Icon(Icons.favorite),
26 color: Theme.of(context).primaryColor,
27 onPressed: () {},
28 ),
29 title: Text(
30 title,
31 textAlign: TextAlign.center,
32 ),
33 trailing: IconButton(
34 icon: const Icon(
35 Icons.shopping_cart,
36 ),
37 onPressed: () {},
10. More on Flutter UI, List, Map, and Provider Best Practices 454

38 color: Theme.of(context).primaryColor,
39 ),
40 ),
41 ),
42 );
43 }
44 }

How do you pass data between screens in flutter?


To have a look at the screenshots we’ve used in this section, please visit this LINK³¹
To pass data between screens in Flutter, we need a list of data first. Most importantly that list of data
must have an id.
Based on that id, we can retrieve other items of that list.
We’ve already learned about persistent data, however, we have not discussed how we can pass data
between screens.
In this article we’ll discuss on passing data between screens and for more curious readers we’ve kept
the full code snippet at this particular GitHub Repository called Book App First.
In future, we’ll try to turn that into a full shopping cart flutter app. Therefore, keep reading and, if
you’re a Flutter enthusiast, please get in touch with the other articles on flutter. We update regularly.
Firstly, we need a data model class Book.

1 import 'package:flutter/foundation.dart';
2
3 class Book with ChangeNotifier {
4 final String id;
5 final String title;
6 final String description;
7 final double price;
8 final String imageUrl;
9 bool isFavorite;
10
11 Book({
12 required this.id,
13 required this.title,
14 required this.description,
15 required this.price,
³¹https://sanjibsinha.com/pass-data-between-screens-flutter/
10. More on Flutter UI, List, Map, and Provider Best Practices 455

16 required this.imageUrl,
17 this.isFavorite = false,
18 });
19
20 void toggleFavoriteStatus() {
21 isFavorite = !isFavorite;
22 notifyListeners();
23 }
24 }

Next, we need a local storage of data where we must fill in with some dummy data.
For images, we could have used local assets. However, we prefer image network instead.
Consequently, let’s get a glimpse of the dummy data.

1 import 'package:flutter/material.dart';
2
3 import 'book.dart';
4
5 class Books with ChangeNotifier {
6 final List<Book> _items = [
7 Book(
8 id: 'p1',
9 title: 'Beginning Flutter With Dart',
10 description: 'You can learn Flutter as well Dart.',
11 price: 9.99,
12 imageUrl:
13 'https://cdn.pixabay.com/photo/2014/09/05/18/32/old-books-436498_960_720.jpg\
14 ',
15 ),
16 Book(
17 id: 'p2',
18 title: 'Flutter State Management',
19 description: 'Everything you should know about Flutter State.',
20 price: 9.99,
21 imageUrl:
22 'https://cdn.pixabay.com/photo/2016/09/10/17/18/book-1659717_960_720.jpg',
23 ),
24 ...
25 // code incomplete for brevity

Now, we can have a books overview screen that displays all the book items. Similarly, if we click
any image that will navigate to the detail screen.
10. More on Flutter UI, List, Map, and Provider Best Practices 456

Let’s take a look at the books overview screen first.


In our dummy data we’ve added six books. On the other hand, we have only one book-detail page
or screen.
That’s the advantage of using the ID of the data that passes between screens.
By the way, to maintain the state of this flutter app, we’ve used provider package and ChangeNoti-
fierProvider.

How do you pass data to a child widget in flutter?


The book over view screen has been controlled by two widgets, which are kept in the controllers
folder.
As a result, we see that book overview screen has the body that points to a custom Books Grid.

1 body: BooksGrid(showFavs: _showOnlyFavorites),

The Books Grid widget, on the other hand, have products data and a child widget Book Items where
it passes the data.

1 final productsData = Provider.of<Books>(context);


2 final products = showFavs ? productsData.favoriteItems : productsData.items;
3 return GridView.builder(
4 padding: const EdgeInsets.all(10.0),
5 itemCount: products.length,
6 itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
7 // builder: (c) => products[i],
8 value: products[i],
9 child: const BookItem(),
10 ),
11 ...
12 // the code is incomplete for brevity

As a result, the Books item child widget plays the crucial role to get the products id and pass it to
the book detail screen.
Let’ see how it has done that.
10. More on Flutter UI, List, Map, and Provider Best Practices 457

1 @override
2 Widget build(BuildContext context) {
3 final product = Provider.of<Book>(context, listen: false);
4 return ClipRRect(
5 borderRadius: BorderRadius.circular(10),
6 child: GridTile(
7 child: GestureDetector(
8 onTap: () {
9 Navigator.of(context).pushNamed(
10 BookDetailScreen.routeName,
11 arguments: product.id,
12 );
13 },
14 child: Image.network(
15 product.imageUrl,
16 fit: BoxFit.cover,
17 ),
18 ),
19 ...

In the Books item widget on tap method, we use Navigator push named method, and as an argument
we’ve passed the product ID.
How did we get the product ID?
Very simple.

1 final product = Provider.of<Book>(context, listen: false);

Since as a type Provider of method has used Book class, now we can get any book property anywhere
in our flutter app.

How do you go from one class to another in flutter?


The Navigator push named method has helped us not only to navigate to another screen, but as an
argument it has passed the Book class ID property.
As a result, now we can take any ID and search the dummy data to find other data that are associated
with that ID.
Consequently, we can display the data on books detail page as we press any image on the overview
screen.
Now, we can also take a look at the book detail screen. It must request that ID of the product that
comes from the Book items widget.
10. More on Flutter UI, List, Map, and Provider Best Practices 458

1 import 'package:flutter/material.dart';
2
3 import 'package:provider/provider.dart';
4
5 import '../models/books.dart';
6
7 class BookDetailScreen extends StatelessWidget {
8 static const routeName = '/product-detail';
9
10 const BookDetailScreen({Key? key}) : super(key: key);
11
12 @override
13 Widget build(BuildContext context) {
14 final productId =
15 ModalRoute.of(context)!.settings.arguments as String; // is the id!
16 final loadedProduct = Provider.of<Books>(
17 context,
18 listen: false,
19 ).findById(productId);
20 return Scaffold(
21 appBar: AppBar(
22 title: Text(loadedProduct.title),
23 ),
24 body: SingleChildScrollView(
25 child: Column(
26 children: <Widget>[
27 SizedBox(
28 height: 300,
29 width: double.infinity,
30 child: Image.network(
31 loadedProduct.imageUrl,
32 fit: BoxFit.cover,
33 ),
34 ),
35 const SizedBox(height: 10),
36 Text(
37 '\$${loadedProduct.price}',
38 style: const TextStyle(
39 color: Colors.grey,
40 fontSize: 20,
41 ),
42 ),
43 const SizedBox(
10. More on Flutter UI, List, Map, and Provider Best Practices 459

44 height: 10,
45 ),
46 Container(
47 padding: const EdgeInsets.symmetric(horizontal: 10),
48 width: double.infinity,
49 child: Text(
50 loadedProduct.title,
51 textAlign: TextAlign.center,
52 softWrap: true,
53 style: TextStyle(
54 fontFamily: 'Lato',
55 fontSize: 30.0,
56 ),
57 ),
58 ),
59 const SizedBox(
60 height: 10,
61 ),
62 Container(
63 padding: const EdgeInsets.symmetric(horizontal: 10),
64 width: double.infinity,
65 child: Text(
66 loadedProduct.description,
67 textAlign: TextAlign.center,
68 softWrap: true,
69 style: TextStyle(
70 fontFamily: 'Anton',
71 fontSize: 20.0,
72 ),
73 ),
74 ),
75 ],
76 ),
77 ),
78 );
79 }
80 }

In the above code, these two lines have played crucial roles. In fact, due to these lines, we can display
any book based on the product ID.
10. More on Flutter UI, List, Map, and Provider Best Practices 460

1 final productId =
2 ModalRoute.of(context)!.settings.arguments as String; // is the id!
3 final loadedProduct = Provider.of<Books>(
4 context,
5 listen: false,
6 ).findById(productId);

How do you pass data with provider in Flutter?


To have a look at the screenshots we’ve used in this section, please visit this LINK³²
When we want to pass data with Provider in Flutter we need to be careful at the very beginning.
Firstly, we cannot equate it passing data between screens.
Why?
Because we can pass data between screens without the latest Provider package, ChangeNotifier-
Provider, and ChangeNotifier.
Let’s consider a simple data model where we have a Book class and a list of dummy data based on
that Book class constructors.
We’ve already created a simple data model to achieve that, and have a respective GitHub Repository,
where you can check the code snippet.
Let us see a glimpse of Book class first.

1 class Book {
2 final String id;
3 final String title;
4 final String description;
5 final double price;
6 final String imageUrl;
7 bool isFavorite;
8
9 Book({
10 required this.id,
11 required this.title,
12 required this.description,
13 required this.price,
14 required this.imageUrl,
15 this.isFavorite = false,
16 });
17 }
³²https://sanjibsinha.com/pass-data-with-provider-flutter/
10. More on Flutter UI, List, Map, and Provider Best Practices 461

Now, based on that, we have some dummy data also so we can display the Book items on the books
overview screen.

1 class BooksOverviewScreen extends StatelessWidget {


2 BooksOverviewScreen({Key? key}) : super(key: key);
3
4 final List<Book> books = [
5 Book(
6 id: 'p1',
7 title: 'Beginning Flutter With Dart',
8 description: 'You can learn Flutter as well Dart.',
9 price: 9.99,
10 imageUrl:
11 'https://cdn.pixabay.com/photo/2014/09/05/18/32/old-books-436498_960_720.jpg\
12 ',
13 ),
14 Book(
15 id: 'p2',
16 title: 'Flutter State Management',
17 description: 'Everything you should know about Flutter State.',
18 price: 9.99,
19 imageUrl:
20 'https://cdn.pixabay.com/photo/2016/09/10/17/18/book-1659717_960_720.jpg',
21 ),
22 Book(
23 id: 'p3',
24 title: 'WordPress Coding',
25 description:
26 'WordPress coding is not difficult, in fact it is interesting.',
27 price: 9.99,
28 imageUrl:
29 'https://cdn.pixabay.com/photo/2015/11/19/21/10/glasses-1052010_960_720.jpg',
30 ),
31 Book(
32 id: 'p4',
33 title: 'PHP 8 Standard Library',
34 description: 'PHP 8 Standard Library has made developers life easier.',
35 price: 9.99,
36 imageUrl:
37 'https://cdn.pixabay.com/photo/2015/09/05/21/51/reading-925589_960_720.jpg',
38 ),
39 Book(
40 id: 'p5',
10. More on Flutter UI, List, Map, and Provider Best Practices 462

41 title: 'Better Flutter',


42 description:
43 'Learn all the necessary concepts of building a Flutter App.',
44 price: 9.99,
45 imageUrl:
46 'https://cdn.pixabay.com/photo/2015/09/05/07/28/writing-923882_960_720.jpg',
47 ),
48 Book(
49 id: 'p6',
50 title: 'Discrete Mathematical Data Structures and Algorithm',
51 description:
52 'Discrete mathematical concepts are necessary to learn Data Structures and A\
53 lgorithm.',
54 price: 9.99,
55 imageUrl:
56 'https://cdn.pixabay.com/photo/2015/11/19/21/14/glasses-1052023_960_720.jpg',
57 ),
58 ];
59
60 @override
61 Widget build(BuildContext context) {
62 return Scaffold(
63 appBar: AppBar(
64 title: Text('MyShop'),
65 ),
66 body: GridView.builder(
67 padding: const EdgeInsets.all(10.0),
68 itemCount: books.length,
69 itemBuilder: (ctx, i) => BookItem(
70 books[i].id,
71 books[i].title,
72 books[i].imageUrl,
73 ),
74 gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
75 crossAxisCount: 2,
76 childAspectRatio: 3 / 2,
77 crossAxisSpacing: 10,
78 mainAxisSpacing: 10,
79 ),
80 ),
81 );
82 }
83 }
10. More on Flutter UI, List, Map, and Provider Best Practices 463

The above code is quite simple. Because we pass data between classes through class constructor.
The following code snippet handles that passage of data.

1 itemBuilder: (ctx, i) => BookItem(


2 books[i].id,
3 books[i].title,
4 books[i].imageUrl,
5 ),

Now we can have a Book Item controller, where we can request that data through class constructor.
In addition, we can display the title, image etc.

1 import 'package:flutter/material.dart';
2
3 class BookItem extends StatelessWidget {
4 final String id;
5 final String title;
6 final String imageUrl;
7
8 BookItem(this.id, this.title, this.imageUrl);
9
10 @override
11 Widget build(BuildContext context) {
12 return ClipRRect(
13 borderRadius: BorderRadius.circular(10),
14 child: GridTile(
15 child: GestureDetector(
16 onTap: () {},
17 child: Image.network(
18 imageUrl,
19 fit: BoxFit.cover,
20 ),
21 ),
22 footer: GridTileBar(
23 backgroundColor: Colors.black87,
24 leading: IconButton(
25 icon: const Icon(Icons.favorite),
26 color: Theme.of(context).primaryColor,
27 onPressed: () {},
28 ),
29 title: Text(
30 title,
10. More on Flutter UI, List, Map, and Provider Best Practices 464

31 textAlign: TextAlign.center,
32 ),
33 trailing: IconButton(
34 icon: const Icon(
35 Icons.shopping_cart,
36 ),
37 onPressed: () {},
38 color: Theme.of(context).primaryColor,
39 ),
40 ),
41 ),
42 );
43 }
44 }

However, the above data model represents the whole book items. Not a single book.
How we can achieve that?
Here Provider package, and ChangeNotifierProvider come to our rescue.
If we add Provider package ChangeNotifierProvider with our book class, we can easily notify the
listener widget in our widget tree.

What is provider architecture in Flutter?


The main slogan of provider architecture in Flutter is, provide the dependencies to another widget.
As a result, now, we can think of a single book now.
We also need to understand another key concept. In the books overview screen, we’ve already
instantiated the Book object.
Consequently, we want only the book, or to be very specific here, the product ID only.
Therefore, first thing we need to do is, add the dependencies of Provider package to our “pub-
spec.yaml” file first.
Next, we change our Book class to this:
10. More on Flutter UI, List, Map, and Provider Best Practices 465

1 import 'package:flutter/foundation.dart';
2
3 class Book with ChangeNotifier {
4 final String id;
5 final String title;
6 final String description;
7 final double price;
8 final String imageUrl;
9 bool isFavorite;
10
11 Book({
12 required this.id,
13 required this.title,
14 required this.description,
15 required this.price,
16 required this.imageUrl,
17 this.isFavorite = false,
18 });
19 }

Why should we do that?


Because we want to provide single book object to Book item widget like this:

1 import 'package:flutter/material.dart';
2 import 'package:provider/provider.dart';
3
4 import '../views/book_detail_screen.dart';
5 import '../models/book.dart';
6
7 class BookItem extends StatelessWidget {
8 const BookItem({Key? key}) : super(key: key);
9
10 @override
11 Widget build(BuildContext context) {
12 final product = Provider.of<Book>(context, listen: false);
13 return ClipRRect(
14 borderRadius: BorderRadius.circular(10),
15 child: GridTile(
16 child: GestureDetector(
17 onTap: () {
18 Navigator.of(context).pushNamed(
19 BookDetailScreen.routeName,
10. More on Flutter UI, List, Map, and Provider Best Practices 466

20 arguments: product.id,
21 );
22 },
23 child: Image.network(
24 product.imageUrl,
25 fit: BoxFit.cover,
26 ),
27 ),
28 footer: GridTileBar(
29 backgroundColor: Colors.black87,
30 leading: Consumer<Book>(
31 builder: (ctx, product, _) => IconButton(
32 icon: Icon(
33 product.isFavorite ? Icons.favorite : Icons.favorite_border,
34 ),
35 color: Theme.of(context).primaryColor,
36 onPressed: () {
37 product.toggleFavoriteStatus();
38 },
39 ),
40 ),
41 title: Text(
42 product.title,
43 textAlign: TextAlign.center,
44 ),
45 trailing: IconButton(
46 icon: const Icon(
47 Icons.shopping_cart,
48 ),
49 onPressed: () {},
50 color: Theme.of(context).primaryColor,
51 ),
52 ),
53 ),
54 );
55 }
56 }

In the above code, two lines are extremely important.


First, we have imported the Book data class to this widget. And, next, based on the mixin of
ChangeNotifierProvider with Book class, now we can provide a single book, or product ID.
As we can see, we don’t pass data through class constructor anymore.
10. More on Flutter UI, List, Map, and Provider Best Practices 467

On the contrary, we got it through this line,

1 final product = Provider.of<Book>(context, listen: false);

And in the Books overview screen we also have not sent all books data through Book Item
constructor. Instead, we’ve used another widget Book Grid as the body.

1 body: BooksGrid(showFavs: _showOnlyFavorites),

After that, in that Book Grid widget, we return a GridView builder like the following.

1 final productsData = Provider.of<Books>(context);


2 final products = productsData.items;
3 return GridView.builder(
4 padding: const EdgeInsets.all(10.0),
5 itemCount: products.length,
6 itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
7 value: products[i],
8 child: const BookItem(),
9 ),

Now as a ChangeNotifierProvider value we pass one product object from the instantiated books or
products.
This part is little bit tricky, but based on that the Book Item widget now can provide one product
object as an argument, while navigating to the books detail page.

1 child: GridTile(
2 child: GestureDetector(
3 onTap: () {
4 Navigator.of(context).pushNamed(
5 BookDetailScreen.routeName,
6 arguments: product.id,
7 );
8 },
9 child: Image.network(
10 product.imageUrl,
11 fit: BoxFit.cover,
12 ),
13 ),

Since, we’ve building this Book shopping cart together, let us keep in touch with the further progress.
Until now, you’ll get the full code snippet at this GitHub Repository.
10. More on Flutter UI, List, Map, and Provider Best Practices 468

What is provider pattern Flutter?


To have a look at the screenshots we’ve used in this section, please visit this LINK³³
In Flutter when we discuss provider pattern to give examples of provider, we usually think of state
management.
It is true that provider package with the help of ChangeNotifierProvider and ChangeNotifier
manages state most efficiently.
But provider is actually a flutter architecture that provides the current data model to the place where
we need it.
In this very short, but important article on Provider pattern flutter we’ll quickly learn the concept.
Let’s have a look at how we can provide the current data model to anywhere in our widget tree.
We’re going to design a shop app where we we’ll have some products. But instead of passing data
through class constructor, we’ll rely on provider package with the help of ChangeNotifierProvider
and ChangeNotifier.
Before we start, let me tell you where you’ll find the source code. We’ve created a folder branch-one
where we keep the main dart file for this example.
Keeping other files intact, you can run the main dart file and get the same result.
Firstly, we must have a data model at our local storage.
In our models folder we have two files Product and Products.
The Product class is the parent class based on which we have added a list of Products.

1 import 'package:flutter/foundation.dart';
2
3 class Product with ChangeNotifier {
4 final String id;
5 final String title;
6 final String description;
7 final double price;
8 final String imageUrl;
9
10 Product({
11 required this.id,
12 required this.title,
13 required this.description,
14 required this.price,
15 required this.imageUrl,
³³https://sanjibsinha.com/provider-flutter-example/
10. More on Flutter UI, List, Map, and Provider Best Practices 469

16 });
17 }

The Products file is too long, so we cut it short for brevity. The list of products has actually
instantiated the product object several times.

1 import 'package:flutter/material.dart';
2
3 import 'product.dart';
4
5 class Products with ChangeNotifier {
6 final List<Product> products = [
7 Product(
8 id: 'p1',
9 title: 'Classic Watch',
10 description: 'A Classic Watch accessorized with style.',
11 price: 9.99,
12 imageUrl:
13 'https://cdn.pixabay.com/photo/2018/02/24/20/39/clock-3179167_960_720.jpg',
14 ),
15 Product(
16 id: 'p1',
17 title: 'Shoe with Gears',
18 description: 'Shoes paired with excersise accessories.',
19 price: 9.99,
20 imageUrl:
21 'https://cdn.pixabay.com/photo/2017/07/02/19/24/dumbbells-2465478_960_720.jp\
22 g',
23 ),
24 ...

The question is how we can provide this data model to our home screen. When should I use provider
in Flutter?
We use provider for many purposes. However, at present we want to count how many products
objects are there in our local storage.
Let’s just count the length of the products.
To do that, we must place our ChangeNotifierProvider on the top of our main app.
10. More on Flutter UI, List, Map, and Provider Best Practices 470

1 import 'package:flutter/foundation.dart';
2 import 'package:flutter/material.dart';
3 import 'package:provider/provider.dart';
4 import '../models/products.dart';
5
6 void main() {
7 runApp(
8 MultiProvider(
9 providers: [
10 ChangeNotifierProvider(
11 create: (_) => Products(),
12 ),
13 ],
14 child: const ShopAppWithProvider(),
15 ),
16 );
17 }

We’re interested about Product objects that have already been instantiated. As a result ChangeNoti-
fierProvider create named parameter returns Products.
Subsequently, we can retrieve all the products by two ways.
Let’s see the first way.

1 class ShopAppWithProvider extends StatelessWidget {


2 const ShopAppWithProvider({Key? key}) : super(key: key);
3 @override
4 Widget build(BuildContext context) {
5 return const MaterialApp(
6 home: ShoHomePage(),
7 );
8 }
9 }
10
11 class ShoHomePage extends StatelessWidget {
12 const ShoHomePage({Key? key}) : super(key: key);
13 @override
14 Widget build(BuildContext context) {
15 return Scaffold(
16 appBar: AppBar(
17 title: const Text('Products App'),
18 ),
19 body: Center(
10. More on Flutter UI, List, Map, and Provider Best Practices 471

20 child: Column(
21 mainAxisSize: MainAxisSize.min,
22 mainAxisAlignment: MainAxisAlignment.center,
23 children: <Widget>[
24 const Text('All products length:'),
25 Text(
26 '${context.watch<Products>().products.length}',
27 key: const Key('productKeyOne'),
28 style: Theme.of(context).textTheme.headline4,
29 ),
30 ],
31 ),
32 ),
33 );
34 }
35 }

Now, directly context can watch the products length and give us a nice output like the following
image.
However, we can count the products length by using Provider also.

What does provider do in Flutter?


As we’ve said earlier, provider does many things and one of them is to provide the current data
model to any widget in the tree.
Therefore, this time we’ll change the above code a little bit and will add provider.

1 class ShopAppWithProvider extends StatelessWidget {


2 const ShopAppWithProvider({Key? key}) : super(key: key);
3 @override
4 Widget build(BuildContext context) {
5 return const MaterialApp(
6 home: ShoHomePage(),
7 );
8 }
9 }
10
11 class ShoHomePage extends StatelessWidget {
12 const ShoHomePage({Key? key}) : super(key: key);
13 @override
14 Widget build(BuildContext context) {
10. More on Flutter UI, List, Map, and Provider Best Practices 472

15 final products = Provider.of<Products>(context).products;


16 return Scaffold(
17 appBar: AppBar(
18 title: const Text('Products App'),
19 ),
20 body: Center(
21 child: Column(
22 mainAxisSize: MainAxisSize.min,
23 mainAxisAlignment: MainAxisAlignment.center,
24 children: <Widget>[
25 const Text('All products length:'),
26 Text(
27 '${context.watch<Products>().products.length}',
28 key: const Key('productKeyOne'),
29 style: Theme.of(context).textTheme.headline4,
30 ),
31 const SizedBox(
32 height: 10.0,
33 ),
34 Text(
35 '${products.length}',
36 key: const Key('productKeyTwo'),
37 style: Theme.of(context).textTheme.headline4,
38 ),
39 ],
40 ),
41 ),
42 );
43 }
44 }

In the above code, a few lines are worth noting. The first one is the usage of Provider.

1 final products = Provider.of<Products>(context).products;

Next, the provider provides the current data model products to the Text widget.
10. More on Flutter UI, List, Map, and Provider Best Practices 473

1 Text(
2 '${products.length}',
3 key: const Key('productKeyTwo'),
4 style: Theme.of(context).textTheme.headline4,
5 ),

Therefore, the next image shows two numbers of products length, instead of one.
And, finally, you may have noticed that each Text widget has different keys.

1 Text(
2 '${context.watch<Products>().products.length}',
3 key: const Key('productKeyOne'),
4 style: Theme.of(context).textTheme.headline4,
5 ),
6 const SizedBox(
7 height: 10.0,
8 ),
9 Text(
10 '${products.length}',
11 key: const Key('productKeyTwo'),
12 style: Theme.of(context).textTheme.headline4,
13 ),

Otherwise, it will throw an exception. Why, because it is final.


We’ll learn more things about provider, so stay tuned as we’ll build that shop app with provider step
by step.

How do you use ChangeNotifierProvider in Flutter?


To have a look at the screenshots we’ve used in this section, please visit this LINK³⁴
What is the best ChangeNotifierProvider example in Flutter? How should we use ChangeNotifier-
Provider in Flutter?
What should be the correct approach to use ChangeNotifierProvider in Flutter?
Firstly, we cannot think of ChangeNotifierProvider without the provider package. And we know
that provider pattern actually provides the current data model to the place where we need it.
In the previous article on provider data model, we’ve discussed the same topic. In the same vein, we
have also seen that we can get the value of existing data quite easily using provider architecture.
³⁴https://sanjibsinha.com/changenotifierprovider-flutter-example/
10. More on Flutter UI, List, Map, and Provider Best Practices 474

Moreover, it’s always better than using class constructor.


In this article, we’ll see how we can retrieve existing value of a shopping app using ChangeNotifier-
Provider.
To do that, we must have a data model ready and instantiate the product objects first.

1 import 'package:flutter/foundation.dart';
2
3 class Product with ChangeNotifier {
4 final String id;
5 final String title;
6 final String description;
7 final double price;
8 final String imageUrl;
9
10 Product({
11 required this.id,
12 required this.title,
13 required this.description,
14 required this.price,
15 required this.imageUrl,
16 });
17 }

Next, we need to instantiate the product objects in a separate class Products. So that ChangeNoti-
fierProvider can use value method to get the existing value.

1 import 'package:flutter/material.dart';
2
3 import 'product.dart';
4
5 class Products with ChangeNotifier {
6 final List<Product> products = [
7 Product(
8 id: 'p1',
9 title: 'Classic Watch',
10 description: 'A Classic Watch accessorized with style.',
11 price: 9.99,
12 imageUrl:
13 'https://cdn.pixabay.com/photo/2018/02/24/20/39/clock-3179167_960_720.jpg',
14 ),
15 Product(
16 id: 'p1',
10. More on Flutter UI, List, Map, and Provider Best Practices 475

17 title: 'Shoe with Gears',


18 description: 'Shoes paired with exercise accessories.',
19 price: 9.99,
20 imageUrl:
21 'https://cdn.pixabay.com/photo/2017/07/02/19/24/dumbbells-2465478_960_720.jp\
22 g',
23 ),
24 ...
25 // code is incomplete for brevity; please stay updated with this GitHub Repository

Now, as a rule, we’ll keep the ChangeNotifierProvider create named parameter points to the Products
class. So that later we can use that type.

1 void main() {
2 runApp(
3 MultiProvider(
4 providers: [
5 ChangeNotifierProvider(
6 create: (_) => Products(),
7 ),
8 ],
9 child: const ShopAppWithProvider(),
10 ),
11 );
12 }

We have used the default constructor because to create a value we should always use the default
constructor. According to the documentation, we cannot create the instance inside build method.
It will lead to memory leaks and potentially undesired side-effects. Where is ChangeNotifierProvider
used?
We use ChangeNotifierProvider here for showing existing data model. Therefore,the Shop App With
Provider stateless widget will show the products overview screen.
Or, we may consider it as the home page.
Actually, we’ve already instantiated the product object. Therefore, we need the object individually.
That means, if we can get the product ID, our job is done.
Based on that ID, we can display all the products in a scrollable Grid. Each product ID points to the
respective image and title.
Now, with the help of ChangeNotifierProvider, we can use three ways to display them.
Let’s see the first one’s code snippet.
10. More on Flutter UI, List, Map, and Provider Best Practices 476

1 class ShopAppWithProvider extends StatelessWidget {


2 const ShopAppWithProvider({Key? key}) : super(key: key);
3 @override
4 Widget build(BuildContext context) {
5 return const MaterialApp(
6 home: ShopHomePage(),
7 );
8 }
9 }
10
11 class ShopHomePage extends StatelessWidget {
12 const ShopHomePage({Key? key}) : super(key: key);
13 @override
14 Widget build(BuildContext context) {
15 final products = context.watch<Products>().products;
16 return Scaffold(
17 appBar: AppBar(
18 title: const Text('Products App'),
19 ),
20 body: GridView.builder(
21 padding: const EdgeInsets.all(10.0),
22 itemCount: products.length,
23 itemBuilder: (ctx, i) => ClipRRect(
24 borderRadius: BorderRadius.circular(10),
25 child: GridTile(
26 child: GestureDetector(
27 onTap: () {},
28 child: Image.network(
29 context.watch<Products>().products[i].imageUrl,
30 fit: BoxFit.cover,
31 ),
32 ),
33 footer: GridTileBar(
34 backgroundColor: Colors.black87,
35 title: Text(
36 context.watch<Products>().products[i].title,
37 textAlign: TextAlign.center,
38 ),
39 ),
40 ),
41 ),
42 // ),
43 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
10. More on Flutter UI, List, Map, and Provider Best Practices 477

44 crossAxisCount: 2,
45 childAspectRatio: 3 / 2,
46 crossAxisSpacing: 10,
47 mainAxisSpacing: 10,
48 ),
49 ),
50 );
51 }
52 }

In the above code, watch this line is extremely important.

1 final products = context.watch<Products>().products;

The next line, which plays a key role is the following one:

1 itemBuilder: (ctx, i) => ClipRRect(


2 ...
3 // code is incomplete for brevity

In the above code we pass the context and the index.


Now depending on that logic, we can change the above home page code this way now:

1 class ShopHomePage extends StatelessWidget {


2 const ShopHomePage({Key? key}) : super(key: key);
3 @override
4 Widget build(BuildContext context) {
5 final product = Provider.of<Products>(context).products;
6 return Scaffold(
7 appBar: AppBar(
8 title: const Text('Products App'),
9 ),
10 body: GridView.builder(
11 padding: const EdgeInsets.all(10.0),
12 itemCount: product.length,
13 itemBuilder: (ctx, i) => ClipRRect(
14 borderRadius: BorderRadius.circular(10),
15 child: GridTile(
16 child: GestureDetector(
17 onTap: () {},
18 child: Image.network(
19 product[i].imageUrl,
10. More on Flutter UI, List, Map, and Provider Best Practices 478

20 fit: BoxFit.cover,
21 ),
22 ),
23 footer: GridTileBar(
24 backgroundColor: Colors.black87,
25 title: Text(
26 product[i].title,
27 textAlign: TextAlign.center,
28 ),
29 ),
30 ),
31 ),
32 // ),
33 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
34 crossAxisCount: 2,
35 childAspectRatio: 3 / 2,
36 crossAxisSpacing: 10,
37 mainAxisSpacing: 10,
38 ),
39 ),
40 );
41 }
42 }

In the above code, the following code is important.

1 final product = Provider.of<Products>(context).products;

We have used Provider of context method where we’ve mentioned the type. And then get all the
products.
Further, we can also use ChangeNotifierProvider value and change the above code.

1 class ShopHomePage extends StatelessWidget {


2 const ShopHomePage({Key? key}) : super(key: key);
3 @override
4 Widget build(BuildContext context) {
5 final product = Provider.of<Products>(context).products;
6 return Scaffold(
7 appBar: AppBar(
8 title: const Text('Products App'),
9 ),
10 body: GridView.builder(
10. More on Flutter UI, List, Map, and Provider Best Practices 479

11 padding: const EdgeInsets.all(10.0),


12 itemCount: product.length,
13 itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
14 value: product[i],
15 child: ClipRRect(
16 borderRadius: BorderRadius.circular(10),
17 child: GridTile(
18 child: GestureDetector(
19 onTap: () {},
20 child: Image.network(
21 product[i].imageUrl,
22 fit: BoxFit.cover,
23 ),
24 ),
25 footer: GridTileBar(
26 backgroundColor: Colors.black87,
27 title: Text(
28 product[i].title,
29 textAlign: TextAlign.center,
30 ),
31 ),
32 ),
33 ),
34 ),
35 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
36 crossAxisCount: 2,
37 childAspectRatio: 3 / 2,
38 crossAxisSpacing: 10,
39 mainAxisSpacing: 10,
40 ),
41 ),
42 );
43 }
44 }

All these three code snippet of products home page will display the products in the same way.
Furthermore, we can use the Gesture Detector on tap method to pass the product ID as an argument.
As a result, we can display the product detail in a separate screen.
In the next article we will discuss that. So stay tuned to get updated on building a flutter shopping
app.
10. More on Flutter UI, List, Map, and Provider Best Practices 480

What is ChangeNotifierProvider value?


To have a look at the screenshots we’ve used in this section, please visit this LINK³⁵
The role of ChangeNotifierProvider value is not like builder or create. We need to understand this
concept specifically.
Certainly, ChangeNotifierProvider value plays a crucial role. However, it has nothing to do with
maintaining state.
In this article, we’ll take a close look at the scope of the ChangeNotifierProvider value. How we can
use it in our app.
Moreover, what is the difference between ChangeNotifierProvider value and ChangeNotifier-
Provider create.
Firstly, ChangeNotifierProvider extends ChangeNotifier that flutter provides.
Secondly, ChangeNotifierProvider listens to ChangeNotifier. Not only that, after listening to
ChangeNotifier, it exposes ChangeNotifier to its descendants.
The ChangeNotifierProvider also rebuilds dependents whenever ChangeNotifier notify its listeners.
Now, the million dollar question is what do we want to do?
Will we want to create a ChangeNotifier? Or, we will reuse the ChangeNotifier?
Certainly, we must create first, and after that we reuse ChangeNotifier.
Let us dig a little bit dipper into that concept. In addition, we must know that we have a GitHub
repository exclusively for this article.
To understand how ChangeNotifierProvider value works, we will use part of code snippets for
brevity. However, we can always check the full code at our GitHub repository.
We need to understand the app structure first. Subsequently, we’ll check the code snippets so that
we understand ChangeNotifierProvider value.
We have some products as our local storage. First we’ve created a class of Product and then we’ve
instantiated some Product objects in a different class.

³⁵https://sanjibsinha.com/changenotifierprovider-value/
10. More on Flutter UI, List, Map, and Provider Best Practices 481

1 import 'package:flutter/foundation.dart';
2
3 class Product with ChangeNotifier {
4 final String id;
5 final String title;
6 final String description;
7 final double price;
8 final String imageUrl;
9
10 Product({
11 required this.id,
12 required this.title,
13 required this.description,
14 required this.price,
15 required this.imageUrl,
16 });
17 }

We don’t have any method yet. Although we use dart mixin, to use ChangeNotifier.
Next, we instantiate a dew product object and keep both classes at models folder.

1 import 'package:flutter/material.dart';
2
3 import 'product.dart';
4
5 class Products with ChangeNotifier {
6 final List<Product> products = [
7 Product(
8 id: 'p1',
9 title: 'Classic Watch',
10 description: 'A Classic Watch accessorized with style.',
11 price: 9.99,
12 imageUrl:
13 'https://cdn.pixabay.com/photo/2018/02/24/20/39/clock-3179167_960_720.jpg',
14 ),
15 Product(
16 id: 'p1',
17 title: 'Shoe with Gears',
18 description: 'Shoes paired with excersise accessories.',
19 price: 9.99,
20 imageUrl:
21 'https://cdn.pixabay.com/photo/2017/07/02/19/24/dumbbells-2465478_960_720.jp\
10. More on Flutter UI, List, Map, and Provider Best Practices 482

22 g',
23 ),
24 ...

As a result, our data model is now ready. Meanwhile, we can now use “ChangeNotifierProvider
create” to create a ChangeNotifier and place it over the top of our shopping cart app.

1 void main() {
2 runApp(
3 MultiProvider(
4 providers: [
5 ChangeNotifierProvider(
6 create: (_) => Products(),
7 ),
8 ],
9 child: const ShopAppWithProvider(),
10 ),
11 );
12 }
13 ...

We should not use ChangeNotifierProvider value to create a ChangeNotifier.

What is ChangeNotifierProvider Flutter?


Now the question is, why we should not use ChangeNotifierProvider value to create a ChangeNoti-
fier in Flutter?
The first, and the foremost reason is it only references the state source, but does not manage its
lifetime.
Since we have placed the ChangeNotifierProvider create on the top of our flutter app, now we can
listen to ChangeNotifier and expose it to all the descendants.
Our shopping app widget tree starts with the ShopAppProvider() stateless widget.
10. More on Flutter UI, List, Map, and Provider Best Practices 483

1 class ShopAppWithProvider extends StatelessWidget {


2 const ShopAppWithProvider({Key? key}) : super(key: key);
3 @override
4 Widget build(BuildContext context) {
5 return MaterialApp(
6 theme: ThemeData(
7 primarySwatch: Colors.brown,
8 primaryColor: Colors.deepOrange,
9 ),
10 home: const ShopHomePage(),
11 routes: {
12 ShopProductDetailScreen.routename: (context) =>
13 const ShopProductDetailScreen(),
14 },
15 );
16 }
17 }

Next in that widget tree comes ShopHomePage() stateless widget. This home page, or screen,
whatever we wan to call it, will display the products object in a Grid View.
Therefore, we must have two separate controllers, which will not only display the products, but also,
at the same time, navigate to the products detail page.
Now, in the ProductView() controller we can use ChangeNotifierProvider value to provide an
existing ChangeNotifier.

1 class ProductView extends StatelessWidget {


2 const ProductView({
3 Key? key,
4 }) : super(key: key);
5
6 @override
7 Widget build(BuildContext context) {
8 final products = Provider.of<Products>(context).products;
9 return GridView.builder(
10 padding: const EdgeInsets.all(10.0),
11 itemCount: products.length,
12 itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
13 value: products[i],
14 child: const ProductItem(),
15 ),
16 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
17 crossAxisCount: 2,
10. More on Flutter UI, List, Map, and Provider Best Practices 484

18 childAspectRatio: 3 / 2,
19 crossAxisSpacing: 10,
20 mainAxisSpacing: 10,
21 ),
22 );
23 }
24 }

The above code snippet is self explanatory. We can use the context and index of all products and
provide ChangeNotifier to the child widget ProductItem().
As a result, now we can easily access each product property through the instantiated product objects;
as we’ve defined them in our Product class.

1 class ProductItem extends StatelessWidget {


2 const ProductItem({
3 Key? key,
4 }) : super(key: key);
5
6 @override
7 Widget build(BuildContext context) {
8 final product = Provider.of<Product>(context, listen: false);
9 return ClipRRect(
10 borderRadius: BorderRadius.circular(10),
11 child: GridTile(
12 child: GestureDetector(
13 onTap: () {
14 Navigator.of(context).pushNamed(
15 ShopProductDetailScreen.routename,
16 arguments: product.id,
17 );
18 },
19 child: Image.network(
20 product.imageUrl,
21 fit: BoxFit.cover,
22 ),
23 ),
24 footer: GridTileBar(
25 backgroundColor: Colors.black87,
26 title: Text(
27 product.title,
28 textAlign: TextAlign.center,
29 ),
10. More on Flutter UI, List, Map, and Provider Best Practices 485

30 subtitle: Text(
31 product.description,
32 textAlign: TextAlign.center,
33 ),
34 ),
35 ),
36 );
37 }
38 }

Not only that, we can now pass the product ID as an argument of of the Navigator class.
Now we can click any image on the product home page, and get to the detail page.
In the next article we’ll try to build the product detail page, so that it displays the product details,
instead of showing a text.

What is navigator and route in Flutter?


To have a look at the screenshots we’ve used in this section, please visit this LINK³⁶
In our previous article, we’ve seen how we can use ChangeNotifierProvider value. And with the
help of ChangeNotifierProvider value we have used the concept of Provider and ChangeNotifier to
read existing data from the local storage.
However, while doing this, we’ve found that we need to create another screen or page that will
display the detail of a single product object.
But to do that, we need to navigate to another screen.
Right?
Firstly, we must know what route and navigator are in flutter.
What are their roles and how we can make them play their roles efficiently.
Secondly, which widgets we should use?
Finally, how we should use those widgets?
Well, let’s answer one by one, so that we can solve this common problem of flutter that involves
navigator and route. But before answering the questions,let me inform you that the full code snippets
is available at this particular GitHub repository.
³⁶https://sanjibsinha.com/navigator-and-routes-in-flutter/
10. More on Flutter UI, List, Map, and Provider Best Practices 486

What is route in flutter?


In flutter we call a page or screen as route. As we know, in flutter every page or screen is a widget.
It could be either stateful or stateless widgets.
As a result we can write each route as a separate widget. However, in our case, it is not that easy.
Why?
Let’s take a look at the first, or home page.
This is our home page where we display all products from a local storage, which is nothing but
instantiated product objects.
Now we click any image, and it takes us to the product-detail page, like the following.
How it happens? Because, we have placed our ChangeNotifierProvider on top of the widget tree.

1 void main() {
2 runApp(
3 MultiProvider(
4 providers: [
5 ChangeNotifierProvider(
6 create: (_) => Products(),
7 ),
8 ],
9 child: const ShopAppWithProvider(),
10 ),
11 );
12 }

Next thing, we must do is, create the route.


That was our second question. Where should we use the route?

1 class ShopAppWithProvider extends StatelessWidget {


2 const ShopAppWithProvider({Key? key}) : super(key: key);
3 @override
4 Widget build(BuildContext context) {
5 return MaterialApp(
6 theme: ThemeData(
7 primarySwatch: Colors.brown,
8 primaryColor: Colors.deepOrange,
9 ),
10 home: const ShopHomePage(),
11 routes: {
10. More on Flutter UI, List, Map, and Provider Best Practices 487

12 ShopProductDetailScreen.routename: (context) =>


13 const ShopProductDetailScreen(),
14 },
15 );
16 }
17 }

In the above code snippet, these two lines are extremely important. In those lines, we have defined
the routes.
We should always define our routes in MaterialApp widget.

1 routes: {
2 ShopProductDetailScreen.routename: (context) =>
3 const ShopProductDetailScreen(),
4 },

In our Shop Product Detail Screen we’ve declared a static constant property routename. Moreover,
it’s a string that we can find in the second image that displays the product detail screen.

1 class ShopProductDetailScreen extends StatelessWidget {


2 const ShopProductDetailScreen({Key? key}) : super(key: key);
3 static const routename = "/product-detail";
4
5 @override
6 Widget build(BuildContext context) {
7 return Scaffold(
8 appBar: AppBar(
9 title: const Text('Product Detail Page'),
10 ),
11 body: const Center(
12 child: Text(
13 'We will get product detail here',
14 style: TextStyle(
15 fontSize: 30.0,
16 ),
17 ),
18 ),
19 );
20 }
21 }

However, how could we reach at this page?


10. More on Flutter UI, List, Map, and Provider Best Practices 488

As we’ve defined the routes, subsequently we must have a method that should use Navigator widget.
Meanwhile, the Navigator widget uses pushnamed method so that we can pass the Shop Detail
Screen routename along with the product ID.
That’s the technique we have adopted in our Book Item controller, which is responsible for displaying
all the products on the home page.
Therefore, we can use Gesture Detector on tap method, so that user can tap the image and reach the
product detail screen, or page.
In that on tap method, we define that Navigator widget methods.

1 child: GridTile(
2 child: GestureDetector(
3 onTap: () {
4 Navigator.of(context).pushNamed(
5 ShopProductDetailScreen.routename,
6 arguments: product.id,
7 );
8 },
9 child: Image.network(
10 product.imageUrl,
11 fit: BoxFit.cover,
12 ),
13 ),
14 ...
15 // code is incomplete for brevity

How do you pass arguments in Navigator pushNamed?


To have a look at the screenshots we’ve used in this section, please visit this LINK³⁷
To pass arguments parameter in Navigator pushNamed method, we must have a named route.
Navigator widget is a widget that manages a set of child widgets. It also maintains a stack discipline.
In the last couple of flutter articles we have seen how to display a list of product items and, in
addition how we can click any item to see the product detail. We also have a dedicated GitHub
repository for that shopping cart app.
In that repository you’ll get the full code snippets. However, in this article, we’ll cut short our code
for brevity.
To see the product detail we have navigated to a named route from the home page.
³⁷https://sanjibsinha.com/navigator-pushnamed-arguments/
10. More on Flutter UI, List, Map, and Provider Best Practices 489

In our cases, we need to pass arguments to the named route, and navigated to the /product-details
route.
The Products Home Page will show all Products.
We can see all product items at a glance here. Moreover, we can have title and subtitle that gives us
an idea about the product.
However, to get the details of the product we need to navigate to another page. And for that we’ll
use Navigator widget pushNamed and pass the product ID as an argument.
To navigate to a new page we must define the routes first in our MaterialApp widget.

1 class ShopAppWithProvider extends StatelessWidget {


2 const ShopAppWithProvider({Key? key}) : super(key: key);
3 @override
4 Widget build(BuildContext context) {
5 return MaterialApp(
6 theme: ThemeData(
7 primarySwatch: Colors.brown,
8 primaryColor: Colors.deepOrange,
9 ),
10 home: const ShopHomePage(),
11 routes: {
12 ShopProductDetailScreen.routename: (context) =>
13 const ShopProductDetailScreen(),
14 },
15 );
16 }
17 }

Before that, we need to declare a static constant string as routename in the Shop Product Detail
Screen widget.

1 class ShopProductDetailScreen extends StatelessWidget {


2 const ShopProductDetailScreen({Key? key}) : super(key: key);
3 static const routename = "/product-detail";
4
5 @override
6 Widget build(BuildContext context) {
7 return Scaffold(
8 appBar: AppBar(
9 title: const Text('Product Detail Page'),
10 ),
11 body: const Center(
10. More on Flutter UI, List, Map, and Provider Best Practices 490

12 child: Text(
13 'We will get product detail here',
14 style: TextStyle(
15 fontSize: 30.0,
16 ),
17 ),
18 ),
19 );
20 }
21 }

How do I send an argument in pushNamed Flutter?


However, the question is, how we can send the argument in Navigator pushNamed Flutter?
To understand that context in this perspective, let us first take a look at the product details page.
We’ve not designed this page until now. However, it will give us an idea about how we can use an
argument and navigate to a named route.
Firstly, we get the products index using Provider and ChangeNotifierProvider value.

1 class ProductView extends StatelessWidget {


2 const ProductView({
3 Key? key,
4 }) : super(key: key);
5
6 @override
7 Widget build(BuildContext context) {
8 final products = Provider.of<Products>(context).products;
9 return GridView.builder(
10 padding: const EdgeInsets.all(10.0),
11 itemCount: products.length,
12 itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
13 value: products[i],
14 child: const ProductItem(),
15 ),
16 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
17 crossAxisCount: 2,
18 childAspectRatio: 3 / 2,
19 crossAxisSpacing: 10,
20 mainAxisSpacing: 10,
21 ),

You might also like