0% found this document useful (0 votes)
370 views

Sanjib Sinha - Better Flutter Learn Essential Concepts To Be A Better Flutter Developer-Leanpub - Com (2021)

Uploaded by

Erivelton Cruz
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
370 views

Sanjib Sinha - Better Flutter Learn Essential Concepts To Be A Better Flutter Developer-Leanpub - Com (2021)

Uploaded by

Erivelton Cruz
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 351

Better Flutter

Learn essential concepts to be a better


Flutter developer

Sanjib Sinha
This book is for sale at http://leanpub.com/betterflutter

This version was published on 2021-09-03

This is a Leanpub book. Leanpub empowers authors and


publishers with the Lean Publishing process. Lean Publishing is
the act of publishing an in-progress ebook using lightweight tools
and many iterations to get reader feedback, pivot until you have
the right book and build traction once you do.

© 2021 Sanjib Sinha


Contents

1. What is immutable in Flutter? Why Flutter widgets are


immutable? . . . . . . . . . . . . . . . . . . . . . . . . . . 1
What does Immutable mean? . . . . . . . . . . . . . . . . 1
Mutable or Immutable? . . . . . . . . . . . . . . . . . . . 2
Dart and Flutter are single threaded . . . . . . . . . . . . 3
What is the advantage and disadvantage of an
immutable class? . . . . . . . . . . . . . . . . . . . 6
How to change an immutable object? . . . . . . . . . . . 9
What are the benefits of Immutable Objects? . . . . . . . 11

2. Essential Widgets, From Layout to Stateful and Stateless 16


What do Widgets mean? . . . . . . . . . . . . . . . . . . . 16
Flutter basic Sate Management widget . . . . . . . . . . . 17
How many Widgets are there in Flutter . . . . . . . . . . 18
A Simple Flutter Application and basic Widget example 22
onGenerateRoute, Material Design and Material Compo-
nents . . . . . . . . . . . . . . . . . . . . . . . . . . 23
How do you use onGenerateRoute in Flutter? . . . . . . 23
How to use a dynamic initial route? . . . . . . . . . . . . 24
How do you define colors in Flutter? . . . . . . . . . . . 28
How build () method works how it rebuild itself flutter? 36
What is a container in flutter? Can container have
children flutter? . . . . . . . . . . . . . . . . . . . 43
How to change color of ElevatedButton in flutter . . . . 51
CONTENTS

What is the difference between container and column,


row in flutter? . . . . . . . . . . . . . . . . . . . . 57

3. Inherited Widget, Provider and State Management in


Flutter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
How we change the widget state in Flutter? . . . . . . . 73
How to manage state of child through parent widget? . . 75
How Child Widget implements this special property? . . 78
What is inherited widget in Flutter, how do you use state
management? . . . . . . . . . . . . . . . . . . . . . 80
What is InheritedWidget in Flutter? . . . . . . . . . . . . 81
Extending state management to the second uncle and his
child . . . . . . . . . . . . . . . . . . . . . . . . . . 92
What is flutter provider? How does provider flutter work? 93
How do you use Provider Consumer to manage State in
Flutter? . . . . . . . . . . . . . . . . . . . . . . . . 102
The Specific Steps to use Consumer widget . . . . . . . . 113
Provider: A recommended approach to manage State . . 115
Different approaches to state management . . . . . . . . 122
A Step by Step guide to use Provider . . . . . . . . . . . . 123
Multi Providers and Multi Models . . . . . . . . . . . . . 132
ChangeNotifier and Provider context read and watch,
when to use and how to use . . . . . . . . . . . . . 147
How can I improve my fluttering performance? . . . . . 148
How to avoid the anti-pattern and stick to the correct
design pattern in Flutter? . . . . . . . . . . . . . . 149
How can we get rid of widget rebuild in Flutter? . . . . . 150
How do you change the state in Flutter? . . . . . . . . . 151
What is ChangeNotifier? . . . . . . . . . . . . . . . . . . 152
What is the difference between stateful and stateless
widget in Flutter? . . . . . . . . . . . . . . . . . . . 154
What does provider do in Flutter? . . . . . . . . . . . . . 156
Provider helps you to avoid rebuilding widgets . . . . . . 158
14. Provider best practices: How to reduce widget rebuilds 160
How do you use a provider in Flutter? . . . . . . . . . . . 160
CONTENTS

How does stateful widget rebuilds the whole tree? . . . . 162


Model, View, Controller and Provider . . . . . . . . . . . 165
The Second Row Widget will listen to the Name Change
Model . . . . . . . . . . . . . . . . . . . . . . . . . 169
Third Row Widget listens to Name Clear Model . . . . . 171
Finally the view folder and main method . . . . . . . . . 173
Riverpod, a better Provider for state management . . . . 176
Riverpod has many options . . . . . . . . . . . . . . . . . 177
The greatest advantage of Riverpod . . . . . . . . . . . . 178
The Data Model on which we’ll work in Riverpod . . . . 179
Understanding Riverpod Scope . . . . . . . . . . . . . . . 181
How to watch a Provider object in Riverpod? . . . . . . . 183
How we can mix Riverpod Provider with old Provider
package? . . . . . . . . . . . . . . . . . . . . . . . . 185
The old Provider package is really gold! . . . . . . . . . . 188

4. Theme, Styling, Fonts and Images Best Practices in Flutter 191


What is theme flutter? . . . . . . . . . . . . . . . . . . . . 191
Using global theme to define color in flutter . . . . . . . 192
The model class and the main view . . . . . . . . . . . . 195
Controller widgets to control the flow of business logic . 199
Imposing uniformity of color through global theme . . . 202
How do you change the theme color globally in flutter . 205
Using multiple Google fonts in Flutter . . . . . . . . . . . 206
What fonts are available in Flutter? . . . . . . . . . . . . 209
How do you change the text color in Flutter? . . . . . . . 212
How do I add Google fonts to Flutter? . . . . . . . . . . . 213
Adding Google fonts package to Flutter . . . . . . . . . . 216
How to display image in Flutter? . . . . . . . . . . . . . . 219
Display image in Flutter using Image widget asset method 221
How to display image from internet in Flutter? . . . . . . 222
A Comparison between Old and New Updated Buttons . 224
How do you make a button flutter? . . . . . . . . . . . . 225
How do you use the raised button in flutter? . . . . . . . 226
How do you decorate the button in flutter? . . . . . . . . 228
CONTENTS

How to use DevTools in Flutter? . . . . . . . . . . . . . . 234

5. How to handle collections of items, all about List and


Map and Code Structure in Flutter . . . . . . . . . . . . 237
How do I add a list in Flutter? . . . . . . . . . . . . . . . 238
How do I show a list in flutter? . . . . . . . . . . . . . . . 241
Showing a list in flutter . . . . . . . . . . . . . . . . . . . 243
How to delete a list in Flutter? . . . . . . . . . . . . . . . 245
What is ListView Flutter? How do I use ListView in Flutter? 251
Is Flutter front end or backend? . . . . . . . . . . . . . . . 253
How do I change the date format in Flutter? . . . . . . . 261
How do you add items to a list in Flutter? . . . . . . . . . 266
How do I use ListView in Flutter? . . . . . . . . . . . . . 268
How to manage null-safety in Flutter . . . . . . . . . . . 275
How to structure your flutter app? . . . . . . . . . . . . . 277
How do I program my Flutter App? . . . . . . . . . . . . 278
What is the best architecture for Flutter? . . . . . . . . . 280
Role of widgets in Flutter . . . . . . . . . . . . . . . . . . 287
How do I improve my flutter app performance? . . . . . 291
How do you deal with navigation in Flutter? . . . . . . . 294

6. Everything about Flutter Navigation and Route . . . . 297


Why do you use onGenerateRoute in flutter? . . . . . . . 297
How do you use onGenerateRoute in Flutter? . . . . . . 298
How to use a dynamic initial route? . . . . . . . . . . . . 298
What is Flutter Navigation and how does Flutter Navi-
gator work? . . . . . . . . . . . . . . . . . . . . . . 300
How do you pass data from one class to another in flutter? 309
What is enum in Dart flutter? How to use enum in Flutter? 318
How do you change the theme on Flutter? . . . . . . . . 325
How do you name a route in Flutter? . . . . . . . . . . . 328
How do you pass data from one screen to another in flutter? 332
How do you make a Flutter app from scratch? . . . . . . 337
What Next? . . . . . . . . . . . . . . . . . . . . . . . . . . 344
1. What is immutable in
Flutter? Why Flutter
widgets are immutable?
Before getting started, let me tell you one thing. Always use
the latest Provider package¹ for state management. And always
maintain the Null Safety².
I also strongly recommend to read the latest and updated articles
on Flutter ³.

What does Immutable mean?


Immutable means unchangeable. With reference to the above ques-
tions we can say Flutter widgets are immutable because they don’t
want to change.
That’s why in Flutter’s documentation it says:
Use const widgets where possible. (This is equivalent to caching a
widget and re-using it.)
In the above statement ‘const’ means constant. And you know
constant value does not change. So Flutter team insists us to make
widgets immutable.
Why the flutter widgets don’t want to change? What is the problem,
if they change?
¹https://pub.dev/packages/provider
²https://flutter.dev/docs/null-safety
³https://sanjibsinha.com/category/flutter/
1. What is immutable in Flutter? Why Flutter widgets are immutable? 2

Well, we’ll discuss that and many more core concepts regarding this
topic.
But before we start, let’s try to understand what is the difference
between these two terms: mutable and immutable?
Either in object oriented programming, or in functional program-
ming, you cannot change an immutable object’s state once you’ve
created it.
Then how do we change the state of the object? Because that is
important. In flutter, changing object’s state is important. Isn’t it?
From animation to log in, everything involves state.
For more Flutter related Articles and Resources⁴

Mutable or Immutable?
Although widgets are immutable in Flutter, they maintain their
mutability in various ways.
Flutter’ stateful widgets are immutable. However, they store their
mutable state either in State objects. Or in Stream or ChangeNoti-
fier objects to which the state subscribes.
The createState method creates State objects in Flutter.
That’s how we can manage state in Flutter although they have
immutable widgets.
But it has a drawback. The widgets get rebuilt many times in this
process, and it directly impacts your app’s performance.
However, to make flutter app performant they make widgets im-
mutable so we can use identical instances, if the constructor pa-
rameters are same.
⁴https://sanjibsinha.com
1. What is immutable in Flutter? Why Flutter widgets are immutable? 3

That’s why, Flutter documentation insists to make widgets constant


as much as possible.
Consider this small example:

1 const SizedBox(
2
3 height: 10.0,
4
5 ),

This is much easier to cache now. So it helps you to make your app
more performant.
Wherever you use the above widget it won’t get rebuilt.
However, apart from Flutter performance, there is a broader spec-
trum around this debate. What should be our choice?
Mutable or Immutable?

Dart and Flutter are single threaded


That’s one of the main reason, why we should keep flutter widgets
immutable. Immutable objects are thread safe.
As the main thread does everything, Flutter uses three
asynchronous APIs - Future, Async and Await. We’ll talk
about later.
At present let’s try to understand what are advantages of im-
mutability.
Granted, many people don’t care about this understanding, but
when you understand this concept from deep inside Dart program-
ming, you can use Flutter with more confidence.
What is the advantage and disadvantage of a mutable class?
1. What is immutable in Flutter? Why Flutter widgets are immutable? 4

It’s obvious that the disadvantage of an immutable object is the


advantage of a mutable object.
An immutable object faces the biggest criticism for its separate
object creation for each distinct value. A mutable object does not
bother about that.
Therefore object oriented programming had started with the con-
cept of creating mutable before this concept of immutability hit the
stand!
Let us consider a small example in Dart programming.

1 class Account {
2 int _id;
3 int get id => _id;
4 int set(int id) => id = _id;
5 String nameOfBranch;
6 Account(this._id, this.nameOfBranch);
7 }
8
9 class AccountHolder {
10 String name;
11 String address;
12 int amount;
13 Account account;
14 AccountHolder({this.name, this.account, this.address, thi\
15 s.amount});
16 }

As you see, we are going to create mutable objects. To continue


we will first make two account holder objects. And they will have
different account id.
Think about a simple banking system. Here two persons with the
same name should be distinguished by their account ids. For that
reason we follow the same rule.
1. What is immutable in Flutter? Why Flutter widgets are immutable? 5

1 void main(List<String> args) {


2 var firstAccountHolder = AccountHolder(
3 name: 'John',
4 account: Account(
5 1,
6 'B. Garden.',
7 ),
8 address: 'ABC Rd.',
9 amount: 1005,
10 );
11 firstAccountHolder.account._id = 2;
12 firstAccountHolder.account._id = firstAccountHolder.accou\
13 nt.id.modPow(2, 4);
14 print(
15 'The account id of first account holder:'
16 ' ${firstAccountHolder.account.id}',
17 );
18 var secondAccountHolder = AccountHolder(
19 name: 'John',
20 account: Account(
21 2,
22 'B. Garden.',
23 ),
24 address: 'XYZ Rd.',
25 amount: 5005,
26 );
27 secondAccountHolder.account._id = secondAccountHolder.acc\
28 ount.id.modPow(2, 4);
29 print(
30 'The account id of second account holder:'
31 ' ${secondAccountHolder.account.id}',
32 );
33 }

However, while creating two separate objects, we also change their


state of id. Mutable objects allow us to do that. Then what is the
1. What is immutable in Flutter? Why Flutter widgets are immutable? 6

output?

1 The account id of first account holder: 0


2 The account id of second account holder: 0

If they were immutable objects, it would never happen.


When we change the state of a mutable object it’s original state gets
deleted. No history remains.
Can you think of any banking, insurance, or a health system where
no history remains?
Impossible!
Today in many cases capturing trends and traceable history be-
comes more and more critical. With the help of traditional rela-
tional database, it’s an impossible task. Moreover, today’s world is
data-centric.

What is the advantage and


disadvantage of an immutable
class?
Well, we can start with the same logic. Mutable object’s disadvan-
tage is the advantage of an immutable object.
Let’s create the same objects in a different way.
1. What is immutable in Flutter? Why Flutter widgets are immutable? 7

1 class Account {
2 final int id;
3 final String nameOfBranch;
4 const Account({this.id, this.nameOfBranch});
5 }
6
7 class AccountHolder {
8 final String name;
9 final String address;
10 final int amount;
11 final Account account;
12 const AccountHolder({this.name, this.account, this.addres\
13 s, this.amount});
14 }

Now inside our main method we are going to create two immutable
objects. Although these two account holder have the same name,
they have different account id.

1 final firstAccountholder = const AccountHolder(


2 name: 'John',
3 account: Account(id: 1, nameOfBranch: 'B. Garden'),
4 address: '12, ABC Rd.',
5 amount: 1256,
6 );
7 print(
8 'The first account holder\'s name: '
9 '${firstAccountholder.name}',
10 );
11
12 print(
13 'The state of the first account holder object: '
14 '${firstAccountholder.hashCode}',
15 );
16 print(
1. What is immutable in Flutter? Why Flutter widgets are immutable? 8

17 'The state of the first account holder name: '


18 '${firstAccountholder.name.hashCode}',
19 );
20 final secondAccountholder = const AccountHolder(
21 name: 'John',
22 account: Account(id: 2, nameOfBranch: 'B. Garden'),
23 address: '56, XYZ Rd.',
24 amount: 5256,
25 );
26 print(
27 'The second account holder\'s name: '
28 '${secondAccountholder.name}',
29 );
30 print(
31 'The second account holder\'s account id: '
32 '${secondAccountholder.account.id}',
33 );
34 print(
35 'The state of the second account holder\'s name: '
36 '${secondAccountholder.name.hashCode}',
37 );
38 print(
39 'The state of the second account holder object: '
40 '${secondAccountholder.hashCode}',
41 );

What will be the output if we want to run the above code?


Let’s see the output first.
1. What is immutable in Flutter? Why Flutter widgets are immutable? 9

1 The first account holder's name: John


2 The state of the first account holder object: 956332595
3 The state of the first account holder name: 48244218
4 The second account holder's name: John
5 The second account holder's account id: 2
6 The state of the second account holder's name: 48244218
7 The state of the second account holder object: 266236901

Nothing unusual has come out from the above output. Yet we have
tried to know two objects’ state in a different way.
As a result we can clearly see that two different immutable objects’
state are not same. The hash code is a single integer which repre-
sents the state of the object that affects operator == comparisons.
Since the both immutable objects have the same name, their hash
code is same. The state of the name is same for both.
But the biggest disadvantage of an immutable object raises its head
now.
Because we want to change the state of the both immutable objects.
Whatever the reason, we cannot change these two objects any way
since we’ve already created them.
We can only create copy of these two immutable objects again.
When this process of changing takes place if the volume of data
is too big, performance may matter.

How to change an immutable


object?
The only way to change any state of them is to create the objects
again with different state.
Both account holders want to change their addresses. At the same
time they have saved some money. So we need enter that data too.
1. What is immutable in Flutter? Why Flutter widgets are immutable? 10

But what will happen if inadvertently a bank employee changes the


second account holder’s account id to 1. It should have been 2.
Exactly the same thing happens here.

1 /// we wanted to modify the second account holder's amount


2 /// but inadvertently modify the account id also
3 final firstCopyOfSecondAccountholder = const AccountHolde\
4 r(
5 name: 'John',
6 account: Account(id: 1, nameOfBranch: 'B. Garden'),
7 address: '458, MNC Rd.',
8 amount: 5256,
9 );
10 print(
11 'The account id of the second account holder after modifi\
12 cation: '
13 '${firstCopyOfSecondAccountholder.account.id}',
14 );
15 print(
16 'The state of the second account holder\'s name after mod\
17 ification: '
18 '${firstCopyOfSecondAccountholder.name.hashCode}',
19 );
20 print(
21 'The state of the second account holder object after modi\
22 fication: '
23 '${firstCopyOfSecondAccountholder.hashCode}',
24 );

Although the bank employee has made a mistake, we don’t have to


worry now. That’s the biggest advantage of any immutable object.
We have the original object which has a traceable history now.
1. What is immutable in Flutter? Why Flutter widgets are immutable? 11

1 The state of the first account holder's name after modifi\


2 cation: 48244218
3 The state of the first account holder object after modifi\
4 cation: 822124965
5 The account id of the second account holder after modific\
6 ation: 1
7 The state of the second account holder's name after modif\
8 ication: 48244218
9 The state of the second account holder object after modif\
10 ication: 854943846

One of the main advantages is we can easily find the bug. Im-
mutable objects are better readable, maintainable.
You can pass it around without worry.
In Flutter, it has less memory footprints. So it is performant.

What are the benefits of Immutable


Objects?
Flutter documentation has already mentioned it. Ease of caching.
But there are more. With reference to the above program, we can
easily prevent the identity mutation.
What we have seen in the above code? The second account holder’s
account id becomes same as the first.
We can prevent it easily.
1. What is immutable in Flutter? Why Flutter widgets are immutable? 12

1 secondAccountholder.name == firstCopyOfSecondAccountholde\
2 r.name
3 ? print('Second account holder\'s name unchanged.')
4 : print('Error in second account holder\'s name.');
5 secondAccountholder.account.id == firstCopyOfSecondAccoun\
6 tholder.account.id
7 ? print('Second account holder\'s id unchanged.')
8 : print('Error in second account holder\'s id.');
9
10 firstAccountholder.name.hashCode ==
11 firstCopyOfFirstAccountholder.name.hashCode
12 ? print('true')
13 : print('false');
14
15 /// prints true; that means object state has not been cha\
16 nged
17
18 secondAccountholder.account.id.hashCode ==
19 firstCopyOfSecondAccountholder.account.id.hashCode
20 ? print('true') : print('false');

If we add this part with our last part of our code. We can catch the
error in an easy way.
Now running the code gives us this output:

1 First account holder's name unchanged.


2
3 First account holder's id unchanged.
4
5 Second account holder's name unchanged.
6
7 Error in second account holder's id.
8
9 true
1. What is immutable in Flutter? Why Flutter widgets are immutable? 13

10
11 false

Can we avoid this failure in a more efficient way?


Yes, we can. Dart allows us to deal with the same problem in a more
efficient way.
Now we can declare the same classes with a copyWith method. That
allows us to update in a safe way.
Moreover, you can now use the original immutable object state.
Suppose, the name of the account holder, or the account id of every
account holder should not be changed forever.
Consider this first part of code:

1 class Account {
2 final int id;
3 final String nameOfBranch;
4 const Account(this.id, this.nameOfBranch);
5 Account copyWith(int id, String nameOfBranch) {
6 return Account(id ?? this.id, nameOfBranch ?? this.nameOf\
7 Branch);
8 }
9 }
10
11 class AccountHolder {
12 final String name;
13 final String address;
14 final int amount;
15 final Account account;
16 const AccountHolder(this.name, this.account, this.address\
17 , this.amount);
18 AccountHolder copyWith(
19 String name, Account account, String address, int amount)\
20 {
1. What is immutable in Flutter? Why Flutter widgets are immutable? 14

21 return AccountHolder(name ?? this.name, account ?? this.a\


22 ccount,
23 address ?? this.address, amount ?? this.amount);
24 }
25 }

Now when we update, we can prevent any error easily.


Let us create the first account holder object as usual:

1 final firstAccountholder =
2
3 constAccountHolder('John',Account(1,'B. Garden'),'12, ABC\
4 Rd.',1256);

Next comes the crucial part of updating the immutable object’s


state.
Suppose we want to change both the address and amount of the first
account holder. However, we don’t want to do the same mistake, we
did earlier.
We want to keep the name and account id same as the original.

1 final updateFirstAccountHolder = firstAccountholder.copyW\


2 ith(
3 firstAccountholder.name,
4 Account(firstAccountholder.account.id, 'B. Garden'),
5 '123 MNC Rd',
6 4561,
7 );

Each time we update the first account holder’s state we can now
use the original state where we need it.
If we want to accomplish more than one goal at a time, we need to
sacrifice.
1. What is immutable in Flutter? Why Flutter widgets are immutable? 15

Granted we need to create a new object to update any immutable


object. But it is not only thread safe, or it has no side effects, but
we can prevent the error at the very beginning.
For more Flutter related Articles and Resources⁵
⁵https://sanjibsinha.com
2. Essential Widgets,
From Layout to Stateful
and Stateless
Knowledge of using Widgets on Flutter directly impacts the Flutter
UI design. Therefore, we need to keep in mind that Flutter UI is
built out of Widgets.
As an extension to the above sentence, we can say, in Flutter
widgets are everything.
Therefore we need to memorize basic widgets. If we cannot find a
solution, we can check the official Flutter Widget catalogs.
Let us make it clear at the very beginning that without widgets we
cannot start building our Flutter UI design.
For another thing, we need to know how to build our widgets tree
logically.
Depending on how we arrange widgets sub-trees, the User Interface
gets the final shape.
Finally, while writing our application, we will use widgets that are
sub-classes of either ‘StatelessWidget or StatefulWidget’.
We have discussed about Flutter State Management earlier.

What do Widgets mean?


Although Widget is a class, in general, the main job of a widget is
the implementation of a build() function.
2. Essential Widgets, From Layout to Stateful and Stateless 17

This build() function returns the root widget, which in turn builds
the UI with the help of lower-level widgets.
The job of Flutter framework is to build those widgets, synchroniz-
ing one widget with another.
This process keeps the process moving on until the process reaches
the low point to represent the ‘RenderObject’, which computes and
gives the representation of the final UI design.
We need to remember that widgets are passed as arguments to other
widgets.
We have already seen how one widget takes a number of different
widgets as named arguments.

Flutter basic Sate Management


widget

We have seen that our Flutter application ‘MyFirstApp()’ extends


‘StatelessWidget’ class and the calls the Widget build() function that
passes ‘BuildContext context’ as its only argument.
The ‘MaterialApp’ widget acts as the root widget. By and large, it
passes two named arguments ‘title’ and ‘home’.
The ‘title’ could be a simple text, the title of the application we are
going to build. Both are passed as arguments to other widgets, one
of them is Scaffold(), another important widget.
In turn, the Scaffold widget takes a number of different widgets as
named arguments, of which ‘AppBar’ widget plays a major role.
The ‘AppBar’ widget again passes different types of widgets as
named arguments.
We can define our title widget here also. This pattern of calling and
adding lower-level widgets one after another, goes on until your
design is complete.
2. Essential Widgets, From Layout to Stateful and Stateless 18

How many Widgets are there in


Flutter
It is difficult to say as the number of Widgets are being updated,
more or less on a regular basis.
As we were saying, we need to use hundreds of widgets to build
our UI.
However, keeping all the widgets in one place, especially in
‘main.dart’ file,does not look clean. It also makes our code
unnecessarily lengthy, hard to debug.
The advantage of object-oriented programming is that we can use
our objects as different module.
It keeps the modular nature of coding. The objects should not be
tightly coupled, they should remain loosely coupled.

Breaking Code in Different Dart files

For that reason, we will use break our code snippets in different
dart files and finally import them in the ‘main.dart’ file.
We will see the code snippet first, after that we will see how it
affects our UI design. After that we will discuss the widgets used
in our code.
Let us check our first Dart file.
2. Essential Widgets, From Layout to Stateful and Stateless 19

1 // my_appbar.dart
2 import 'package:flutter/material.dart';
3
4 class MyAppBar extends StatelessWidget {
5 MyAppBar({this.title});
6
7 final Widget title;
8
9 @override
10 Widget build(BuildContext context) {
11 return Container(
12 height: 116.0,
13 decoration: BoxDecoration(color: Colors.redAccent),
14
15 child: Row(
16
17 children: <Widget>[
18 IconButton(
19 icon: Icon(Icons.menu),
20 tooltip: 'Navigation menu',
21 onPressed: null,
22 ),
23
24 Expanded(
25 child: title,
26 ),
27 IconButton(
28 icon: Icon(Icons.search),
29 tooltip: 'Search',
30 onPressed: null,
31 ),
32 ],
33 ),
34 );
35 }
2. Essential Widgets, From Layout to Stateful and Stateless 20

36 }

Then we will have the second Dart file.

1 //my_scaffold.dart
2 import 'package:flutter/material.dart';
3 import 'package:my_first_flutter_app/chap5_widgets/my_app\
4 bar.dart';
5
6 class MyScaffold extends StatelessWidget {
7 @override
8 Widget build(BuildContext context) {
9
10 return Material(
11
12 child: Column(
13 children: <Widget>[
14 MyAppBar(
15 title: Text(
16 'Test Your Knowledge...',
17 style: Theme.of(context).primaryTextTheme.headline6,
18 ),
19 ),
20 Expanded(
21 child: Center(
22 child: Text('Here we will place our body widget...',
23 style: TextStyle(fontSize: 25),
24 ),
25 ),
26 ),
27 ],
28 ),
29 );
30 }
31 }
2. Essential Widgets, From Layout to Stateful and Stateless 21

And, finally we have the main Dart file through which our applica-
tion will run.

1 //main.dart
2 import 'package:flutter/material.dart';
3 import 'package:my_first_flutter_app/chap5_widgets/my_sca\
4 ffold.dart';
5
6 void main() {
7 runApp(MyFirstApp());
8 }
9
10 class MyFirstApp extends StatelessWidget {
11 @override
12 Widget build(BuildContext context) {
13 return MaterialApp(
14 title: 'My app',
15 home: MyScaffold(),
16 );
17 }
18 }

We have used three different Dart files. While talking about this, we
need to remember that either we can keep these files in ‘lib’ folder.
Or, we can create separate folders inside the ‘lib’ folder, and keep
those files there.
Wherever, we keep this file, while importing we have to mention
the full path.
We will come to that point in a minute, before we need to see how
our widgets build the UI.
2. Essential Widgets, From Layout to Stateful and Stateless 22

A Simple Flutter Application and


basic Widget example
It represents a simple UI design. To make it happen we have used
three different Dart files to make our code clean.
Now, let us watch the widgets used in those files.
Let us start with ‘my_appbar.dart’ file. It describes the ‘AppBar’
widget.
We could have called it directly, while building the UI.
However, we have decided to keep it in a generic class, where we
have passed one named argument ‘tile’ through its constructor.

1 MyAppBar({this.title});
2 final Widget title;

Fields (here ‘title’) in a Widget subclass (here ‘MyAppBar’) are


always marked “final”. It is a convention, and we will maintain
always.
Here as a lower-level widget we first use the ‘Container’.
That widget, in turn, passes many named arguments as lower-level
widgets.
As the ‘child’ it passes the ‘Row’ widget and inside passes a list of
‘children’ widgets.

1 children: <Widget>[]

The above line describes what type of Widget we should pass as a


list. We have learned the syntax of list data structure in Dart.
Whenever we use the lower-level widgets we always maintain the
structure of hierarchies.
A mobile screen will finally render this UI design. We have to build
our widget trees keeping that in our mind.
2. Essential Widgets, From Layout to Stateful and Stateless 23

onGenerateRoute, Material Design


and Material Components
In some cases, we want the user to log in to enter the app. Otherwise
the new user can also register on that same page.
As a result, on opening the app, the route takes the user to a certain
page.
Good news is, Flutter has a built-in feature for achieving such a feat.
Moreover, we can control the navigation at the very beginning of
our Flutter app.
To do this, in flutter MaterialApp widget, we use onGenerateRoute
property.
Material design and material components play a key role in build-
ing a Flutter app. In addition, the material design system unites
style, branding, interaction and motion using a set of material
components.
And these material components work under material design’s
principles. That makes the usage of onGenerateRoute property in
flutter.

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.
2. Essential Widgets, From Layout to Stateful and Stateless 24

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 setting\
2. Essential Widgets, From Layout to Stateful and Stateless 25

19 s) {
20 if (settings.name != '/second') {
21 return null;
22 }
23
24 return MaterialPageRoute<void>(
25 settings: settings,
26 builder: (BuildContext context) => MDSecondPage(),
27 fullscreenDialog: true,
28 );
29 }
30 }

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.
2. Essential Widgets, From Layout to Stateful and Stateless 26

Figure 2.1 – onGenerateRoute navigation technique in Flutter

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.
2. Essential Widgets, From Layout to Stateful and Stateless 27

Figure 2.2 – onGenerateRoute navigation technique in Flutter using home


property

For full code, you can check the GitHub repository for this post, and
related posts.
Before adding custom color in Flutter we need to keep a few things
in mind.
2. Essential Widgets, From Layout to Stateful and Stateless 28

Why we need a custom color? How we can use that custom theme
color throughout an app?
Let me answer the first question first. Because we need a syn-
chronization throughout the app, we need a custom color theme.
Moreover, when the user moves from one screen to the other, she
finds a similarity in theme.
However, the journey starts from the material design and compo-
nents part. And it answers our second question.
The MaterialApp plays the key role here to set the custom color
theme.

How do you define colors in Flutter?


First of all, we need to fill our ThemeData widget properties with
custom colors. In addition it will not only help us to maintain a
custom theme, but at the same time, we can also change colors at
one place like the following:

1 ThemeData _customTheme() {
2 return ThemeData(
3 accentColor: Color(0xFF442B2D),
4 primaryColor: Color(0xFFFEDBD0),
5 buttonColor: Color(0xFFFEDBD0),
6 scaffoldBackgroundColor: Colors.white,
7 cardColor: Color(0xFF883B2D),
8 textSelectionTheme: TextSelectionThemeData(
9 selectionColor: Color(0xFFFEDBD0),
10 ),
11 errorColor: Colors.red,
12 buttonTheme: ThemeData.light().buttonTheme.copyWith(
13 buttonColor: Color(0xFFFEDBD0),
14 colorScheme: ThemeData.light().colorScheme.copyWith(
2. Essential Widgets, From Layout to Stateful and Stateless 29

15 secondary: Color(0xFF442B2D),
16 ),
17 ),
18 buttonBarTheme: ThemeData.light().buttonBarTheme.copyWith(
19 buttonTextTheme: ButtonTextTheme.accent,
20 ),
21 primaryIconTheme: ThemeData.light().primaryIconTheme.copy\
22 With(
23 color: Color(0xFF442B2D),
24 ),
25 );
26 }

Wherever we keep this custom colors theme we need to call this


method inside MaterialApp. Because MaterialApp theme property
returns ThemeData constructor,we can use the custom color theme
method there.

1 class MaterialDesignThemeControl extends StatelessWidget {


2 const MaterialDesignThemeControl({Key? key}) : super(key:\
3 key);
4
5 @override
6 Widget build(BuildContext context) {
7 // ignore: todo
8 // TODO: building custom theme that'll control color and \
9 text
10 return MaterialApp(
11 title: 'Material Design Theme Control',
12 home: MaterialDesignCustomTheme(),
13 theme: _customTheme(),
14 debugShowCheckedModeBanner: false,
15 );
16 }}

The following line is important here.


2. Essential Widgets, From Layout to Stateful and Stateless 30

1 theme: _customTheme(),

As a result, we can have this output in our virtual device.


2. Essential Widgets, From Layout to Stateful and Stateless 31

Figure 2.3 – How do you change colors in Flutter?

Consequently, changing colors in Flutter using a custom theme


2. Essential Widgets, From Layout to Stateful and Stateless 32

becomes easy now.


In one single file, you can add more functionalities. Moreover, from
MaterialApp, now you can control the color theme throughout the
app.
We can control AppBar color through Scaffold widget.

1 class MaterialDesignCustomTheme extends StatelessWidget {


2 const MaterialDesignCustomTheme({Key? key}) : super(key: \
3 key);
4
5 @override
6 Widget build(BuildContext context) {
7 return Scaffold(
8 appBar: AppBar(
9 title: Text(
10 'Material Design Custom Theme',
11 style: TextStyle(
12 fontSize: 20,
13 color: Theme.of(context).primaryColorDark,
14 ),
15 ),
16 backgroundColor: Theme.of(context).scaffoldBackgroundColo\
17 r,
18 ),
19 body: CustomPage(),
20 );
21 }
22 }

After that, we can use a CustomPage widget where we can build


the page using our custom theme.
2. Essential Widgets, From Layout to Stateful and Stateless 33

1 class MaterialDesignCustomTheme extends StatelessWidget {


2 const MaterialDesignCustomTheme({Key? key}) : super(key: \
3 key);
4
5 @override
6 Widget build(BuildContext context) {
7 return Scaffold(
8 appBar: AppBar(
9 title: Text(
10 'Material Design Custom Theme',
11 style: TextStyle(
12 fontSize: 20,
13 color: Theme.of(context).primaryColorDark,
14 ),
15 ),
16 backgroundColor: Theme.of(context).scaffoldBackgroundColo\
17 r,
18 ),
19 body: CustomPage(),
20 );
21 }
22 }
23
24 class CustomPage extends StatelessWidget {
25 const CustomPage({Key? key}) : super(key: key);
26
27 @override
28 Widget build(BuildContext context) {
29 return ListView(
30 children: [
31 Container(
32 margin: EdgeInsets.all(10),
33 padding: EdgeInsets.all(5),
34 decoration: BoxDecoration(
35 border: Border.all(
2. Essential Widgets, From Layout to Stateful and Stateless 34

36 width: 5,
37 color: Theme.of(context).accentColor,
38 ),
39 ),
40 child: Text(
41 'Material Design Custom Theme Page',
42 style: TextStyle(
43 fontSize: 30,
44 fontWeight: FontWeight.bold,
45 color: Theme.of(context).cardColor,
46 ),
47 ),
48 ),
49 Container(
50 margin: EdgeInsets.all(10),
51 padding: EdgeInsets.all(8),
52 child: Card(
53 elevation: 30,
54 shadowColor: Theme.of(context).cardColor,
55 child: Container(
56 margin: EdgeInsets.all(10),
57 padding: EdgeInsets.all(8),
58 child: Column(
59 children: [
60 TextField(
61 decoration: InputDecoration(
62 labelText: 'Username',
63 labelStyle: TextStyle(
64 color: Theme.of(context).primaryColorLight,
65 ),
66 ),
67 ),
68 SizedBox(height: 12.0),
69 TextField(
70 decoration: InputDecoration(
2. Essential Widgets, From Layout to Stateful and Stateless 35

71 labelText: 'Password',
72 labelStyle: TextStyle(
73 color: Theme.of(context).primaryColorLight,
74 ),
75 ),
76 obscureText: true,
77 ),
78 ButtonBar(
79 children: <Widget>[
80 TextButton(
81 child: Text(
82 'CANCEL',
83 style: TextStyle(
84 color: Theme.of(context).buttonColor,
85 ),
86 ),
87 onPressed: () {},
88 ),
89 ElevatedButton(
90 child: Text(
91 'NEXT',
92 style: TextStyle(
93 color: Theme.of(context).buttonColor,
94 ),
95 ),
96 onPressed: () {},
97 ),
98 ],
99 ),
100 ],
101 ),
102 ),
103 ),
104 )
105 ],
2. Essential Widgets, From Layout to Stateful and Stateless 36

106 );
107 }
108 }

For full code please visit my GitHub repository⁶

How build () method works how it


rebuild itself flutter?
Calling the setState() method in a stateful widget in Flutter calls the
build() method first. Not only that, the build() method rebuilds all
the descendant widgets.
To enhance your fluttering performance, you need to understand
the inner mechanism of Flutter. However, before that, as an anec-
dote, we can remember the famous saying about Flutter.
What does that say?
It says, Flutter is all about Widget. Of course, it’s true, and besides,
there are other factors that we must be aware of.

How can I improve my fluttering


performance?

Your fluttering performance depends on many factors. One of them


depends entirely on your understanding of how Flutter rebuilds its
widget tree.
However, the tree rebuilding journey of Flutter does not stop there.
The element trees refer to the state objects and, finally, it goes to
the rendered object tree.
⁶https://github.com/sanjibsinha/better_flutter_chapter_two
2. Essential Widgets, From Layout to Stateful and Stateless 37

No doubt, although it’s a complex architecture of Flutter, still


Flutter manages it internally.
The setState() only rebuilds widgets. The rebuilding mechanism
doesn’t touch the element tree and the rendered object tree. They
only get the reference of the changed state object.

How do you rebuild in Flutter?

Well, to answer this question, we must take a look at the following


image. Although it’s a very simple counter, we have passed the state
object through two more custom widget constructor.

Figure 2.4 – A counter app in Flutter

Moreover, to understand the rebuild() mechanism, we’ve added


a simple print() method that gives us some outputs with each
rebuild() of a widget.
2. Essential Widgets, From Layout to Stateful and Stateless 38

How flutter widgets get rebuilt

As you can see we’ve pressed the button 5 times. And for that
reason, we get this output in our terminal:

1 print _MyHomePageState
2 print CenterWidget
3 print ColumnWidget
4 print _MyHomePageState
5 print CenterWidget
6 print ColumnWidget
7 print _MyHomePageState
8 print CenterWidget
9 print ColumnWidget
10 print _MyHomePageState
11 print CenterWidget
12 print ColumnWidget
13 print _MyHomePageState
14 print CenterWidget
15 print ColumnWidget
16 print _MyHomePageState
17 print CenterWidget
18 print ColumnWidget

There are six sets, because whenever you run the Flutter app, the
setState() calls the build() method and it displays the number 0 on
the screen.
Next, each time you press the button, it rebuilds the descendant
widget trees also. If we quit the app and restarts the app again, it
starts with a fresh display of a screen.

Flutter hot restart


However, the hot restart also rebuilds the widget trees just once,
like this:
2. Essential Widgets, From Layout to Stateful and Stateless 39

1 Performing hot restart... 88ms


2 Restarted application in 88ms.
3 print _MyHomePageState
4 print CenterWidget
5 print ColumnWidget

We have passed the counter state object through two widgets


constructors. CenterWidget and ColumnWidget. So that we can
understand the rebuilding process.

Figure 2.5 – Flutter hot restart rebuilds the widget tree once

What is build() in Flutter?

Now it makes sense why we have added a print() method with each
build() method of each widget.
Every time the setState() method in the state full widget calls the
build() method, the descendant widget trees’ build() method also
rebuilds the associated widget.
2. Essential Widgets, From Layout to Stateful and Stateless 40

Now we can take a look at the simple code snippet, that we have
used to demonstrate the flutter rebuilding inner mechanism.

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
15 primarySwatch: Colors.blue,
16 ),
17 home: MyHomePage(title: 'Flutter Demo Home Page'),
18 );
19 }
20 }
21
22 class MyHomePage extends StatefulWidget {
23 MyHomePage({Key? key, this.title}) : super(key: key);
24
25 final String? title;
26
27 @override
28 _MyHomePageState createState() => _MyHomePageState();
29 }
30
31 class _MyHomePageState extends State<MyHomePage> {
32 int _counter = 0;
33
2. Essential Widgets, From Layout to Stateful and Stateless 41

34 void _incrementCounter() {
35 setState(() {
36
37 _counter++;
38 });
39 }
40
41 @override
42 Widget build(BuildContext context) {
43
44 title: Text(widget.title!),
45 ),
46 body: CenterWidget(
47 counter: _counter,
48 ),
49 floatingActionButton: FloatingActionButton(
50 onPressed: _incrementCounter,
51 tooltip: 'Increment',
52 child: Icon(Icons.add),
53 ), // This trailing comma makes auto-formatting nicer for\
54 build methods.
55 );
56 }
57 }
58
59 class CenterWidget extends StatelessWidget {
60
61 const CenterWidget({
62 Key? key,
63 required this.counter,
64 }) : super(key: key);
65
66 final counter;
67
68 @override
2. Essential Widgets, From Layout to Stateful and Stateless 42

69 Widget build(BuildContext context) {


70 print('print CenterWidget');
71 return Center(
72
73 child: ColumnWidget(counter: counter),
74 );
75 }
76 }
77
78 class ColumnWidget extends StatelessWidget {
79 const ColumnWidget({
80 Key? key,
81 required this.counter,
82 }) : super(key: key);
83
84 final counter;
85
86 @override
87 Widget build(BuildContext context) {
88 print('print ColumnWidget');
89 return Column(
90
91 mainAxisAlignment: MainAxisAlignment.center,
92 children: <Widget>[
93 Text(
94 'You have pushed the button this many times:',
95 ),
96 Text(
97 '$counter',
98 style: Theme.of(context).textTheme.headline4,
99 ),
100 ],
101 );
102 }
103 }
2. Essential Widgets, From Layout to Stateful and Stateless 43

Although Flutter is all about widgets, and no doubt, by and large it’s
true, yet we shouldn’t forget about the element tree and rendered
object tree that Flutter manages under the hood.
Each time we call the setState() method, it reruns the build()
method. Flutter optimizes this rerunning process, and does that
with the help of element and rendered object trees that we don’t
see.
However, we can see the build() rerun process with the help of
print() method,which we’ve exactly done.

What is setState in Flutter?

With reference to the state object we can say, setState() tells the
Flutter framework that something has changed in this State. This
change fires the build() method to rerun. Moreover, it reflects the
change on the screen.
The display can now reflect the change on the screen.
For full code for this part please visit my GitHub repository⁷

What is a container in flutter? Can


container have children flutter?
Container is a convenience widget in flutter. What does that mean?
We can do a lot of things inside a container widget that takes a child
widget. However, we do that according to our convenience.
What do we mean by ‘doing a lot of things’? Of course, that tells us
mostly about adding styles to a container. Moreover, it means we
can style our container by adding margin, padding, color, etc. We
can even border the container with a certain width.
⁷https://github.com/sanjibsinha/better_flutter_chapter_two_extension
2. Essential Widgets, From Layout to Stateful and Stateless 44

Now, question arises, whether a container can have children? The-


oretically, it is possible. A child widget can have a list of children.
In that case, a container will size itself to the size of the incoming
children.
Otherwise, it tries to be as small as possible.

What is a container in flutter?

As we have said before, container is a convenience widget that we


can decorate by applying different type of styles.
To make our statement stand on a solid ground, the best option is
to see how we can do that.
With reference to what we have said about container widget, let us
style our container with two child widget. And these two widgets
are none other than our familiar Text widget.
2. Essential Widgets, From Layout to Stateful and Stateless 45

Figure 2.6 – How to adjust positions of two container widgets

As you can see, we have positioned both the Text widgets applying
some styles.
How did we do that?
2. Essential Widgets, From Layout to Stateful and Stateless 46

Container and its properties


To make our container look more stylish we use to supply the right
elements to our container properties.
Just to be on a safe side, we have placed our two container widgets
inside a ListView widget, which we can scroll further down. In case,
we need to add more widgets, or we develop this application to
compare container widget with other basic widgets like AppBar,
Column and Row.
Here goes our code:

1 import 'package:flutter/material.dart';
2
3 class AddNameList extends StatefulWidget {
4 @override
5 _AddNameListState createState() => _AddNameListState();
6 }
7
8 class _AddNameListState extends State<AddNameList> {
9 final textController = TextEditingController();
10 @override
11 Widget build(BuildContext context) {
12 return Scaffold(
13 appBar: AppBar(
14 title: Text('Add Expenditures'),
15 ),
16 body: Center(
17 child: ListView(
18 children: [
19 Container(
20 margin: EdgeInsets.all(8),
21 padding: EdgeInsets.all(8),
22 decoration: BoxDecoration(
23 color: Colors.blue[50],
24 border: Border.all(
2. Essential Widgets, From Layout to Stateful and Stateless 47

25 color: Colors.redAccent,
26 ),
27 ),
28 alignment: Alignment.topCenter,
29 child: const Text(
30 'I am at the top and positioned center.',
31 style: TextStyle(
32 fontWeight: FontWeight.bold,
33 fontSize: 20,
34 ),
35 ),
36 ),
37 Container(
38 margin: EdgeInsets.all(8),
39 padding: EdgeInsets.all(8),
40 decoration: BoxDecoration(
41 color: Colors.blue[100],
42 border: Border.all(
43 color: Colors.black45,
44 width: 8,
45 ),
46 ),
47 alignment: Alignment.bottomLeft,
48 child: const Text(
49 'I am at the bottom and positioned left.',
50 style: TextStyle(
51 fontWeight: FontWeight.bold,
52 fontSize: 20,
53 ),
54 ),
55 ),
56 const SizedBox(
57 height: 8,
58 ),
59 ],
2. Essential Widgets, From Layout to Stateful and Stateless 48

60 ),
61 ),
62 );
63 }
64 }

For the full application code snippets please visit the GitHub
repository of the related post.

How Container properties work?

To see how container widget properties work, we need to check the


container widget code carefully.
Take a look at the to container widget.

1 Container(
2 margin: EdgeInsets.all(8),
3 padding: EdgeInsets.all(8),
4 decoration: BoxDecoration(
5 color: Colors.blue[50],
6 border: Border.all(
7 color: Colors.redAccent,
8 ),
9 ),
10 alignment: Alignment.topCenter,
11 child: const Text(
12 'I am at the top and positioned center.',
13 style: TextStyle(
14 fontWeight: FontWeight.bold,
15 fontSize: 20,
16 ),
17 ),
18 ),
2. Essential Widgets, From Layout to Stateful and Stateless 49

We pass the value either through constructors or call any static


method. However, they must synchronize with other properties.
Next, take a look at the second container that we have placed at the
bottom.

1 Container(
2 margin: EdgeInsets.all(8),
3 padding: EdgeInsets.all(8),
4 decoration: BoxDecoration(
5 color: Colors.blue[100],
6 border: Border.all(
7 color: Colors.black45,
8 width: 8,
9 ),
10 ),
11 alignment: Alignment.bottomLeft,
12 child: const Text(
13 'I am at the bottom and positioned left.',
14 style: TextStyle(
15 fontWeight: FontWeight.bold,
16 fontSize: 20,
17 ),
18 ),
19 ),

Not only we have changed the alignment, but along with that we
have added a border and changed the color of the container.
For that reason, it looks different, now.
2. Essential Widgets, From Layout to Stateful and Stateless 50

Figure 2.7 – Container with border decoration


2. Essential Widgets, From Layout to Stateful and Stateless 51

How to change color of


ElevatedButton in flutter
Flutter team has recently deprecated the FlatButton , RaisedButton
and OutlineButton widgets.
Although we don’t use those buttons, yet in their place we can use
TextButton , ElevatedButton , and OutlinedButton.
However, the ElevatedButton comes with default blue color. Some-
times it makes beginners scratch their heads. How to change this
default color of ElevatedButton?
The question they ask, how we can change the color of the Elevat-
edButton.
Not only this question bears some importance, but also it helps us
to dig deep into its definition.

How do you change the color of


ElevatedButton in flutter?

The ElevatedButton class has come with many properties.Most of


them as usual pass through the constructor.
But some of them are static, like the following.

1 static ButtonStyle styleFrom({


2 Color? primary,
3 Color? onPrimary,
4 Color? onSurface,
5 Color? shadowColor,
6 double? elevation,
7 TextStyle? textStyle,
8 EdgeInsetsGeometry? padding,
9 Size? minimumSize,
2. Essential Widgets, From Layout to Stateful and Stateless 52

10 Size? fixedSize,
11 BorderSide? side,
12 OutlinedBorder? shape,
13 MouseCursor? enabledMouseCursor,
14 MouseCursor? disabledMouseCursor,
15 VisualDensity? visualDensity,
16 MaterialTapTargetSize? tapTargetSize,
17 Duration? animationDuration,
18 bool? enableFeedback,
19 AlignmentGeometry? alignment,
20 InteractiveInkFeatureFactory? splashFactory,
21 })

You can check it by going to the definition in your Visual Studio


Code.
Suppose we want to place several ElevatedButton in a Row. At the
same time we want to arrange them systematically so they would
not break our app structure.
To do that, we can use Wrap widget, first. Second, we can use the
static method styleFrom().

How do you use the elevated button on


flutter?

To make it happen we can keep all those ElevatedButton widgets


inside Conatiner widgets. That is the first step. However, that does
not end our work.
The next step, which is really important, tells us to keep them
wrapped under the Wrap widget.
2. Essential Widgets, From Layout to Stateful and Stateless 53

1 import 'package:flutter/material.dart';
2
3 class ContainerRowColumnModified extends StatelessWidget {
4 const ContainerRowColumnModified({Key, key}) : super(key:\
5 key);
6
7 @override
8 Widget build(BuildContext context) {
9 return ListView(
10 children: [
11 Container(
12 margin: EdgeInsets.all(8),
13 padding: EdgeInsets.all(15),
14 alignment: Alignment.topCenter,
15 decoration: BoxDecoration(
16 color: Colors.purple,
17 border: Border.all(
18 width: 5,
19 color: Colors.grey,
20 ),
21 ),
22 child: Column(
23 mainAxisAlignment: MainAxisAlignment.center,
24 children: [
25 Card(
26 child: Column(
27 mainAxisSize: MainAxisSize.min,
28 children: <Widget>[
29 const ListTile(
30 leading: Icon(Icons.album),
31 title: Text('Books'),
32 subtitle: Text('25.00'),
33 ),
34 const ListTile(
35 leading: Icon(Icons.album),
2. Essential Widgets, From Layout to Stateful and Stateless 54

36 title: Text('Groceries'),
37 subtitle: Text('250.00'),
38 ),
39 const ListTile(
40 leading: Icon(Icons.album),
41 title: Text('Fruits'),
42 subtitle: Text('480.00'),
43 ),
44 Wrap(
45 alignment: WrapAlignment.spaceBetween,
46 direction: Axis.horizontal,
47 children: <Widget>[
48 Container(
49 margin: const EdgeInsets.all(8),
50 child: ElevatedButton(
51 onPressed: () => {},
52 child: Text(' ADD '),
53 style: ElevatedButton.styleFrom(
54 primary: Colors.purple,
55 padding: EdgeInsets.symmetric(
56 horizontal: 20, vertical: 20),
57 textStyle: TextStyle(
58 fontSize: 16, fontWeight: FontWeight.bold),
59 ),
60 ),
61 ),
62 Container(
63 margin: const EdgeInsets.all(8),
64 child: ElevatedButton(
65 onPressed: () => {},
66 child: Text(' UPDATE '),
67 style: ElevatedButton.styleFrom(
68 primary: Colors.purple,
69 padding: EdgeInsets.symmetric(
70 horizontal: 20, vertical: 20),
2. Essential Widgets, From Layout to Stateful and Stateless 55

71 textStyle: TextStyle(
72 fontSize: 16, fontWeight: FontWeight.bold),
73 ),
74 ),
75 ),
76 Container(
77 margin: const EdgeInsets.all(8),
78 child: ElevatedButton(
79 onPressed: () => {},
80 child: Text(' DELETE '),
81 style: ElevatedButton.styleFrom(
82 primary: Colors.purple,
83 padding: EdgeInsets.symmetric(
84 horizontal: 20, vertical: 20),
85 textStyle: TextStyle(
86 fontSize: 16, fontWeight: FontWeight.bold),
87 ),
88 ),
89 ),
90 Container(
91 margin: const EdgeInsets.all(8),
92 child: ElevatedButton(
93 onPressed: () => {},
94 child: Text(' NEXT PAGE '),
95 style: ElevatedButton.styleFrom(
96 primary: Colors.purple,
97 padding: EdgeInsets.symmetric(
98 horizontal: 20, vertical: 20),
99 textStyle: TextStyle(
100 fontSize: 16, fontWeight: FontWeight.bold),
101 ),
102 ),
103 ),
104 ],
105 ),
2. Essential Widgets, From Layout to Stateful and Stateless 56

106 ],
107 ),
108 ),
109 ],
110 ),
111 ),
112 ],
113 );
114 }
115 }

With reference to color change, we have used that static method.


And, take any container out of it, and that will tell the story.
If you take out the style property from the ElevatedButton, this
color will go back to default blue. Only through the static method,
we can change the color of ElevatedButton.

1 Container(
2 margin: const EdgeInsets.all(8),
3 child: ElevatedButton(
4 onPressed: () => {},
5 child: Text(' UPDATE '),
6 style: ElevatedButton.styleFrom(
7 primary: Colors.purple,
8 padding: EdgeInsets.symmetric(
9 horizontal: 20, vertical: 20),
10 textStyle: TextStyle(
11 fontSize: 16, fontWeight: FontWeight.bold),
12 ),
13 ),
14 ),
2. Essential Widgets, From Layout to Stateful and Stateless 57

What is the difference between


container and column, row in
flutter?
There are lot of differences between container and column, or row
widgets. However, when we build any flutter app, we need them
both.
Firstly, we need container widget. Secondly, we need column and
row, both. Moreover, we want them together.
What does that mean actually?
Their names reveal their exact identity. That is to say, the names
also tell us why we need them.

What is container widget in flutter?

Let us start with the container widget first, and check how we can
explore its characteristics.
Most importantly, a container widget must have a child widget. As
a result, a container widget in flutter can have a column as its child
widget.
On the other hand, it could have a row as its child widget. Conse-
quently, inside a container, a column can have several containers
as its children, or those containers can have a row as its child again.
As a result, app design might turn into a complex one.
With the help of different types of images, we will try to understand
how container widget works, and at the same time we’ll understand
the difference between container and column, row.
The main characteristic of container widget can control the custom
styling and alignment. For instance you can align it to left or right,
2. Essential Widgets, From Layout to Stateful and Stateless 58

adding some color and border the container with different color,
like that.
Most importantly, container’s rich alignment, styling options and
flexible width let us design our flutter app in a better way.

What is row and column in flutter?

On the other hand, row and column, both can take multiple, in fact,
unlimited child widgets. In conclusion, their children return a list
of widgets. As a result, we can map a list there.
However, there are some styling limitations that we must handle.
We can align a column, or row; but we cannot add some other
styling options that are available in a container widget.
Above all, we have to use Column and Row when we want to sit
widgets, either above, or next to each other. The column widget
takes the full available height. Meanwhile, a row widget takes the
full available width.

Can container have children flutter?

Since container must take exactly one child widget, it can also have
children widgets. For example, a container widget have a column
as its child widget, so that column can have unlimited widgets as
its children.
Moreover, they will act as the container’s descendants.
Consider the following code snippets.
2. Essential Widgets, From Layout to Stateful and Stateless 59

1 import 'package:flutter/material.dart';
2
3 class ContainerColumnRow extends StatelessWidget {
4 const ContainerColumnRow({Key, key}) : super(key: key);
5
6 @override
7 Widget build(BuildContext context) {
8 return MaterialApp(
9 title: 'Difference between container, column and row',
10 home: CCRFirstPage(),
11 );
12 }
13 }
14
15 class CCRFirstPage extends StatelessWidget {
16 const CCRFirstPage({Key, key}) : super(key: key);
17
18 @override
19 Widget build(BuildContext context) {
20 return Scaffold(
21 appBar: AppBar(
22 title: Text('Container, Column and Row first page'),
23 ),
24 body: CCRFirstPageBody(),
25 );
26 }
27 }
28
29 class CCRFirstPageBody extends StatelessWidget {
30 const CCRFirstPageBody({Key, key}) : super(key: key);
31
32 @override
33 Widget build(BuildContext context) {
34 return Container(
35 margin: EdgeInsets.all(8),
2. Essential Widgets, From Layout to Stateful and Stateless 60

36 padding: EdgeInsets.all(15),
37 decoration: BoxDecoration(
38 color: Colors.purple,
39 ),
40 child: Column(
41 mainAxisAlignment: MainAxisAlignment.start,
42 children: [
43 Card(
44 child: Column(
45 mainAxisSize: MainAxisSize.min,
46 children: <Widget>[
47 const ListTile(
48 leading: Icon(Icons.album),
49 title: Text('Books'),
50 subtitle: Text('25.00'),
51 ),
52 const ListTile(
53 leading: Icon(Icons.album),
54 title: Text('Groceries'),
55 subtitle: Text('250.00'),
56 ),
57 const ListTile(
58 leading: Icon(Icons.album),
59 title: Text('Fruits'),
60 subtitle: Text('480.00'),
61 ),
62 Row(
63 mainAxisAlignment: MainAxisAlignment.end,
64 children: <Widget>[
65 TextButton(
66 child: const Text('ADD TO LIST'),
67 onPressed: () {/* ... */},
68 ),
69 const SizedBox(width: 8),
70 TextButton(
2. Essential Widgets, From Layout to Stateful and Stateless 61

71 child: const Text('DELETE'),


72 onPressed: () {/* ... */},
73 ),
74 const SizedBox(width: 8),
75 ],
76 ),
77 ],
78 ),
79 ),
80 ],
81 ),
82 );
83 }
84 }

In the above example we see clearly, how a container can have


multiple child widgets as its descendants.
For instance, we can see the image of our app, now.
2. Essential Widgets, From Layout to Stateful and Stateless 62

Figure 2.8 – We can control alignment of a column

Likewise, we can align this column of items at the top or at the


center. The same thing happens in case of the Row widget.
Presently we have two buttons sitting next to each other. The above
image shows us that the alignment is at the right hand side.
2. Essential Widgets, From Layout to Stateful and Stateless 63

Now we can take it to the center.

Figure 2.9 – We can control alignment of a row

We can control only the alignment of column and row. However,


we can return a list of items through them, only we cannot add any
other styling options.
2. Essential Widgets, From Layout to Stateful and Stateless 64

How do you add multiple rows in flutter?

Adding multiple rows in Flutter is not easy. Rather, it’s tricky. We


need to be careful so that an ugly error doesn’t pop up its head.
For example, take a look at the following image.
2. Essential Widgets, From Layout to Stateful and Stateless 65

Figure 2.10 – Row overflow error in flutter

We cannot allow this to happen. Further, we should tweak our code


in a different way, so that we can adjust unlimited widgets to sit
nest to each other without giving any error.
2. Essential Widgets, From Layout to Stateful and Stateless 66

What is Wrap in Flutter?

Wrap widget in Flutter allows us to get the same effect that we


might expect from a Row widget.
Moreover we can control the space between the child widgets that
sit next to each other.
2. Essential Widgets, From Layout to Stateful and Stateless 67

Figure 2.11 – Wrap widget in Flutter

We have shown the image, therefore we can take a look at the code
first.
2. Essential Widgets, From Layout to Stateful and Stateless 68

1 import 'package:flutter/material.dart';
2
3 class ContainerRowColumnModified extends StatelessWidget {
4 const ContainerRowColumnModified({Key, key}) : super(key:\
5 key);
6
7 @override
8 Widget build(BuildContext context) {
9 return ListView(
10 children: [
11 Container(
12 margin: EdgeInsets.all(8),
13 padding: EdgeInsets.all(15),
14 alignment: Alignment.topCenter,
15 decoration: BoxDecoration(
16 color: Colors.purple,
17 border: Border.all(
18 width: 5,
19 color: Colors.grey,
20 ),
21 ),
22 child: Column(
23 mainAxisAlignment: MainAxisAlignment.center,
24 children: [
25 Card(
26 child: Column(
27 mainAxisSize: MainAxisSize.min,
28 children: <Widget>[
29 const ListTile(
30 leading: Icon(Icons.album),
31 title: Text('Books'),
32 subtitle: Text('25.00'),
33 ),
34 const ListTile(
35 leading: Icon(Icons.album),
2. Essential Widgets, From Layout to Stateful and Stateless 69

36 title: Text('Groceries'),
37 subtitle: Text('250.00'),
38 ),
39 const ListTile(
40 leading: Icon(Icons.album),
41 title: Text('Fruits'),
42 subtitle: Text('480.00'),
43 ),
44 Wrap(
45 alignment: WrapAlignment.spaceBetween,
46 direction: Axis.horizontal,
47 children: <Widget>[
48 Container(
49 margin: const EdgeInsets.all(8),
50 child: ElevatedButton(
51 onPressed: () => {},
52 child: Text(' ADD '),
53 style: ElevatedButton.styleFrom(
54 primary: Colors.purple,
55 padding: EdgeInsets.symmetric(
56 horizontal: 20, vertical: 20),
57 textStyle: TextStyle(
58 fontSize: 16, fontWeight: FontWeight.bold),
59 ),
60 ),
61 ),
62 Container(
63 margin: const EdgeInsets.all(8),
64 child: ElevatedButton(
65 onPressed: () => {},
66 child: Text(' UPDATE '),
67 style: ElevatedButton.styleFrom(
68 primary: Colors.purple,
69 padding: EdgeInsets.symmetric(
70 horizontal: 20, vertical: 20),
2. Essential Widgets, From Layout to Stateful and Stateless 70

71 textStyle: TextStyle(
72 fontSize: 16, fontWeight: FontWeight.bold),
73 ),
74 ),
75 ),
76 Container(
77 margin: const EdgeInsets.all(8),
78 child: ElevatedButton(
79 onPressed: () => {},
80 child: Text(' DELETE '),
81 style: ElevatedButton.styleFrom(
82 primary: Colors.purple,
83 padding: EdgeInsets.symmetric(
84 horizontal: 20, vertical: 20),
85 textStyle: TextStyle(
86 fontSize: 16, fontWeight: FontWeight.bold),
87 ),
88 ),
89 ),
90 Container(
91 margin: const EdgeInsets.all(8),
92 child: ElevatedButton(
93 onPressed: () => {},
94 child: Text(' NEXT PAGE '),
95 style: ElevatedButton.styleFrom(
96 primary: Colors.purple,
97 padding: EdgeInsets.symmetric(
98 horizontal: 20, vertical: 20),
99 textStyle: TextStyle(
100 fontSize: 16, fontWeight: FontWeight.bold),
101 ),
102 ),
103 ),
104 ],
105 ),
2. Essential Widgets, From Layout to Stateful and Stateless 71

106 ],
107 ),
108 ),
109 ],
110 ),
111 ),
112 ],
113 );
114 }
115 }

While we are using the Wrap widget to adjust the child widgets
sitting next to each other, we’ve done another important thing.
Further, we have changed the color of the ElevatedButton. By
default it takes the blue color. But, we can change the color to our
choice.
At the same time, we can adjust the position.
The code repository for this chapter is also available at⁸
For more Flutter and Dart related articles please visit⁹
Just click on [email protected] to send me an email.
⁸https://github.com/sanjibsinha/better_flutter/tree/main/lib/chapter_two
⁹https://sanjibsinha.com/
3. Inherited Widget,
Provider and State
Management in Flutter
Why state management in Flutter is one of the most important
topics? What is flutter state?
Well, state is the information that a user can read synchronously
with the change in widget. During the lifetime of the widget, it can
hold that data.
Even if you refresh or render the widget, the data stays.
Now, there are different types of approaches to manage state.
This is the most basic approach. Moreover, in this tutorial, we will
see how a widget can manage the state and renders the state to
itself. Why do we need state management?
To begin with, I must clarify one thing. State is an object. It is not
a widget. Although in Flutter, everything is widget.
Therefore, we can say that a StatefulWidget is actually immutable,
it is StatelessWidget. The State of the widget is managed by the
State object.
However, that is not the key point here.
We’ll see how a widget can manage its own state.
There are a few steps that we need to do before we proceed.
We’ll create a class OwnStateManagingWidget. And we’ll manages
state for OwnStateManagingWidget.
Next, we’ll define the _stateChanged boolean which determines the
box’s current color.
3. Inherited Widget, Provider and State Management in Flutter 73

Next, we’ll define the _changeState() function, which updates _stat-


eChanged when the box is tapped and calls the setState() function to
update the UI. At the end, we’ll implement all interactive behavior
for the widget.
For more Flutter related Articles and Resources¹⁰

How we change the widget state in


Flutter?
We’ve defined the _stateChanged boolean which determines the
box’s current color. And we’ve defined the _changeState() function,
too.
Let us see the code, so we can understand how it works.

1 import 'package:flutter/material.dart';
2
3 class OwnStateManagingWidget extends StatefulWidget {
4 @override
5 _OwnStateManagingWidgetState createState() => _OwnStateMa\
6 nagingWidgetState();
7 }
8
9 class _OwnStateManagingWidgetState extends State<OwnState\
10 ManagingWidget> {
11 /// let's define the boolean value first
12 ///
13
14 bool _stateChanged = false;
15
16 /// let's create a function that will define the setState\
17 () method
¹⁰https://sanjibsinha.com
3. Inherited Widget, Provider and State Management in Flutter 74

18 /// it'll change the state of this widget only


19 ///
20 void _changeState() {
21 setState(() {
22 _stateChanged = !_stateChanged;
23 });
24 }
25
26 @override
27 Widget build(BuildContext context) {
28 /// we'll use GestureDetector so that we can tap a box th\
29 at will turn green as
30 /// the state is changed
31 return GestureDetector(
32 /// when we tap this onTap fires
33 onTap: _changeState,
34 child: Container(
35 child: Center(
36 child: Text(
37 _stateChanged ? 'State Changed' : 'State Unchanged',
38 style: TextStyle(fontSize: 35.0),
39 ),
40 ),
41 width: 350.0,
42 height: 350.0,
43 decoration:
44 BoxDecoration(color: _stateChanged ? Colors.green : Color\
45 s.red),
46 ),
47 );
48 }
49 }

How does it work? The _changeState() function, updates _state-


Changed boolean value to true when the box is tapped.
3. Inherited Widget, Provider and State Management in Flutter 75

Once the user taps the red box, it calls the setState() function to
update the UI and make the box color to green.

How to manage state of child


through parent widget?
Any flutter widget can manage its own state. It uses stateful widget.
However,when it’s a stateless child widget, the parent manages its
state.
It’s an interesting feature of Flutter. Especially when we manage
state in a small application.
For a large scale application where we have to pass the state
object to many screens, or pages, this process is not good. In such
cases we’ll use Provider or Riverpod. Even we can use the BLOC
architecture.
However, Provider is our best option. And we must stick to one
good choice.
Still, to understand Flutter state management, you should know
how it works at the root level.

How do you use stateful widget Flutter?

A Flutter stateful widget is a dynamic widget. As the user taps a


box, or click a button, it changes its state. And this process updates
the whole widget.
A stateful widget depends either on user action, or on data change.
The State class, or object actually manages the internal state of the
stateful widget. In that sense, a stateful widget also depends on the
State class, which is not a widget.
3. Inherited Widget, Provider and State Management in Flutter 76

In this tutorial, we will see how a stateful parent widget manages


the state of a stateless child widget.
If you’ve not read the previous article on how a widget manages its
own state,please read it, before we start.

How a Child Widget exports its state to its


parent
How can a Child Widget export its state to its parent? Without a
callback, we cannot imagine it.
So, we need to think about the callback first. Since the parent is
importing the state of the child widget, we don’t have to make the
child widget stateful anymore. It can be stateless.
However, the parent widget should be stateful. Moreover, there
should be a consistent mechanism that will help the child widget
to export its state safely.
Let’s see the code of the Parent Widget class.

1 class ParentWidget extends StatefulWidget {


2 @override
3 _ParentWidgetState createState() => _ParentWidgetState();
4 }
5
6 class _ParentWidgetState extends State<ParentWidget> {
7 /// Manages the _inActive state for ChildWidget.
8 ///
9 bool _inActive = true;
10
11 /// Implements _manageStateForChildWidget(), the method c\
12 alled when the box is tapped.
13 ///
14 void _manageStateForChildWidget(bool newValue) {
15 setState(() {
3. Inherited Widget, Provider and State Management in Flutter 77

16 _inActive = newValue;
17 });
18 }
19
20 @override
21 Widget build(BuildContext context) {
22 return ChildWidget(
23 inActive: _inActive,
24 notifyParent: _manageStateForChildWidget,
25 );
26 }
27 }

The code is very straight forward. By default the state object should
be inactive. So we’ve made it true. It also manages the state of the
child widget.
As we tap a box, it will no longer remain inactive. It becomes false,
from true and makes the state active.
So we need the setState() method, that will implement a method,
which in turn,passes a boolean parameter whose value is false.
Watch this part of the above code:

1 /// Manages the _inActive state for ChildWidget.


2 ///
3 bool _inActive = true;
4
5 /// Implements _manageStateForChildWidget(), the method c\
6 alled when the box is tapped.
7 ///
8 void _manageStateForChildWidget(bool newValue) {
9 setState(() {
10 _inActive = newValue;
11 });
12 }
3. Inherited Widget, Provider and State Management in Flutter 78

Now, we need a callback.


For that we will use a special feature of Flutter:

1 typedef ValueChanged<T> = void Function(T value);

How Child Widget implements this


special property?
Let us see the code of Child Widget. That will explain the rest.

1 /// Extends StatelessWidget because all state is handled \


2 by its parent, ParentWidget
3 ///
4 class ChildWidget extends StatelessWidget {
5 ChildWidget({Key key, this.inActive = true, this.notifyPa\
6 rent})
7 : super(key: key);
8 final bool inActive;
9
10 /// When a tap is detected, it notifies the parent.
11 ///
12 final ValueChanged<bool> notifyParent;
13 void manageState() {
14 notifyParent(!inActive);
15 }
16
17 @override
18 Widget build(BuildContext context) {
19 return GestureDetector(
20 onTap: manageState,
21 child: Container(
22 child: Center(
3. Inherited Widget, Provider and State Management in Flutter 79

23 child: Text(
24 inActive ? 'Inactive' : 'Active',
25 style: TextStyle(
26 fontSize: 25.0,
27 color: Colors.white,
28 ),
29 ),
30 ),
31 width: 250.0,
32 height: 250.0,
33 decoration: BoxDecoration(color: inActive ? Colors.red : \
34 Colors.green),
35 ),
36 );
37 }
38 }

Watch this part of the above code:

1 /// When a tap is detected, it notifies the parent.


2 ///
3 final ValueChanged<bool> notifyParent;
4
5 void manageState() {
6 notifyParent(!inActive);
7 }

As we have said earlier, using this special feature of Flutter, we


have a method, which passes a boolean value that in turn exports
the state to the parent widget.
3. Inherited Widget, Provider and State Management in Flutter 80

How the export and import of state take


place
How does the child widget export state? And, at the same time, how
does the parent widget import the state?
The mystery reveals itself at the child widget constructor, where
two named parameters point to a piece of data and a method that
through its parameter change the state of that data.
At the parent widget:

1 return ChildWidget( inActive: _inActive, notifyParent: _m\


2 anageStateForChildWidget, );

And at the child widget, this line is important.

1 final ValueChanged<bool> notifyParent;

Here notifyParent is a method that passes a certain type of data. We


have indicated which type of data should be passed –

1 ValueChanged<bool>.

For more Flutter related Articles and Resources¹¹

What is inherited widget in Flutter,


how do you use state management?
The inherited widget is one of the low level process of state
management in Flutter. However, when it manages state, it also
propagates information down the tree.
¹¹https://sanjibsinha.com
3. Inherited Widget, Provider and State Management in Flutter 81

And to do that it acts as a base class. Although state management


is a complex topic, it can be managed quite comfortably through
Inherited Widgets.
In this Chapter, let us concentrate on InheritedWidget only, as this
is extremely important concept in Flutter. Especially if you want to
manage state efficiently.
However, before proceeding further, let’s check a diagram so it
will clearly depict us how state can be floated to the bottom-most
widget without affecting the parent widgets.
We should remember that Flutter is all about widgets.
Flutter builds tree and sub-tree of widgets to build an application.
Think about a family tree structure. Flutter builds its widgets in that
fashion.
For more Flutter related Articles and Resources¹²

What is InheritedWidget in Flutter?


We can answer the above question in one or two sentences. Inherit-
edWidget is a special type of widget that defines a context at the
root of the base tree. This context then travels down the sub-tree as
deep as possible.
However, the advantage is plenty. The lowest widget at the sub-
tree may directly access the state defined in the InheritedWidget
without affecting the other widgets residing above it.
By using the InheritedWidget when the lowest level widget, resid-
ing at the bottom of the widget tree, tries to change its state, that
process does not affect other widgets above.
It does not happen if we use widget specific another low-level
approach ‘setState’ that comes with Flutter naturally. Although it
¹²https://sanjibsinha.com
3. Inherited Widget, Provider and State Management in Flutter 82

adds interactivity to any Flutter app in a safe way, but while doing
so it redraws every widget above it in the widget tree.
We don’t prefer that approach. For a small application we can use
that approach though.

How do you manage state efficiently?

In a complex scenario, where we need to pass state using the widget


specific low-level approach ‘setState’ among hundreds of widgets,
it becomes cumbersome.
The InheritedWidget tries to solve that riddle in its own way,
although it has some disadvantages too. We will come to that point
in a minute.
Before that let us take a look at an image to get an idea of how
InheritedWidget works.

InheritedWidget manages state efficiently,


but is that all?

No. As I have said earlier, I want to emphasize on the word


“context”. In the above image you see that some widgets below the
sub-tree talks directly to the InheritedWidget residing at the root
of the widget tree.
The context flows down to the bottom-most-widget without affect-
ing other. When the InheritedWidget changes its state it passes the
new value to the consumer bottom-most-widget.
While doing so the InheritedWidget rebuilds itself and at the same
time it rebuilds its consumer.
In doing so Flutter does not rebuild the other top-widgets through
which the context flows down to the bottom.
3. Inherited Widget, Provider and State Management in Flutter 83

How do you use an inherited widget in


Flutter?

Let us come to the point. How can we use InheritedWidget in


Flutter?
We need write a widget tree first. The tree starts with two Inherit-
edWidget. One passes the color through its context. And the other
passes an integer value through its context.
Let us see the main method through which we run our Inherited-
Widget app. You get the full code snippets at my GitHub repo.

1 import 'package:flutter/material.dart';
2 import 'controller/inherited-widget/inherited_widget_on_t\
3 op.dart';
4
5 main() => runApp(OurApp());
6
7 class OurApp extends StatelessWidget {
8 @override
9 Widget build(BuildContext context) {
10 return MaterialApp(
11 title: 'Our App',
12 debugShowCheckedModeBanner: false,
13 home: Scaffold(
14 body: InheritedWidgetOnTop(),
15 ),
16 );
17 }
18 }

Flutter widget tree can be deep, very deep


Keeping the above philosophy in our mind, we need to at the base
of the tree keeps our two inherited widgets.
3. Inherited Widget, Provider and State Management in Flutter 84

1 import 'package:flutter/material.dart';
2 import 'package:our_app/controller/inherited-widget/widge\
3 ts-lists/widgets_lists.dart';
4
5 class InheritedWidgetOnTop extends StatefulWidget {
6 @override
7 _InheritedWidgetOnTopState createState() => _InheritedWid\
8 getOnTopState();
9 }
10
11 class _InheritedWidgetOnTopState extends State<InheritedW\
12 idgetOnTop> {
13 @override
14 Widget build(BuildContext context) {
15 return ListView(
16 padding: const EdgeInsets.all(30.0),
17 children: [
18 EyeColor(
19 color: Colors.deepOrange,
20 child: Builder(builder: (BuildContext innerContext) {
21 return GrandParent();
22 })),
23 SizedBox(
24 height: 10.0,
25 ),
26 ChangingAge(
27 age: new ChangeAge(age: 25),
28 child: Builder(builder: (BuildContext textContext) {
29 return UncleClasses();
30 })),
31 ],
32 );
33 }
34 }

Two inherited widgets are “EyeColor” and “ChangingAge”.


3. Inherited Widget, Provider and State Management in Flutter 85

Through the “EyeColor”we pass a context that carries the color


of eyes. The descendant widgets, hopefully they are humans, will
consume the context and get their eye colors accordingly.
However, any descendant may not wish to consume that context,
and choose its own eye color.
The above code clearly indicates that the consumer of the inherited
widget “EyeColor” is the Grandparent widget. The Grandparent
widget has FatherClass widget as sub-tree.
To make it simple, we have another inherited widget “Changin-
gAge” that has also a consumer widget “UncleClasses”. The “Uncle-
Classes” widget has a fairly deep sub-tree, in which we have two
uncles widgets of different age. One of the uncle has a child, so we
can view that child as the bottom-most-widget in the Uncles tree.
Moreover, we can change the age of each uncle. We can change the
age of the child too by pressing buttons.
Nevertheless age changes when we press the button, the context
maintains the state so efficiently that the other ages do not get
affected.

We keep our inherited widgets in a


separate folders

Let us break down our app structure a little bit.


Inside our “lib” folder, the folder structure looks like the following:

1 controller/inherited-widget/widgets-lists/widgets_lists.d\
2 art

Now the widget lists include these two inherited widgets.


3. Inherited Widget, Provider and State Management in Flutter 86

1 import 'package:flutter/material.dart';
2
3 class EyeColor extends InheritedWidget {
4 const EyeColor({
5 Key key,
6 @required this.color,
7 @required Widget child,
8 }) : assert(color != null),
9 assert(child != null),
10 super(key: key, child: child);
11
12 final Color color;
13
14 static EyeColor of(BuildContext context) {
15 return context.dependOnInheritedWidgetOfExactType<EyeColo\
16 r>();
17 }
18
19 @override
20 bool updateShouldNotify(EyeColor old) => color != old.col\
21 or;
22 }
23
24 class ChangingAge extends InheritedWidget {
25 const ChangingAge({
26 Key key,
27 @required this.age,
28 @required Widget child,
29 }) : assert(age != null),
30 super(key: key, child: child);
31
32 final ChangeAge age;
33
34 static ChangingAge of(BuildContext context) {
35 return context.dependOnInheritedWidgetOfExactType<Changin\
3. Inherited Widget, Provider and State Management in Flutter 87

36 gAge>();
37 }
38
39 @override
40 bool updateShouldNotify(ChangingAge old) => age != old.ag\
41 e;
42 }
43
44 class ChangeAge {
45 int age;
46 ChangeAge({this.age});
47 void changeAge() {
48 age += 5;
49 }
50 }

How does Scope matter in passing Context?


Yes, that’s a valid point. While building this simple app, we should
remember that in the widget tree there could be a lot of widgets
that do not consume the same context.
So there could be widgets that may not in the Scope. The value that
context carries on its shoulder will be null for those widgets that
are not in the Scope. Otherwise the context carries a default value.
It is as simple as that.
Taking a look at the above code will tell you that there is a static
“of” method that passes “context” as a parameter. And it returns the
following line of code:

1 return context.dependOnInheritedWidgetOfExactType<Changin\
2 gAge>();

It allows the class to create its own fallback logic. It is important for
one reason. There could be other widgets who are not consumers
and they are not in the scope.
3. Inherited Widget, Provider and State Management in Flutter 88

Now the “of” method may return any type of data. In our app,
the inherited widget “EyeColor” returns Flutter Color class. The
consumer widgets of inherited widget EyeColor
The Grandparent is the main consumer, and it has a descendant
FatherClass. Let us close watch them first.

1 class GrandParent extends StatelessWidget {


2 @override
3 Widget build(BuildContext context) {
4 final eyeColor = EyeColor.of(context).color;
5 return Column(
6 children: [
7 Text(
8 'I am the Grandparent, although I am a Ghost now! I had t\
9 wo sons.',
10 style: TextStyle(
11 fontWeight: FontWeight.bold,
12 fontSize: 25.0,
13 color: eyeColor,
14 ),
15 ),
16 SizedBox(
17 height: 10.0,
18 ),
19 FatherClass(),
20 ],
21 );
22 }
23 }
24
25 class FatherClass extends StatelessWidget {
26 @override
27 Widget build(BuildContext context) {
28 return Column(
29 children: [
3. Inherited Widget, Provider and State Management in Flutter 89

30 Text(
31 'I am the Father. I have two brothers.',
32 style: TextStyle(
33 color: EyeColor.of(context).color,
34 fontSize: 30.0,
35 fontWeight: FontWeight.bold),
36 ),
37 ],
38 );
39 }
40 }

As you see on the above code, for Grandparent we have consumed


the context this way:

1 final eyeColor = EyeColor.of(context).color;

And then we use that color indirectly.

1 color: eyeColor,

In the FatherClass widget we have consumed it directly. Since in


our inherited widget EyeColor we have defined the color as deep
orange. The text inside both consumer take the same color.
We have not finished our code snippets yet. We have another
inherited widget “ChangingAge”. Through the context an integer
data, which is age of the uncles and the child, flows down to the
bottom.

How do you manage state through


inherited widgets?

Well, the next code snippets will show you that.


3. Inherited Widget, Provider and State Management in Flutter 90

Managing state becomes fairly simple with the inherited widgets.


The main consumer of the inherited widget “ChangingAge” is
“UncleClasses”.
Let us take a look at the code first.

1 class UncleClasses extends StatelessWidget {


2 @override
3 Widget build(BuildContext context) {
4 return Column(
5 children: [
6 Text('This is a list of Uncles with different states.',
7 style: TextStyle(
8 color: Colors.black45,
9 fontSize: 25.0,
10 fontWeight: FontWeight.bold,
11 )),
12 SizedBox(
13 height: 5.0,
14 ),
15 FirstUncleClass(),
16 SizedBox(
17 height: 5.0,
18 ),
19 UncleClass(),
20 ],
21 );
22 }
23 }

The main consumer has two more consumer widgets inside it.
FirstUncleClass and UncleClass.
Let us take a look at them too.
3. Inherited Widget, Provider and State Management in Flutter 91

1 class FirstUncleClass extends StatefulWidget {


2 @override
3 _FirstUncleClassState createState() => _FirstUncleClassSt\
4 ate();
5 }
6
7 class _FirstUncleClassState extends State<FirstUncleClass\
8 > {
9 var firstUncleAge = new ChangeAge(age: 35);
10 @override
11 Widget build(BuildContext context) {
12 return Column(
13 children: [
14 Text(
15 'I am First Uncle, ${firstUncleAge.age} years old, change\
16 my age by add button below.',
17 style: TextStyle(
18 fontSize: 30.0,
19 color: Colors.lightGreenAccent,
20 backgroundColor: Colors.black),
21 ),
22 SizedBox(
23 height: 10.0,
24 ),
25 FloatingActionButton(
26 onPressed: () {
27 setState(() {
28 firstUncleAge.changeAge();
29 });
30 },
31 child: Icon(Icons.add),
32 backgroundColor: Colors.blue,
33 ),
34 ],
35 );
3. Inherited Widget, Provider and State Management in Flutter 92

36 }
37 }

If we press the FloatingActionButton below, the age of first uncle


increases by 5. Probably he is in hurry to grow older.

Extending state management to the


second uncle and his child
The code of second uncle class is similar to that of the first uncle.
However,the design slightly differs. Another important thing is this
widget has a child. So we have declared it inside too.
Just below the FloatingActionButton .

1 FloatingActionButton(
2 onPressed: () {
3 setState(() {
4 secondUncleAge.changeAge();
5 });
6 },
7 child: Icon(Icons.add),
8 backgroundColor: Colors.blue,
9 ),
10 UnclesChildClass(),

The widget UnclesChildClass has the similar code structure, so we


need not repeat it.
Press the FloatingActionButton, age of each consumer increases by
5 individually. As I have said earlier, the whole family is in hurry
to grow older quickly. What about you?
Actually the same context, here age, flows down the family tree.
3. Inherited Widget, Provider and State Management in Flutter 93

The context points to the exact position where any Widget stays
at the Widget tree. We’ll discuss this topic separatly in another
Chapter.

What is flutter provider? How does


provider flutter work?
What is Flutter provider? The answer will remain incomplete. Why?
Because you must also know how provider flutter works.
Provider is a Flutter package. It’s a wrapper around the Inherited-
Widget.
Before you proceed you must add the dependency on provider to
the ‘pubspec.yaml’ file.
It looks like this:

1 // pubspec.yaml
2
3 dependencies:
4 flutter:
5 sdk: flutter
6
7 provider:

Now you are set to use Provider to manage state in your app.
What is state? Well, state is something that exists on memory. When
your app is running, in most cases you will try to manage state.
Provider helps you to do that.
State is extremely important. Managing it efficiently is important
too.
3. Inherited Widget, Provider and State Management in Flutter 94

Because to build any type of complex app that handles multiple


screens, different variables, and user sessions, managing state is
crucial, you should plan it beforehand.
Why?
Flutter creates a very deep and nested widget tree. To manage state
at the lowest bottom, you cannot rebuild every top-widget. That is
wasteful memory consumption.
Provider in Flutter is the answer!
Enough talking. Let us try to do some code together. Our goal is to
understand the main concept of Flutter Provider.
Using ChangeNotifierProvider is one solution. It comes from the
Provider package and it provides an instance of a ChangeNotifier to
the widgets. Now Flutter has has in-built mechanisms for widgets
to provide data and services to their distant descendants.
Widget is very powerful. ChangeNotifierProvider uses that power
to notify the listeners.
How does it do that?
Making it possible needs to place it on top of our widget tree.

1 import 'package:basic_flutter_provider/models/counting_th\
2 e_number.dart';
3 import 'package:basic_flutter_provider/views/my_home_page\
4 .dart';
5 import 'package:flutter/material.dart';
6 import 'package:provider/provider.dart';
7
8 void main() {
9 runApp(
10 ChangeNotifierProvider(
11 create: (context) =>
12 CountingTheNumber(),
13 child: MyApp(),
3. Inherited Widget, Provider and State Management in Flutter 95

14 ),
15 );
16 }
17
18 class MyApp extends StatelessWidget {
19 // This widget is the root of your application.
20 @override
21 Widget build(BuildContext context) {
22 return MaterialApp(
23 title: 'Flutter Demo',
24 theme: ThemeData(
25 primarySwatch: Colors.blue,
26 ),
27 home: MyHomePage(),
28 );
29 }
30 }

It creates a context that returns the model from where the data
comes. And it also returns a child widget, which will consume the
data.
For more Flutter related Articles and Resources¹³

Why we need a Model?

For a simple reason. First of all it will store and give us changed data.
Although in this example the data is ephemeral, or short-lived, still
we need a small model class.

¹³https://sanjibsinha.com
3. Inherited Widget, Provider and State Management in Flutter 96

1 import 'package:flutter/widgets.dart';
2
3 class CountingTheNumber with ChangeNotifier {
4 int number = 0;
5 void increaseNumber() {
6 number++;
7 notifyListeners();
8 }
9 }

Now we can provide this designed model to the desired widget.


However our desired widget positions itself at the lowest-bottom.

1 import 'package:basic_flutter_provider/controllers/a_very\
2 _deep_widget_tree.dart';
3
4 import 'package:flutter/material.dart';
5
6 class MyHomePage extends StatelessWidget {
7 @override
8 Widget build(BuildContext context) {
9 return Scaffold(
10 appBar: AppBar(
11 title: Text('Basic Provider Explained to Beginners'),
12 ),
13 body: Center(
14 child: AVeryDeepWidgetTree(),
15 // This trailing comma makes auto-formatting nicer for bu\
16 ild methods.
17 ));
18 }
19 }

While sending our data directly to the lowest-bottom we shouldn’t


rebuild the top-widgets.
3. Inherited Widget, Provider and State Management in Flutter 97

AVeryDeepWidgetTree class is a long code snippet. However you


can study the code structure better in my GitHub repo.
Let us see how it works. I will explain what is happening exactly
inside. A Very Deep and Nested Widget Tree
As I have said earlier, Flutter allows nested widget tree. In fact, you
cannot imagine a complex app without that.
Let us see the code first. Then I’ll explain what is happening.

1 import 'package:basic_flutter_provider/models/countin\
2 g_the_number.dart';
3 import 'package:flutter/material.dart';
4 import 'package:provider/provider.dart';
5
6 class AVeryDeepWidgetTree extends StatelessWidget {
7 @override
8 Widget build(BuildContext context) {
9 // ‘Provider.of’, just like Consumer needs to know th\
10 e type of the model.
11 //We need to specify the model ‘CountingTheNumber’.
12 final counter = Provider.of<CountingTheNumber>(contex\
13 t);
14 return Container(
15 padding: const EdgeInsets.all(20.0),
16 child: Column(
17 mainAxisAlignment: MainAxisAlignment.center,
18 children: <Widget>[
19 Text(
20 'This is a simple Text widget',
21 style: TextStyle(
22 color: Colors.black,
23 fontSize: 45.0,
24 fontWeight: FontWeight.bold,
25 ),
26 ),
3. Inherited Widget, Provider and State Management in Flutter 98

27 //now we are going to build a very deep widget tree


28 Center(
29 child: Container(
30 child: Column(
31 mainAxisAlignment: MainAxisAlignment.center,
32 children: [
33 Text(
34 'This is another simple Text widget deep inside the t\
35 ree.',
36 style: TextStyle(
37 fontSize: 35.0,
38 fontWeight: FontWeight.bold,
39 ),
40 ),
41 SizedBox(
42 height: 5.0,
43 ),
44 Text(
45 'You have pushed the button this many times:',
46 style: TextStyle(fontSize: 35.0),
47 ),
48 SizedBox(
49 height: 5.0,
50 ),
51 Text(
52 '${counter.number}',
53 style: TextStyle(fontSize: 25.0),
54 ),
55 SizedBox(
56 height: 5.0,
57 ),
58 FloatingActionButton(
59 onPressed: () {
60 counter.increaseNumber();
61 },
3. Inherited Widget, Provider and State Management in Flutter 99

62 tooltip: 'Increment',
63 child: Icon(Icons.add),
64 ),
65 ],
66 ),
67 ),
68 ),
69 ],
70 ),
71 );
72 }
73 }

Remember our model class. We have only one variable that will
reflect the state change. So at the bottom-most widget we need to
access it.
To do that, we will use Provider.of() method. Where we should
mention the type of our model, and pass the context.

1 final counter = Provider.of<CountingTheNumber>(context);

The Provider helps us to access the model data and method any-
where inside that widget tree.

1 Text(
2 '${counter.number}',
3 style: TextStyle(fontSize: 25.0),
4 ),
5 SizedBox(
6 height: 5.0,
7 ),
8 FloatingActionButton(
9 onPressed: () {
10 counter.increaseNumber();
3. Inherited Widget, Provider and State Management in Flutter 100

11 },
12 tooltip: 'Increment',
13 child: Icon(Icons.add),
14 ),

Clicking the button will change the state of the app by increasing
the number.
The main drawback of the above code hides itself in this line of
Code:

1 final CountingTheNumber counter = Provider.of<CountingThe\


2 Number>(context);

We want to rebuild only the text part where the number will show
itself. And we want to rebuild the floating action button section.
Achieving that is easy. All we need to write a separate widget for
those parts. After that we will call it inside our view.

1 import 'package:flutter/material.dart';
2 import 'package:flutter_provider_explained_for_beginners/\
3 model/counting_the_number.dart';
4 import 'package:provider/provider.dart';
5
6 class ColumnClass extends StatelessWidget {
7 @override
8 Widget build(BuildContext context) {
9 // ‘Provider.of’, just like Consumer needs to know the ty\
10 pe of the model.
11 // We need to specify the model ‘CountingTheNumber’.
12 //this time only this widget will be rebuilt
13 final CountingTheNumber counter = Provider.of<CountingThe\
14 Number>(context);
15 return Column(
16 children: [
3. Inherited Widget, Provider and State Management in Flutter 101

17 Text(
18 '${counter.number}',
19 style: TextStyle(fontSize: 25.0),
20 ),
21 SizedBox(height: 10.0),
22 FloatingActionButton(
23 onPressed: () {
24 counter.increaseNumber();
25 },
26 tooltip: 'Increment',
27 child: Icon(Icons.add),
28 )
29 ],
30 );
31 }
32 }

Next, we will add this widget to the “AVeryDeepWidgetTree”:

1 // the whole top widgets will remain unaffected when stat\


2 e changes
3 ColumnClass(),

That’s all. Running the code will only change a very small segment
when we press the button and change the state.
We have successfully removed the pitfalls of rebuilding the whole
widget tree. What is flutter provider? How does provider flutter
work?
The change of state affects a small segment of widget.
3. Inherited Widget, Provider and State Management in Flutter 102

How do you use Provider Consumer


to manage State in Flutter?
How do you use provider Consumer widget to manage state? Is it
better than Provider.of()<T> to manage state?
Well, let us check that. Here is a simple guide.
We have seen what is Flutter state before. Although I’ve not written
on state management in flutter alone, but I have written on provider
before.In that article I’ve written on how to manage state in Flutter.
I’ve also written on the relationship between flutter InheritedWid-
get and State.
However, I’ve not written on how to use provider Consumer widget.
So in this article I’ll show it.
How do you use provider consumer to manage state? Very simple.
If you’ve already have read the previous article on Provider,of()
method, and understood the concept, you can use consumer quite
easily. What is better? Provider.of<T> or Consumer<T>?
The author of Provider package Rémi Rousselet in an answer
mentioned a few key points.
Consumer allows us to build more granular widgets. At the same
time, it also solves most BuildContext misuse.
In a minute we will learn how we can take those advantages.
Rémi Rousselet also said that the choice is yours. So you can use
any one of them. Although you can use any one of them, without
context your preference is meaningless nevertheless.
Keeping context in my mind, I’ll always prefer using Consumer<T>,
instead of Provider.of()<T>.
Why? I am going to show you in a minute. What is the best state
management architecture in Flutter?
3. Inherited Widget, Provider and State Management in Flutter 103

I opened the Flutter documentation page on 25th December, 2020


and found this line:
** If you are new to Flutter and you don’t have a strong reason to
choose another approach (Redux, Rx, hooks, etc.), this is probably
the approach you should start with. The provider package is easy
to understand and it doesn’t use much code. It also uses concepts
that are applicable in every other approach. **
As a big admirer of Provider package, I also recommend my readers
to use provider. From the very beginning. Provider manages state
in the simplest way.
Although, you can use a bit low-level InheritedWidget concept, still
that has some limitations. I mentioned that in my previous chapter.
You can pass data and services to the descendant widgets by using
InheritedWidget concept.
But Provider makes it more simple. In fact, provider actually works
with these low-level widgets.

How do you use Provider Consumer?

First add the provider package in your pubspecy.yaml file. Next,


you need to import that package.

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

We will import other packages if needed.

What is the Problem in managing state in


Flutter?

The main problem lies in widget rebuilding process.


3. Inherited Widget, Provider and State Management in Flutter 104

We don’t want that. Flutter’s default State object rebuilds the whole
widget tree. While managing state in the deepest widget, we cannot
allow this to happen.
Suppose at the bottom of widget tree, inside any Text widget we
want to reflect our state change.
To do that, we cannot allow the top widgets to rebuild themselves.
What is the solution?
The solution is Provider and Consumer. It starts with ChangeNoti-
fierProvider.
ChangeNotifierProvider is the widget that provides an instance of
a ChangeNotifier to its descendants. It comes from the provider
package.
We should keep it at the topmost place of our widget tree. Why
should we do this?
Let us try to understand the mechanism of state management in
Flutter, first. Flutter is a declarative framework. If we want to
change the state of UI, we should rebuild it.
Suppose we want to change the bottom-most widget. We cannot do
that imperatively from outside, by calling a method on it.
Is there any way so that we can let Flutter help us?
Yes, there is.
We don’t want to fight with it, or force it to adopt something that
goes against its nature. On the contrary, we will take help from
Flutter to do that heavy lifting.

How Flutter helps us to manage State?

ChangeNotifier class provides notification to its listeners. You will


find this class in Flutter Software Development Kit (SDK).
3. Inherited Widget, Provider and State Management in Flutter 105

In the Provider package, ChangeNotifier is one way to encapsulate


the state.
Have a look at our model class.

1 import 'package:flutter/widgets.dart';
2
3 class CountingTheNumber with ChangeNotifier {
4 int number = 0;
5 String message = 'Sanjib Sinha';
6
7 void increaseNumber(int number) {
8 number++;
9 notifyListeners();
10 }
11
12 void testMessage() {
13 message.startsWith('S')
14 ? message = 'Hi Sanjib'
15 : message = 'First letter is not S';
16 notifyListeners();
17 }
18 }

The only code that is specific to ChangeNotifier is the call to


notifyListeners(). We have called it twice in our methods. (See the
code above).

How could we notify Listeners?


We need ChangeNotifierProvider widget that provides an instance
of a ChangeNotifier to its descendants. It comes from the provider
package.
Now our task becomes much easier. Because we place ChangeNo-
tifierProvider at the top of the all widgets, any descendant widget
3. Inherited Widget, Provider and State Management in Flutter 106

can access state from it directly.

1 import 'package:flutter/material.dart';
2 import 'package:flutter_provider_explained_for_beginners/\
3 model/counting_the_number.dart';
4 import 'package:flutter_provider_explained_for_beginners/\
5 view/my_home_page.dart';
6 import 'package:provider/provider.dart';
7
8 void main() {
9 runApp(
10 // ChangeNotifierProvider, unlike ChangeNotifier, comes f\
11 rom the Provider package
12 // and it provides an instance of a ChangeNotifier to the\
13 widgets,
14 // which have already subscribed to it
15 // we should place the ChangeNotifierProvider Just above \
16 the widgets that need to access it.
17 // you will understand provider better if you already hav\
18 e understood how
19 // InheritedWidget works
20 ChangeNotifierProvider(
21 create: (context) =>
22 CountingTheNumber(), // designed Model is provided to the\
23 desired widgets
24 child: MyApp(),
25 ),
26 );
27 }
28
29 class MyApp extends StatelessWidget {
30 // This widget is the root of your application.
31 @override
32 Widget build(BuildContext context) {
33 return MaterialApp(
34 title: 'Flutter Demo',
3. Inherited Widget, Provider and State Management in Flutter 107

35 theme: ThemeData(
36 primarySwatch: Colors.blue,
37 ),
38 home: MyHomePage(),
39 );
40 }
41 }

Now we can provide CountingTheNumber model to any descen-


dant widgets in our app through the ChangeNotifierProvider. We
have declared it already at the top, we can start using it. (See the
bold sections at the above and below code).
How we can access changed data? How we can change state
through Consumer widget?
We’re closing towards the lesser known facts to the unknown world
of provider package. As we have just seen the ChangeNotifier-
Provider provides only one class here. The CountingTheNumber
model class, which we have placed in our models folder.
Next, we’ll try to solve the next part of the problem.
Where to place the Consumer<T>?
Once our ChangeNotifierProvider provides the CountingTheNum-
ber model class, we can start using it through the Consumer widget.
It is the best practice to put the Consumer widgets as deep as in the
tree as possible. That is what we have done.
In our controllers folder, we have created a ColumnClass custom
widget that returns a Column widget. In its children section we
have two Container widgets, which have the Consumer widgets.
The next code snippet gives you an idea how we use Consumer
widget to get the provided model class methods.
3. Inherited Widget, Provider and State Management in Flutter 108

1 import 'package:flutter/material.dart';
2 import 'package:flutter_provider_explained_for_beginners/\
3 model/counting_the_number.dart';
4 import 'package:provider/provider.dart';
5
6 class ColumnClass extends StatelessWidget {
7 @override
8 Widget build(BuildContext context) {
9
10 /// we're using Consumer widget instead of Provider.of().
11 /// we've put our Consumer widget as deep as possible in \
12 the tree
13 return Column(
14 children: [
15 Container(
16 margin: const EdgeInsets.all(
17 5.0,
18 ),
19 child: Consumer<CountingTheNumber>(
20 builder: (context, message, child) {
21 return Column(
22 children: [
23 child,
24 Text(
25 '${message.message}',
26 style: TextStyle(fontSize: 25.0),
27 ),
28 ],
29 );
30 },
31
32 /// building a humongous widget tree
33 child: Row(
34 mainAxisAlignment: MainAxisAlignment.center,
35 children: [
3. Inherited Widget, Provider and State Management in Flutter 109

36 Column(
37 children: [
38 Text(
39 'First Row',
40 style: TextStyle(
41 fontSize: 20.0,
42 color: Colors.blue,
43 ),
44 ),
45 SizedBox(
46 height: 10.0,
47 ),
48 Text(
49 'Second Row',
50 style: TextStyle(
51 fontSize: 20.0,
52 color: Colors.red,
53 ),
54 ),
55 ],
56 ),
57 const Divider(
58 color: Colors.black,
59 height: 20,
60 thickness: 5,
61 indent: 20,
62 endIndent: 0,
63 ),
64 Column(
65 children: [
66 Text(
67 'First Row',
68 style: TextStyle(
69 fontSize: 20.0,
70 color: Colors.red,
3. Inherited Widget, Provider and State Management in Flutter 110

71 ),
72 ),
73 SizedBox(
74 height: 10.0,
75 ),
76 Text(
77 'Second Row',
78 style: TextStyle(
79 fontSize: 20.0,
80 color: Colors.blue,
81 ),
82 ),
83 ],
84 ),
85 ],
86 ),
87 ),
88 ),
89 SizedBox(height: 10.0),
90 Container(
91 margin: const EdgeInsets.all(
92 5.0,
93 ),
94 child: Consumer<CountingTheNumber>(
95 builder: (context, message, child) {
96 return Column(
97 children: [
98 FloatingActionButton(
99 onPressed: () {
100 message.testMessage();
101 },
102 tooltip: 'Increment',
103 child: Icon(Icons.ac_unit_rounded),
104 ),
105 child,
3. Inherited Widget, Provider and State Management in Flutter 111

106 ],
107 );
108 },
109
110 /// building another humongous widget tree
111 child: Row(
112 mainAxisAlignment: MainAxisAlignment.center,
113 children: [
114 Column(
115 children: [
116 Text(
117 'First Row',
118 style: TextStyle(
119 fontSize: 20.0,
120 color: Colors.blue,
121 ),
122 ),
123 SizedBox(
124 height: 10.0,
125 ),
126 Text(
127 'Second Row',
128 style: TextStyle(
129 fontSize: 20.0,
130 color: Colors.red,
131 ),
132 ),
133 ],
134 ),
135 const Divider(
136 color: Colors.black,
137 height: 20,
138 thickness: 5,
139 indent: 20,
140 endIndent: 0,
3. Inherited Widget, Provider and State Management in Flutter 112

141 ),
142 Column(
143 children: [
144 Text(
145 'First Row',
146 style: TextStyle(
147 fontSize: 20.0,
148 color: Colors.red,
149 ),
150 ),
151 SizedBox(
152 height: 10.0,
153 ),
154 Text(
155 'Second Row',
156 style: TextStyle(
157 fontSize: 20.0,
158 color: Colors.blue,
159 ),
160 ),
161 ],
162 ),
163 ],
164 ),
165 ),
166 ),
167 ],
168 );
169 }
170 }

Watch the bold sections. Reading them will explain how Consumer
widgets work. How do the Consumer widgets work?
The bold sections in the above code tells us one thing. In a
Consumer widget we must specify the type of the model that we
3. Inherited Widget, Provider and State Management in Flutter 113

want to access.

1 child: Consumer<CountingTheNumber>()

Here the model class is CountingTheNumber() that has two meth-


ods. When we call one method that increases the number of the
counter variable.
We have seen its usage in our previous article, where we have used
Provide.of()<T>.
The another method of the model class helps us to search and
find one character. By pressing the button we check whether the
message starts with that letter or not.
Clicking the button changes the state of the associated widget and
gives us a message.
When we click the blue icon button, it calls the method on our
model class that checks whether the name has started with letter ‘s’
or not. If it starts with that letter it gives a welcome message.
Now everything happens without rebuilding the whole widget tree.
Take a good look at my GitHub repo : The Flutter Provider code
repository for this book¹⁴
You will find how we have built a humongous widget tree above
and below of our Consumer widgets.

The Specific Steps to use Consumer


widget
We should know each individual steps that we exercise to use
Consumer widgets.
The very first step involves specifying the type of the model class.
We have already seen that line in the above code.
¹⁴https://github.com/sanjibsinha/flutter_provider_explained_for_beginners
3. Inherited Widget, Provider and State Management in Flutter 114

1 Consumer<CountingTheNumber>

What happens if we don’t specify the generics? The provider


package cannot help us any more. The provider is based on types.
If you don’t mention the type, it doesn’t know what to provide.
The only required argument of Consumer widget is builder. Under-
standing how the builder function works, is very important.
Each time we try to change the state by pressing the button, the
ChangeNotifier changes. In fact, we call the notifyListeners() in our
model class methods, just like below:

1 void testMessage() {
2 message.startsWith('S')
3 ? message = 'Hi Sanjib'
4 : message = 'First letter is not S';
5 notifyListeners();
6 }

Consequently it calls all the builder methods of all the correspond-


ing Consumer widgets. Now the builder method is called with three
arguments.

1 builder: (context, message, child){}

The first argument in any builder function is context. We get


context in every build method. The role of context is extremely
important, not only in Flutter, but in any language too. A detailed
discussion on context is what I’m planning to write. You will get
that in Flutter section.
The second argument is the instance of the ChangeNotifier. Here it
is message. For this data alone we’re using the Consumer widget.
Isn’t it?
3. Inherited Widget, Provider and State Management in Flutter 115

The third argument is child. If you plan to add large sub-tree under
the control of your Consumer widget, then just go ahead. Use that
child to build more complex UI. However, when state changes, the
child does not get affected.
The large sub-tree you’ve just added under Consumer doesn’t
change when the model changes.
If you run the code, the first screen shows you a name: “Sanjib
Sinha” at the bottom most Widget.
Next, we’ve pressed the button and it changes the state of the
bottom-most Widget, and gives us a message, such as “Hi Sanjib”.
While changing the State of the bottom-most Widget, it does not
change the top and bottom widgets.
So, that’s it. Although I feel we should learn provider using more
complex examples.
We’ll learn Provider by building more complex UI in the coming
chapters.

Provider: A recommended approach


to manage State
When an app is running, sometimes we want something, such as
user’s log-in session or added item in the cart, to exist in memory.
If we want something to exist in memory, we can call it ‘state’.
In the previous chapters, we have already discussed ‘state’ in
various lavels.
We have also learned a few tricks to manage it.
However, that is an introduction. We need to understand the con-
cept of ‘state’, because it is extremely important to build any type
of complex app, that handles multiple screens, different variables,
user sessions, etc.
3. Inherited Widget, Provider and State Management in Flutter 116

State can include anything – the app’s assets, as we said, all


the variables that the Flutter framework keeps about the UI, user
sessions that can be shared in different parts of the app, etc.
Whenever we design an app, and start building it, we don’t have to
manage every state.
Flutter framework takes care of a large sections, like textures.
Despite that,we need some data to rebuild our UI at any moment
in time.
The simplest example is we press a button and the text changes on
the screen.
Again we press the restore button, and the text disappears. We need
to provide the business logic so that it happens.
Consider a complex example, where a user adds an item to cart and
that item remains at that cart as long as user is logged in.
Now, state is of two types – ephemeral and app state.
We know the meaning of the word ephemeral, it means short-lived.
Some kind of state is very short-lived.
We may contain it in a single widget. That is why it is also called
local state. Suppose we want to show the current progress of a
complex animation. Once it is done, the UI is rebuilt, and we don’t
want it anymore.
For that reason, we don’t have to need any specialized state
management techniques like ‘Provider’ for that.
Let us see an example of ephemeral state or short-lived state.
Suppose we have a list of questions and answers. User can press
the button and change to the next question.
The app is quite straight forward and simple. To start with we need
to organize our application in three directories - controller, model
and view.
The main.dart file and the main method calls the runApp() method,
in which we pass our app - QuizApp().
3. Inherited Widget, Provider and State Management in Flutter 117

1 import 'package:flutter/material.dart';
2 import 'package:quiz_app/view/quiz_app.dart';
3
4 void main() {
5 runApp(QuizApp());
6 }

Next we need quiz_app.dart file in view directory.

1 import 'package:flutter/material.dart';
2 import 'package:quiz_app/view/home_page.dart';
3
4 class QuizApp extends StatelessWidget {
5 // This widget is the root of your application.
6 @override
7 Widget build(BuildContext context) {
8 return MaterialApp(
9 title: 'Flutter Demo',
10 theme: ThemeData(
11 primarySwatch: Colors.blue,
12 ),
13 home: MyHomePage(title: 'Quiz App Home Page'),
14 );
15 }
16 }

The MaterialApp Widget again returns MyHomePage() and we will


get that in view directory.
By the way, it will be a stateful widget.
3. Inherited Widget, Provider and State Management in Flutter 118

1 import 'package:flutter/material.dart';
2 import 'package:quiz_app/controller/question_widget.dart';
3 import 'package:quiz_app/model/questions_list.dart';
4
5 class MyHomePage extends StatefulWidget {
6 MyHomePage({Key key, this.title}) : super(key: key);
7
8 final String title;
9
10 @override
11 _MyHomePageState createState() => _MyHomePageState();
12 }
13
14 class _MyHomePageState extends State<MyHomePage> {
15 int _counter = 0;
16
17 void _incrementCounter() {
18 setState(() {
19 _counter++;
20 });
21 if (_counter > 2) {
22 _counter = 0;
23 }
24 }
25
26 @override
27 Widget build(BuildContext context) {
28 var questions = questionList;
29 return Scaffold(
30 appBar: AppBar(
31 title: Text(widget.title),
32 ),
33 body: Center(
34 child: Column(
35 mainAxisAlignment: MainAxisAlignment.center,
3. Inherited Widget, Provider and State Management in Flutter 119

36 children: <Widget>[
37 QuestionWidget(questions: questions, counter:\
38 _counter),
39 ...(questions[_counter]['answers'] as List<St\
40 ring>)
41 .map(
42 (answer) => buildElevatedButton(answer),
43 )
44 .toList(),
45 ],
46 ),
47 ),
48 // This trailing comma makes auto-formatting nicer fo\
49 r build methods.
50 );
51 }
52
53 ElevatedButton buildElevatedButton(String answer) {
54 return ElevatedButton(
55 onPressed: _incrementCounter,
56 child: Text(
57 answer,
58 style: TextStyle(
59 fontSize: 30.0,
60 ),
61 ),
62 );
63 }
64 }

As you have noticed, we need two more files. One stays in model
directory. It consists of a List of questions and different answers.
Basically, it is a List of Map that consists of two data types, String
as key and Object or List as value.
3. Inherited Widget, Provider and State Management in Flutter 120

1 List<Map<String, Object>> questionList = [


2 {
3 'question': 'What is the synonym of Mendacity?',
4 'answers': ['truthfulness', 'daring', 'falsehood', 'e\
5 nemy'],
6 },
7 {
8 'question': 'What is the synonym of Culpable?',
9 'answers': ['gay', 'guilty', 'falsehood', 'enemy'],
10 },
11 {
12 'question': 'What is the synonym of Rapacious?',
13 'answers': ['guilty', 'daring', 'falsehood', 'greedy'\
14 ],
15 },
16 ];

Finally we go to the controller directory and fetch the Question


widget.

1 import 'package:flutter/material.dart';
2
3 class QuestionWidget extends StatelessWidget {
4 const QuestionWidget({
5 Key key,
6 @required this.questions,
7 @required int counter,
8 }) : _counter = counter,
9 super(key: key);
10
11 final List<Map<String, Object>> questions;
12 final int _counter;
13
14 @override
15 Widget build(BuildContext context) {
3. Inherited Widget, Provider and State Management in Flutter 121

16 return Text(
17 questions[_counter]['question'],
18 style: TextStyle(
19 fontSize: 25.0,
20 fontWeight: FontWeight.bold,
21 ),
22 );
23 }
24 }

Nothing fancy althogh, it gives us an idea of how we can organize


our small ephemeral state in a single widget.

Ephemeral State or Single Widget is not


enough

As our application grows and we need different screens or views,


many widgets, we the single widget approach to manage ephemeral
state is not enough.
There are many other techniques as well, but in this chapter we will
only learn Provider, because Google recommends it.
For ephemeral state management using setState() and a field inside
the StatefulWidget’s State class is enough, because, a single widget
needs it, no other part of the device can access its single private
variable.
An app state or application state is not like that. We want to share
the app state across many parts of our app, not only that, we may
want it to keep between user sessions. In like manner, we can call
it shared state.
To manage app state we can opt for several options. Nevertheless,
Google recommends Provider, we will have a brief look at other
options as well.
3. Inherited Widget, Provider and State Management in Flutter 122

Different approaches to state


management
As we have said before, Provider is the recommended approach.
Provider helps you to manage state efficiently, in a very simple and
it has great flexibility.
We will learn that techniques in a minute.
Before that, let us see other approaches to manage state. Using set-
State() and a field inside the StatefulWidget’s State class is another
approach; yet that is good and recommended for the ephemeral
state. This lower-level approach is made up when we create a new
Flutter application.
InheritedWidget & InheritedModel approach is another lower-level
approach that communicates between ancestors and children in
the widget tree. We have seen how we can manage state through
Inherited Widget in our previous chapter.
Redux is another approach that is familiar to the web developers.
It is a state container approach, which is also very popular among
Flutter developers.
BLoC is another stream and observable based patterns, in fact
before Provider has stepped in, BLoC was very popular. Still the
flutter community adores BLoC.
Otherwise we might use MobX or GetX approach; the first one is
a popular library based conceptualization on observables and reac-
tions, and the second one is a simplified reactive state management
solution.
There are plenty of open source resources available to learn any one
of them, thoroughly.
In this chapter, although, we will learn only Provider, the state man-
agement recommended by Google, creator of Dart programming
language and Flutter framework.
3. Inherited Widget, Provider and State Management in Flutter 123

A Step by Step guide to use Provider


First thing first, we have add the dependency on provider to our
‘pubspec.yaml’ file.

1 // pubspec.yaml
2 # ...
3
4 dependencies:
5 flutter:
6 sdk: flutter
7
8 provider: ^4.0.0

At the time of writing this book, provider package is 4 and above.


We will always check the latest version.
The app state is something that we need to modify from many
different places, and to do that we have to pass around a lot of
callbacks; for a complex widget tree, it will be suicidal to replace
several widgets again and again.
To understand this mechanism we need to find out a solution that
will not disturb the widget tree as a whole, yet the app state will
modify a few widgets deep down the tree. Suppose we need to
modify one widget that has hundred widgets on top of it.
Without disturbing top hundred widgets, we can successfully han-
dle the app state using Provider.
Flutter has in-built mechanisms for widgets to provide data and ser-
vices to their distant descendants, it means not just the immediate
children, but any widgets below them.
Provider makes it possible to forget the callbacks and InheritedWid-
gets. We need to understand three primary concepts:
3. Inherited Widget, Provider and State Management in Flutter 124

1 ChangeNotifier
2 ChangeNotifierProvider
3 Consumer

ChangeNotifier is an in-built class included in the Flutter SDK,


this class notifies the listeners when any change in the state of
the ChangeNotifier class takes place. Any widget having hundreds
widgets on the top, can subscribe to its changes.
ChangeNotifierProvider, unlike ChangeNotifier, comes from the
Provider package and it provides an instance of a ChangeNotifier
to the widgets, which have already subscribed to it.
Where we should place the ChangeNotifierProvider? Just above the
widgets that need to access it.

1 void main() {
2 runApp(
3 ChangeNotifierProvider(
4 create: (context) => AnyModel(),
5 child: HomeApp(),
6 ),
7 );
8 }

Or we can even use MultiProvider, if we want to use multiple


classes.
3. Inherited Widget, Provider and State Management in Flutter 125

1 void main() {
2 runApp(
3 MultiProvider(
4 providers: [
5 ChangeNotifierProvider(create: (context) => First\
6 Model()),
7 Provider(create: (context) => SecondClass()),
8 ],
9 child: HomeApp(),
10 ),
11 );
12 }

Once our designed Model is provided to the desired widgets in our


app through the ChangeNotifierProvider declaration at the top, the
Consumer widgets that have subscribed to the notifications can use
it.

1 return Consumer<FirstModel>(
2 builder: (context, value, child) {
3 return Text("The value : ${value.firstModelVariable}"\
4 );
5 },
6 );

The first rule of using Consumer widget is we need to be specific


about the type of the model that we want to access. Suppose, we
want ‘FirstModel’, so we write Consumer<FirstModel>.
If the generic type <FirstModel> is not specified, the Provider
package cannot help us.
** The Provider package is based on ‘type’. Therefore, we must
mention the type. **
The second most important rule is we must supply the ‘builder’
argument of the Consumer widget. This is the only required argu-
ment of the Consumer widget.
3. Inherited Widget, Provider and State Management in Flutter 126

Whenever in the model class ChangeNotifier changes, the builder


argument is called.
Let us try to understand what is happening. Whenever the
ChangeNotifier changes, the method notifyListeners() is called, and
at the same time, all the builder methods of all the corresponding
Consumer widgets are called.
The ‘builder’ is called with three arguments, the first one is quite
familiar,‘context’; we get it in every build method. The second
argument ‘value’ is the instance of the ChangeNotifier. Using that
instance we can define the app state, and along with it, we can also
use the data in the model according to our requirement.
The role of the third argument ‘child’ is quite interesting. Suppose
we have a large widget subtree under our Consumer that does not
change when our model changes.
We can also get it through the builder argument ‘child’.
We have done enough talking, tried to understand the interaction
between Provider, and Consumer. Nonetheless, we won’t under-
stand this concepts unless we try to implement them.
Let us start with a very simple counter model. Through Provider,
we will change the counter number. We have two buttons –
Increase and Decrease (Figure 8.1). Imagine a number line, using
these buttons, we can either move towards the right side (positive),
or towards the left side (negative).
But before that we need to see the code and try to understand how
we have used the Provider package.
3. Inherited Widget, Provider and State Management in Flutter 127

1 import 'package:flutter/widgets.dart';
2
3 /// using the mixin concept of dart that we have discussed
4 /// in our previous chapter
5 class CountingTheNumber with ChangeNotifier {
6 int value = 0;
7 void incrementTheValue() {
8 value++;
9 notifyListeners();
10 }
11
12 void decreaseValue() {
13 value--;
14 notifyListeners();
15 }
16 }

The above code snippets is quite simple. This is our model class
through which we want to manage the state of the counter in a
ChangeNotifier.
Next, we need to use the ChangeNotifierProvider in the right place.
Because we need to call two methods, using Consumer is wasteful.
We don’t want to change the whole UI with the help of our model
data.
That is why we will use another concept - ‘Provider.of’, instead of
using Consumer.
3. Inherited Widget, Provider and State Management in Flutter 128

1 import 'package:flutter/cupertino.dart';
2 import 'package:flutter/material.dart';
3 import 'package:provider/provider.dart';
4
5 import 'counter_class.dart';
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 visualDensity: VisualDensity.adaptivePlatformDens\
16 ity,
17 ),
18 home: ChangeNotifierProvider<CountingTheNumber>(
19 // it will not redraw the whole widget tree anymo\
20 re
21 create: (BuildContext context) => CountingTheNumb\
22 er(),
23 child: MyHomePage()),
24 );
25 }
26 }
27
28 class MyHomePage extends StatelessWidget {
29 /*
30 MyHomePage({Key key, this.title}) : super(key: key);
31
32 final String title;
33 */
34
35 @override
3. Inherited Widget, Provider and State Management in Flutter 129

36 Widget build(BuildContext context) {


37 final counter = Provider.of<CountingTheNumber>(contex\
38 t);
39 return Scaffold(
40 appBar: AppBar(
41 title: Text('Using Provider Example One'),
42 ),
43 body: Center(
44 child: Column(
45 mainAxisAlignment: MainAxisAlignment.center,
46 children: <Widget>[
47 Text(
48 'You have pushed the button this many times:',
49 ),
50 // only Text widget listens to the notificati\
51 on
52 Text(
53 '${counter.value}',
54 style: Theme.of(context).textTheme.headline4,
55 ),
56 SizedBox(
57 height: 10.0,
58 ),
59 RaisedButton(
60 onPressed: () => counter.incrementTheValue(),
61 child: Text(
62 'Increase',
63 style: TextStyle(
64 fontSize: 20.0,
65 ),
66 ),
67 ),
68 SizedBox(
69 height: 10.0,
70 ),
3. Inherited Widget, Provider and State Management in Flutter 130

71 RaisedButton(
72 onPressed: () => counter.decreaseValue(),
73 child: Text(
74 'Decrease',
75 style: TextStyle(
76 fontSize: 20.0,
77 ),
78 ),
79 ),
80 ],
81 ),
82 ),
83 // This trailing comma makes auto-formatting nicer fo\
84 r build methods.
85 );
86 }
87 }

Now, we can run the app and by tapping two buttons change the
value. Before that, let us have a close look at some parts of the above
code.

1 final counter = Provider.of<CountingTheNumber>(context);

‘Provider.of’, just like Consumer needs to know the type of the


model. We need to specify the model ‘CountingTheNumber’. Now
using the ‘counter’ we have accessed the model data.
3. Inherited Widget, Provider and State Management in Flutter 131

1 Text(
2 '${counter.value}',
3 style: Theme.of(context).textTheme.headline4,
4 ),
5 …
6 RaisedButton(
7 onPressed: () => counter.incrementTheValue(),
8 child: Text(
9 'Increase',
10 style: TextStyle(
11 fontSize: 20.0,
12 ),
13 ),
14 ),
15 …
16 RaisedButton(
17 onPressed: () => counter.decreaseValue(),
18 child: Text(
19 'Decrease',
20 style: TextStyle(
21 fontSize: 20.0,
22 ),
23 ),
24 ),

The next step is running the app.

1 import 'package:flutter/material.dart';
2 import 'utilities/first_provider_example.dart';
3
4 void main() {
5 runApp(MyApp());
6 }

After that, we can run the app once again, and it turns the counter
value to 0. Now, we can test the decrease button (Figure 8.3).
3. Inherited Widget, Provider and State Management in Flutter 132

The above code snippets give us an idea of how Provider package


works.

Multi Providers and Multi Models


Now we will take closer looks and use multi Providers and multi
models to understand this process. This time we will use Consumer.
Let us start with an image. We have extended our old code added
a few more generic models.
Now we can press the counter button, and besides, we will press a
button to change the text below. After that, we can also press the
restore button to clear that data and give an output of that.
Let us see the code:

1 //main.dart
2
3 import 'models/providers/first_model_provider.dart';
4
5 import 'models/providers/counter_model_provider.dart';
6 import 'package:flutter/material.dart';
7 import 'package:provider/provider.dart';
8 import 'models/providers/second_model_provider.dart';
9 import 'views/my_app.dart';
10
11 void main() {
12 runApp(MultiProvider(
13 providers: [
14 ChangeNotifierProvider(
15 create: (context) => CountingTheNumber(),
16 ),
17 ChangeNotifierProvider(
18 create: (context) => FirstModelProvider(),
19 ),
3. Inherited Widget, Provider and State Management in Flutter 133

20 ],
21 child: MyApp(),
22 ));
23 }
24
25
26 // first_model_provider.dart
27
28 import 'package:flutter/widgets.dart';
29
30 class FirstModelProvider with ChangeNotifier {
31 String someDate = 'Some Date';
32
33 void supplyFirstData() {
34 someDate = 'Data Changed!';
35 print(someDate);
36 notifyListeners();
37 }
38
39 void clearData() {
40 someDate = 'Data Cleared!';
41 print(someDate);
42 notifyListeners();
43 }
44 }
45
46
47 // my_home_page.dart
48
49 import 'package:all_about_flutter_provider/models/provide\
50 rs/first_model_provider.dart';
51 import 'package:all_about_flutter_provider/models/provide\
52 rs/second_model_provider.dart';
53 import 'package:flutter/cupertino.dart';
54 import 'package:flutter/material.dart';
3. Inherited Widget, Provider and State Management in Flutter 134

55 import 'package:provider/provider.dart';
56
57 import '../models/providers/counter_model_provider.dart';
58
59 class MyHomePage extends StatelessWidget {
60 /*
61 MyHomePage({Key key, this.title}) : super(key: key);
62
63 final String title;
64 */
65 final String title = 'Using Provider Examples';
66
67 @override
68 Widget build(BuildContext context) {
69 /// MyHomePage is rebuilt when counter changes
70 final counter = Provider.of<CountingTheNumber>(contex\
71 t);
72
73 return Scaffold(
74 appBar: AppBar(
75 title: Text(title),
76 ),
77 body: SafeArea(
78 child: ListView(
79 padding: const EdgeInsets.all(10.0),
80 children: <Widget>[
81 Text(
82 'You have pushed the button this many times:',
83 style: TextStyle(fontSize: 25.0),
84 textAlign: TextAlign.center,
85 ),
86
87 /// consumer or selector
88 Text(
89 '${counter.value}',
3. Inherited Widget, Provider and State Management in Flutter 135

90 style: Theme.of(context).textTheme.headline4,
91 textAlign: TextAlign.center,
92 ),
93 SizedBox(
94 height: 10.0,
95 ),
96 Row(
97 mainAxisAlignment: MainAxisAlignment.spaceEve\
98 nly,
99 children: <Widget>[
100 RaisedButton(
101 onPressed: () => counter.increaseValue(),
102 child: Text(
103 'Increase',
104 style: TextStyle(
105 fontSize: 20.0,
106 ),
107 ),
108 ),
109 SizedBox(
110 height: 10.0,
111 ),
112 RaisedButton(
113 onPressed: () => counter.decreaseValue(),
114 child: Text(
115 'Decrease',
116 style: TextStyle(
117 fontSize: 20.0,
118 ),
119 ),
120 ),
121 ],
122 ),
123 SizedBox(
124 height: 10.0,
3. Inherited Widget, Provider and State Management in Flutter 136

125 ),
126 Column(
127 mainAxisAlignment: MainAxisAlignment.spaceEve\
128 nly,
129 children: <Widget>[
130 Container(
131 padding: const EdgeInsets.all(10.0),
132 color: Colors.red,
133 child: Consumer<FirstModelProvider>(
134 builder: (context, firstModelProvider\
135 , child) =>
136 RaisedButton(
137 child: Text(
138 'Press me!',
139 style: TextStyle(fontSize: 20.0),
140 ),
141 onPressed: () {
142 firstModelProvider.supplyFirstDat\
143 a();
144 },
145 ),
146 ),
147 ),
148 Container(
149 padding: const EdgeInsets.all(10.0),
150 color: Colors.white30,
151 child: Consumer<FirstModelProvider>(
152 builder: (context, firstModelProvider\
153 , child) => Text(
154 firstModelProvider.someDate,
155 style: TextStyle(fontSize: 40.0),
156 ),
157 ),
158 ),
159 SizedBox(
3. Inherited Widget, Provider and State Management in Flutter 137

160 height: 10.0,


161 ),
162 Container(
163 padding: const EdgeInsets.all(10.0),
164 color: Colors.red[200],
165 child: Consumer<FirstModelProvider>(
166 builder: (context, firstModelProvider\
167 , child) =>
168 RaisedButton(
169 child: Text(
170 'Reset',
171 style: TextStyle(fontSize: 20.0),
172 ),
173 onPressed: () {
174 firstModelProvider.clearData();
175 },
176 ),
177 ),
178 ),
179 ],
180 ),
181 ],
182 ),
183 ),
184
185 /// This trailing comma makes auto-formatting nicer f\
186 or build methods.
187 );
188 }
189 }

In the above code, we have used two Providers, inside the main()
function.
3. Inherited Widget, Provider and State Management in Flutter 138

1 runApp(MultiProvider(
2 providers: [
3 ChangeNotifierProvider(
4 create: (context) => CountingTheNumber(),
5 ),
6 ChangeNotifierProvider(
7 create: (context) => FirstModelProvider(),
8 ),
9 ],
10 child: MyApp(),
11 ));

Along with the ‘CountingTheNumber’ model, we have used a new


‘ChangeNotifier’ model - ‘FirstModelProvider’ class. And finally,
inside the ‘MyHomePage’ widget, we have used the Consumer
concepts.

1 child: Consumer<FirstModelProvider>(
2 builder: (context, firstModelProvider\
3 , child) =>
4 RaisedButton(
5 child: Text(
6 'Press me!',
7 style: TextStyle(fontSize: 20.0),
8 ),
9 onPressed: () {
10 firstModelProvider.supplyFirstDat\
11 a();
12 },
13 ),
14 ),

Because this Consumer’s builder argument returns a RaisedBut-


ton() widget, we have used the onPressed() argument to call one
of model methods. It gives us the next figure (Figure 8.5).
3. Inherited Widget, Provider and State Management in Flutter 139

If we click the ‘Reset’ button, the data has been cleared.


Now we are going to add another model class in the next code
snippets. It will add another button that will display the first name.

1 // main.dart
2
3 import 'models/providers/first_model_provider.dart';
4
5 import 'models/providers/counter_model_provider.dart';
6 import 'package:flutter/material.dart';
7 import 'package:provider/provider.dart';
8 import 'models/providers/second_model_provider.dart';
9 import 'views/my_app.dart';
10
11 void main() {
12 runApp(MultiProvider(
13 providers: [
14 ChangeNotifierProvider(
15 create: (context) => CountingTheNumber(),
16 ),
17 ChangeNotifierProvider(
18 create: (context) => FirstModelProvider(),
19 ),
20 ChangeNotifierProvider(
21 create: (context) => SecondModelProvider(),
22 ),
23 ],
24 child: MyApp(),
25 ));
26 }
27
28
29 // second_model_provider.dart
30
31 import 'package:flutter/widgets.dart';
3. Inherited Widget, Provider and State Management in Flutter 140

32
33 class SecondModelProvider with ChangeNotifier {
34 String name = 'Some Name';
35 int age = 0;
36
37 void getFirstName() {
38 name = 'Json';
39 print(name);
40 notifyListeners();
41 }
42 }
43
44
45 // my_home_page.dart
46
47 import 'package:all_about_flutter_provider/models/provide\
48 rs/first_model_provider.dart';
49 import 'package:all_about_flutter_provider/models/provide\
50 rs/second_model_provider.dart';
51 import 'package:flutter/cupertino.dart';
52 import 'package:flutter/material.dart';
53 import 'package:provider/provider.dart';
54
55 import '../models/providers/counter_model_provider.dart';
56
57 class MyHomePage extends StatelessWidget {
58 /*
59 MyHomePage({Key key, this.title}) : super(key: key);
60
61 final String title;
62 */
63 final String title = 'Using Provider Examples';
64
65 @override
66 Widget build(BuildContext context) {
3. Inherited Widget, Provider and State Management in Flutter 141

67 /// MyHomePage is rebuilt when counter changes


68 final counter = Provider.of<CountingTheNumber>(contex\
69 t);
70
71 return Scaffold(
72 appBar: AppBar(
73 title: Text(title),
74 ),
75 body: SafeArea(
76 child: ListView(
77 padding: const EdgeInsets.all(10.0),
78 children: <Widget>[
79 Text(
80 'You have pushed the button this many times:',
81 style: TextStyle(fontSize: 25.0),
82 textAlign: TextAlign.center,
83 ),
84
85 /// consumer or selector
86 Text(
87 '${counter.value}',
88 style: Theme.of(context).textTheme.headline4,
89 textAlign: TextAlign.center,
90 ),
91 SizedBox(
92 height: 10.0,
93 ),
94 Row(
95 mainAxisAlignment: MainAxisAlignment.spaceEve\
96 nly,
97 children: <Widget>[
98 RaisedButton(
99 onPressed: () => counter.increaseValue(),
100 child: Text(
101 'Increase',
3. Inherited Widget, Provider and State Management in Flutter 142

102 style: TextStyle(


103 fontSize: 20.0,
104 ),
105 ),
106 ),
107 SizedBox(
108 height: 10.0,
109 ),
110 RaisedButton(
111 onPressed: () => counter.decreaseValue(),
112 child: Text(
113 'Decrease',
114 style: TextStyle(
115 fontSize: 20.0,
116 ),
117 ),
118 ),
119 ],
120 ),
121 SizedBox(
122 height: 10.0,
123 ),
124 Column(
125 mainAxisAlignment: MainAxisAlignment.spaceEve\
126 nly,
127 children: <Widget>[
128 Container(
129 padding: const EdgeInsets.all(10.0),
130 color: Colors.red,
131 child: Consumer<FirstModelProvider>(
132 builder: (context, firstModelProvider\
133 , child) =>
134 RaisedButton(
135 child: Text(
136 'Press me!',
3. Inherited Widget, Provider and State Management in Flutter 143

137 style: TextStyle(fontSize: 20.0),


138 ),
139 onPressed: () {
140 firstModelProvider.supplyFirstDat\
141 a();
142 },
143 ),
144 ),
145 ),
146 Container(
147 padding: const EdgeInsets.all(10.0),
148 color: Colors.white30,
149 child: Consumer<FirstModelProvider>(
150 builder: (context, firstModelProvider\
151 , child) => Text(
152 firstModelProvider.someDate,
153 style: TextStyle(fontSize: 40.0),
154 ),
155 ),
156 ),
157 SizedBox(
158 height: 10.0,
159 ),
160 Container(
161 padding: const EdgeInsets.all(10.0),
162 color: Colors.red[200],
163 child: Consumer<FirstModelProvider>(
164 builder: (context, firstModelProvider\
165 , child) =>
166 RaisedButton(
167 child: Text(
168 'Reset',
169 style: TextStyle(fontSize: 20.0),
170 ),
171 onPressed: () {
3. Inherited Widget, Provider and State Management in Flutter 144

172 firstModelProvider.clearData();
173 },
174 ),
175 ),
176 ),
177 SizedBox(
178 height: 10.0,
179 ),
180 Container(
181 padding: const EdgeInsets.all(10.0),
182 color: Colors.white30,
183 child: Consumer<SecondModelProvider>(
184 builder: (context, secondModel, child\
185 ) => Text(
186 secondModel.name,
187 style: TextStyle(fontSize: 40.0),
188 ),
189 ),
190 ),
191 SizedBox(
192 height: 10.0,
193 ),
194 Container(
195 padding: const EdgeInsets.all(10.0),
196 color: Colors.red[200],
197 child: Consumer<SecondModelProvider>(
198 builder: (context, secondModel, child\
199 ) => RaisedButton(
200 child: Text(
201 'Get First Name',
202 style: TextStyle(fontSize: 20.0),
203 ),
204 onPressed: () {
205 secondModel.getFirstName();
206 },
3. Inherited Widget, Provider and State Management in Flutter 145

207 ),
208 ),
209 ),
210 ],
211 ),
212 ],
213 ),
214 ),
215
216 /// This trailing comma makes auto-formatting nicer f\
217 or build methods.
218 );
219 }
220 }

This part of the code has handled the Consumer section. Therefore,
let us check that part first.

1 Container(
2 padding: const EdgeInsets.all(10.0),
3 color: Colors.white30,
4 child: Consumer<SecondModelProvider>(
5 builder: (context, secondModel, child\
6 ) => Text(
7 secondModel.name,
8 style: TextStyle(fontSize: 40.0),
9 ),
10 ),
11 ),
12 SizedBox(
13 height: 10.0,
14 ),
15 Container(
16 padding: const EdgeInsets.all(10.0),
17 color: Colors.red[200],
3. Inherited Widget, Provider and State Management in Flutter 146

18 child: Consumer<SecondModelProvider>(
19 builder: (context, secondModel, child\
20 ) => RaisedButton(
21 child: Text(
22 'Get First Name',
23 style: TextStyle(fontSize: 20.0),
24 ),
25 onPressed: () {
26 secondModel.getFirstName();
27 },
28 ),
29 ),
30 ),

We are able to add another feature of state management through


Provider. The second model Provider is a simple class.

1 // second_model_provider.dart
2
3 import 'package:flutter/widgets.dart';
4
5 class SecondModelProvider with ChangeNotifier {
6 String name = 'Some Name';
7 int age = 0;
8
9 void getFirstName() {
10 name = 'Json';
11 print(name);
12 notifyListeners();
13 }
14 }

Next, if you proceed, you will find how Provider and Consumer
work together. First, we have pressed the decrease button for 3
times. Next, we have pressed the ‘Press me’ button, and the ‘Data
3. Inherited Widget, Provider and State Management in Flutter 147

Changed’. After that, finally, we have pressed the ‘Get First Name’
button, and the name appears on the screen.
Each Consumer widget has persisted its state, one button-press does
not affect the other. The changed-data stays on the screen.
Before concluding this chapter, we will learn how we can separate
business logic, application logic and screen-view.
To do that, we will keep our models inside the ‘model’ folder and
keep our business logic there. We will keep our application logic
inside the ‘controller’ folder, and finally we get the screen-view
inside the ‘view’ folder.

ChangeNotifier and Provider


context read and watch, when to
use and how to use
If we want to avoid widget rebuilding in Flutter, the rule of thumb
is avoid stateful widget. And the golden solution is also available.
Use ChangeNotifier native class of Flutter along with Provider
package.
Although for a small app using stateful widget is okay. But for a full
blown, complicated app architecture always avoid stateful widget,
and always try to stick with the Provider package.
Why?
That will help you to avoid widget rebuilding. When you use
ChangeNotifier native Flutter class with Provider, value changes
but widget is not rebuilt.
Hence the loading time increases. In any app development, manag-
ing time complexity makes all the difference.
3. Inherited Widget, Provider and State Management in Flutter 148

In this chapter, we’re going to learn how we can avoid widget-


rebuilding in Flutter, reducing the loading time.
In the previous chapter we’ve seen how one can build a small quiz
app in Flutter. Since it was a small app, we had used stateful widget.
As a result, each time we click the button, the whole widget tree is
rebuilt. Remember, it’s a small app, so we don’t have an expensive
parent widget tree on the top of the widget where we return the
changed value.
In reality or to be precise in the professional app building world, it
never happens.
What I feel, using stateful widget is unnecessary. Moreover, for a
small app it’s okay. But what will happen when we will have an
expensive parent widget tree?
While rebuilding a single widget, it will actually rebuild the whole
parent widget tree.
We should avoid this imbroglio to enhance our fluttering perfor-
mance.

How can I improve my fluttering


performance?
Use Provider package. Provider is a wrapper class of Inherited
widget. However, with the help of Change notifier it helps us to
overcome the limitations of stateful widget.
We don’t have to rebuild the widget anymore.
Let us try to make the whole concept a little bit simpler than it
seems. Take a look at the image below.
We have rebuilt the previous quiz app using Provider package this
time.
3. Inherited Widget, Provider and State Management in Flutter 149

As you can see, there are several options from which you can choose
the correct synonym of the word Mendacity.
Clicking any button will take you to the next question and, besides
it will also display the correct answer.
Therefore we need to tackle three changes at a single time! Al-
though these changes take place in different widgets, but not a
single widget is rebuilt.
The next image will show you how our app is working fine.
It certainly improves our fluttering performance because we’ve
used ChangeNotifier with Provider.
In the above image, it’s clearly visible that the next screenshot
shows us the next question and at the same time,it also displays
the synonym of the previous word - Mendacity.

How to avoid the anti-pattern and


stick to the correct design pattern in
Flutter?
This time we’ve used provider package and follow the correct
design pattern principle. So with the click of the button we see three
values simultaneously change.
At the top appears the new question. In the Elevated button text
four new synonyms appear, and finally, the app displays the correct
answer of the previous question.
With reference to our previous query, whether we can avoid
the unnecessary widget rebuilding, the answer is, yes, we have
achieved that in this quiz app.
Before diving deep into the code let me tell you one thing. You’ll
get the whole code snippet in my GitHub repository.
3. Inherited Widget, Provider and State Management in Flutter 150

The first code repository with stateful widget¹⁵


The second code repository with statless widget and provider
package¹⁶

How can we get rid of widget rebuild


in Flutter?
The code snippet is fairly long. So let us break it down and try to
understand how things take place.
We keep the providers above the app. Although it has a different
reason involving the test purpose, but still it has another advantage.
As we have already said, Provider is the wrapper class of inherited
widget.
Here is the main method, the entry point of our app.

1 import 'package:flutter/material.dart';
2 import 'package:provider/provider.dart';
3 import './remodelled-quiz-app/model/question_and_answer_m\
4 odel.dart';
5 import './remodelled-quiz-app/view/new_quiz_app.dart';
6
7 void main() {
8 runApp(
9 /// Providers are above [NewQuizApp] instead of inside it
10 MultiProvider(
11 providers: [
12 // ChangeNotifierProvider(create: (_) => Counter()),
13 // ChangeNotifierProvider(create: (_) => MyCounter()),
14 ChangeNotifierProvider(create: (_) => QuestionAndAnswerMo\
¹⁵https://github.com/sanjibsinha/quiz_app
¹⁶https://github.com/sanjibsinha/how_flutter_uses_dart/tree/main/lib/remodelled-quiz-
app
3. Inherited Widget, Provider and State Management in Flutter 151

15 del()),
16 ],
17 child: NewQuizApp(),
18 ),
19 );
20 }

As we have already created three directories – controller, model


and view, we keep our NewQuizApp widget in the view directory.

1 import 'package:flutter/material.dart';
2 import './new_quiz_app_home.dart';
3
4 class NewQuizApp extends StatelessWidget {
5 const NewQuizApp({Key key}) : super(key: key);
6
7 @override
8 Widget build(BuildContext context) {
9 return MaterialApp(
10 home: NewQuizAppHome(),
11 );
12 }
13 }

The NewQuizAppHome widget is the main user interface that


we’ve already seen in the above images.

How do you change the state in


Flutter?
Whenever we want to change the state in Flutter we follow some
rules.
What are the rules regarding state change?
3. Inherited Widget, Provider and State Management in Flutter 152

Usually we notify the framework that the internal state of an object


has changed. To do that we need to change the internal state of
a State object, making the change in a function that we pass to
setState(){}.
Here we will take a different route.
In our model directory we have a class QuestionAndAnswerModel,
which extends ChangeNotifier class.

What is ChangeNotifier?
According to the Flutter documentation:

1 ChangeNotifier is a simple class included in the Flutter \


2 SDK which provides change notification to its listeners. \
3 In other words, if something is a ChangeNotifier, you can\
4 subscribe to its changes.

One thing is clear. Our quiz app values, that means questions,
answers, etc can subscribe its changes.

1 import 'package:flutter/widgets.dart';
2
3 class QuestionAndAnswerModel extends ChangeNotifier {
4 List<Map<String, Object>> questions = [
5 {
6 'question': 'What is the synonym of Mendacity?',
7 'answers': ['truthfulness', 'daring', 'falsehood', 'enemy\
8 '],
9 },
10 {
11 'question': 'What is the synonym of Culpable?',
12 'answers': ['gay', 'guilty', 'falsehood', 'enemy'],
13 },
3. Inherited Widget, Provider and State Management in Flutter 153

14 {
15 'question': 'What is the synonym of Rapacious?',
16 'answers': ['guilty', 'daring', 'falsehood', 'greedy'],
17 },
18 ];
19 int counter = 0;
20
21 String answerChecking = 'Click to check correct answer!';
22
23 void incrementCounter() {
24 counter++;
25 notifyListeners();
26
27 if (counter > 2) {
28 counter = 0;
29 }
30 checkAnswer();
31 }
32
33 void checkAnswer() {
34 if (counter == 0) {
35 answerChecking = 'Synonym of Rapacious was Greedy.';
36 } else if (counter == 1) {
37 answerChecking = 'Synonym of Mendacity was Falsehood.';
38 } else if (counter == 2) {
39 answerChecking = 'Synonym of Culpable was Guilty.';
40 } else {
41 answerChecking = 'Click to check correct answer!';
42 }
43 }
44 }

Now we’re going to use the Provider package.


3. Inherited Widget, Provider and State Management in Flutter 154

What is the difference between


stateful and stateless widget in
Flutter?
If you have already read the previous article where we have used
the stateful widget to accomplish the same task, you will distinguish
between these two widgets.
Now we’re going to have the main interface NewQuizAppHome
where we use Provider read and watch method for the first time.

1 import 'package:flutter/material.dart';
2 import 'package:provider/provider.dart';
3 import '../controller/question_widget.dart';
4 import '../controller/check_answer_widget.dart';
5 import '../controller/elevated_button_widget.dart';
6 import '../model/question_and_answer_model.dart';
7
8 class NewQuizAppHome extends StatelessWidget {
9 const NewQuizAppHome({Key key}) : super(key: key);
10
11 @override
12 Widget build(BuildContext context) {
13 return Container(
14 child: Scaffold(
15 appBar: AppBar(
16 title: Text('New Quiz App'),
17 ),
18 body: Center(
19 child: Padding(
20 padding: const EdgeInsets.all(8.0),
21 child: Column(
22 children: [
23 Text(
3. Inherited Widget, Provider and State Management in Flutter 155

24 'Start the Quiz, test your English vocabulary:'


25 ' Find the right Synonym!'
26 ' Click any button and check your answer!',
27 style: TextStyle(
28 fontSize: 20.0,
29 fontWeight: FontWeight.bold,
30 ),
31 ),
32 SizedBox(height: 20.0),
33 QuestionWidget(
34 /// static method Provider.of<T>(context), which will beh\
35 ave similarly to watch
36 ///
37 questions:
38 Provider.of<QuestionAndAnswerModel>(context).questions,
39 // questions: context.watch<QuestionAndAnswerModel>().que\
40 stions,
41 counter: context.watch<QuestionAndAnswerModel>().counter,
42 ),
43 ...(context.watch<QuestionAndAnswerModel>().questions[con\
44 text
45 .watch<QuestionAndAnswerModel>()
46 .counter]['answers'] as List<String>)
47 .map(
48 (answer) => ElevatedButtonWidget(
49 answer: answer,
50 ),
51 )
52 .toList(),
53 CheckAnswerWidget(),
54 ],
55 ),
56 ),
57 ),
58 ),
3. Inherited Widget, Provider and State Management in Flutter 156

59 );
60 }
61 }

According to the Provider package documentation:

1 It’s worth noting that context.read<T>() won’t make widge\


2 t rebuild when the value changes and cannot be called ins\
3 ide StatelessWidget.build/State.build. On the other hand,\
4 it can be freely called outside of these methods.

You can use both:

1 Or to use the static method Provider.of<T>(context), whic\


2 h will behave similarly to watch and when you pass false \
3 to the listen parameter like Provider.of<T>(context,liste\
4 n: false) it will behave similar to read.

As Flutter encourages to break the app design into smaller segments,


we keep three controllers in separate directory.

What does provider do in Flutter?


I hope you have already got the answer. Yet it is always better we
show the code.
First the QuestionWidget, where we’re going to have the list of
questions and the index through which we can change the question
by the press of button.
3. Inherited Widget, Provider and State Management in Flutter 157

1 import 'package:flutter/material.dart';
2 import 'package:provider/provider.dart';
3 import '../model/question_and_answer_model.dart';
4
5 class QuestionWidget extends StatelessWidget {
6 const QuestionWidget({
7 Key key,
8 @required this.questions,
9 @required this.counter,
10 }) : super(key: key);
11
12 final List<Map<String, Object>> questions;
13 final int counter;
14
15 @override
16 Widget build(BuildContext context) {
17 return Text(
18 context
19 .watch<QuestionAndAnswerModel>()
20 .questions[context.watch<QuestionAndAnswerModel>().counte\
21 r]
22 ['question'],
23 style: TextStyle(
24 fontSize: 25.0,
25 fontWeight: FontWeight.bold,
26 ),
27 );
28 }
29 }

The following line is important.

1 context.watch<QuestionAndAnswerModel>().questions[context\
2 .watch<QuestionAndAnswerModel>().counter]['question'],

Here the type is QuestionAndAnswerModel.


3. Inherited Widget, Provider and State Management in Flutter 158

The same way, we can watch and check the correct answer.

1 import 'package:flutter/material.dart';
2 import 'package:provider/provider.dart';
3 import '../model/question_and_answer_model.dart';
4
5 class CheckAnswerWidget extends StatelessWidget {
6 const CheckAnswerWidget({
7 Key key,
8 }) : super(key: key);
9
10 @override
11 Widget build(BuildContext context) {
12 return Text(
13 context.watch<QuestionAndAnswerModel>().answerChecking,
14 style: TextStyle(
15 fontSize: 20.0,
16 ),
17 );
18 }
19 }

For the Elevated button we’re going to use context.read<T>.

Provider helps you to avoid


rebuilding widgets
Whenever we press the Elevated button it calls the increment-
Counter() method, which in turn calls another method that we have
seen in our model class.
However, this doesn’t involve any kind of widget rebuilding. That
is the advantage of Provider package.
3. Inherited Widget, Provider and State Management in Flutter 159

1 import 'package:flutter/material.dart';
2 import 'package:provider/provider.dart';
3 import '../model/question_and_answer_model.dart';
4
5 class ElevatedButtonWidget extends StatelessWidget {
6 ElevatedButtonWidget({Key key, this.answer}) : super(key:\
7 key);
8 final String answer;
9
10 @override
11 Widget build(BuildContext context) {
12 return ElevatedButton(
13 /// when you pass false to the listen parameter
14 /// like Provider.of<T>(context,listen: false) it will be\
15 have similar to read
16 ///
17 onPressed: () =>
18 // context.read<QuestionAndAnswerModel>().incrementCounte\
19 r(),
20 Provider.of<QuestionAndAnswerModel>(context, listen: fals\
21 e)
22 .incrementCounter(),
23 child: Text(
24 answer,
25 style: TextStyle(
26 fontSize: 30.0,
27 ),
28 ),
29 );
30 }
31 }

For more Flutter related Articles and Resources¹⁷


¹⁷https://sanjibsinha.com
3. Inherited Widget, Provider and State Management in Flutter 160

14. Provider best practices: How to


reduce widget rebuilds
How do we use a provider in Flutter? Well, we have seen many
examples already. In fact, there is nothing new in it.
However, the question is what is the most efficient way to use
Provider package?
Another question is how we can use Flutter ChangeNotifier class
along with Provider, so that it gives us the best result.
Will that really reduce widget re-building and enhance the effi-
ciency of our Flutter App?
The answer is – yes! It does.
In this chapter we will look into that matter. Not only that, we will
also show proof of it. To prove that Provider is more efficient than
stateful widget, we have used Android Studio Flutter Inspector and
Flutter Performance,which will track the widget rebuilding.
These tools will show you how many widgets are rebuilt when
user presses a button. With reference to the provider’s best usage
patterns we will also learn how to organize our code.

How do you use a provider in


Flutter?
In a large application, there could be many types of provider. The
generics, the value inside <> brackets point to a particular provider.
In fact, according to the generics, Flutter knows what “type” of
provider it is looking for.
Once it knows, Flutter goes up the widget tree until it finds the
provided value.
3. Inherited Widget, Provider and State Management in Flutter 161

Once Flutter gets the provider, either it uses watch method to reflect
the changed property or read method to change the event or, in
other words, call the method on it.
Therefore how to use flutter provider doesn’t concern us. We want
the proof that provider works better and faster than stateful widget.
So let’s start with a simple stateful widget example where user
presses a button that increments the value. In this example we will
see how a stateful widget takes a toll on the whole widget tree.

When do I use stateful widget? Or, should I?

In my opinion, stateful widget doesn’t make any sense. In fact,


when a stateless widget does the same job, why you should use
that?
Why not, I am going to show you in a minute.
If you search Internet, it says many things about stateful widget.
People say stateful widget is useful when the part of the user
interface you are describing can change dynamically.
We can do the same with the help of provider package, CgangeNo-
tifier class and a stateless widget.
Just like too much sunlight takes a heavy toll on your skin, a stateful
widget has a serious effect on the whole widget tree. Moreover, that
bad effect starts rebuilding widgets from the very top. That is from
your home page, then it rebuilds the scaffold widget, and all the
child widgets under it.
Now under Scaffold widget, whatever widgets you have, all are
rebuilt for a press of button!
Does it sound good?
I don’t think so.
But give us a proof!
3. Inherited Widget, Provider and State Management in Flutter 162

How does stateful widget rebuilds


the whole tree?
Let us first consider a simple stateful widget example.

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, this.title}) : super(key: key);
23
24 final String title;
25
26 @override
27 _MyHomePageState createState() => _MyHomePageState();
28 }
29
3. Inherited Widget, Provider and State Management in Flutter 163

30 class _MyHomePageState extends State<MyHomePage> {


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

65 onPressed: _incrementCounter,
66 tooltip: 'Increment',
67 child: Icon(Icons.add),
68 ), // This trailing comma makes auto-formatting nicer for\
69 build methods.
70 );
71 }
72 }

Now we have pressed the counter button six times. This press of
button will rebuild the Floating action button widget, and even the
Icon widget. We cannot even avoid if we use provider package.
But why it should start rebuilding from the top widget?
We must demonstrate to establish the truth of our conjecture.
On the right side of Android Studio, we have opened the Flutter Per-
formance window and ticked the “Track Widget rebuilds”, which
shows us the proof.
the topmost MyHOmePage widget has been also hot reloaded or
restarted 6 times resulting in heavy memory consumption.
From MyHomePage to Scaffold to AppBar, everything has been
rebuilt six times. Can we have a close look at how widget rebuilds?
It clearly shows that MyHomePage has been rebuilt six time for a
singular button pressing. And that happens to Scaffold widget also.
However, when we will use provider package, this will never
happen. We’ll see the proof in a minute.
We can have more proof that will show you that in the similar way
Scaffold has been rebuilt six times too!
But we have enough proof. Now let’s concentrate on provider
package. How do we use provider efficiently so that we can avoid
the whole widget tree rebuilding?
Now, we come to the main point.
3. Inherited Widget, Provider and State Management in Flutter 165

First of all we will demonstrate how we can stop this bad effect on
our flutter app. If we use a stateful widget, a single button-press
rebuilds the whole widget tree and consumes a hell lot of memory.
We’ve just seen that effect.
Moreover, for a complex app structure using a stateful widget
seriously affects the speed.
Therefore, we must find the best solution. Our first target is simple.
We cannot stop the widget rebuilds totally. But we can try to make
it sure that less widgets are rebuilt in the process.
Well, what does that mean actually?
It means we can place one type of provider inside a one type of
widget and another type of widget inside another type of widget.
The golden rule is break your app in many small chunks. One model
class should have one task.
That will enhance the efficiency of our flutter app.

Model, View, Controller and Provider


Inside our model folder we have three separate class that extends
change notifier. Each method will notify the listeners.
Now, the bottom-most widget could be the subscriber of that
notification. At that widget, one press of button changes one value.
Moreover, while doing so it doesn’t rebuild the whole widget tree
structure.
This time, our main app page is not rebuilt. Remember the top-most
MyHomePage widget in our previous stateful widget example.
We have a solid proof indeed. However, before demonstrate the
proof we should look at the code organization first.
3. Inherited Widget, Provider and State Management in Flutter 166

Model folder has three classes

Firstly, we have a NumberModel class. It has one task to do. If you


press the button, the counter value increases by 2.

1 import 'package:flutter/material.dart';
2
3 class NumberModel extends ChangeNotifier {
4 int _counter = 0;
5 int get counter => _counter;
6 void incrementNumberByTwo() {
7 _counter = _counter + 2;
8 notifyListeners();
9 }
10 }

Next, we have NameChangeModel that changes a name from


‘Sanjib’ to ‘John’.

1 import 'package:flutter/material.dart';
2
3 class NameChangeModel extends ChangeNotifier {
4 String _name = 'Sanjib';
5 String get name => _name;
6 void changeName() {
7 _name = 'John';
8 notifyListeners();
9 }
10 }

Finally, we have NameClearModel class that helps us to get the


previous name back to the screen.
3. Inherited Widget, Provider and State Management in Flutter 167

1 import 'package:flutter/material.dart';
2
3 class NameClearModel extends ChangeNotifier {
4 String _name = ' ';
5 String get name => _name;
6 void clearName() {
7 _name = 'Sanjib';
8 notifyListeners();
9 }
10 }

Who will subscribe to these notifications? The First row widget will
listen to Number Model
According to our design patterns, the controller folder has three
related Widgets that will subscribe to these notifications.
Let us see one by one.
The FirstRowWidget has code like the following:

1 import 'package:flutter/material.dart';
2 import 'package:provider/provider.dart';
3 import '../model/number_model.dart';
4
5
6 class FirstRowWidget extends StatelessWidget {
7 const FirstRowWidget({
8 Key key,
9 }) : super(key: key);
10
11 @override
12 Widget build(BuildContext context) {
13 return Row(
14 mainAxisSize: MainAxisSize.min,
15 children: [
16 Expanded(
3. Inherited Widget, Provider and State Management in Flutter 168

17 child: Padding(
18 padding: EdgeInsets.all(20.0),
19 child: Column(
20 children: [
21 Text('You have pushed this button this time!'),
22
23 /// this is our one [NumberModel] listener
24 /// watch() will reflect the change in number
25 /// as one presses the button
26 ///
27 Text('${context.watch<NumberModel>().counter}'),
28 /**
29 * ElevatedButton(
30 /// this is our another [NumberModel] listener
31 /// read() will fire the event the changes the number
32 /// by adding 2
33 ///
34 onPressed: () =>
35 context.read<NumberModel>().incrementNumberByTwo(),
36 child: Text('Increment'),
37 ),
38 */
39 FloatingActionButton(
40 onPressed: () => context.read<NumberModel>().incrementNum\
41 berByTwo(),
42 tooltip: 'Increment',
43 child: Icon(Icons.add),
44 ), // Th
45 ],
46 ),
47 ),
48 ),
49 ],
50 );
51 }
3. Inherited Widget, Provider and State Management in Flutter 169

52 }

Now we get the changed look of flutter app with the help of
provider package.
On the left side we can see that we have pressed the button 4 times,
so we’ve got 8. And on the right hand side of the screen, we can see
how many widgets get rebuilt.

The Second Row Widget will listen to


the Name Change Model
Just like before, this time we will press the second button to change
the name ‘Sanjib’ to ‘John’.
To make it happen, the second row widget will listen to the name
change model.

1 import 'package:flutter/material.dart';
2 import 'package:provider/provider.dart';
3 import '../model/name_change_model.dart';
4
5
6 class SecondRowWidget extends StatelessWidget {
7 const SecondRowWidget({
8 Key key,
9 }) : super(key: key);
10
11 @override
12 Widget build(BuildContext context) {
13 return Row(
14 mainAxisSize: MainAxisSize.min,
15 children: [
16 Expanded(
3. Inherited Widget, Provider and State Management in Flutter 170

17 child: Padding(
18 padding: EdgeInsets.all(20.0),
19 child: Column(
20 children: [
21 Text('${context.watch<NameChangeModel>().name}'),
22 /**
23 * ElevatedButton(
24 /// this is our another [NumberModel] listener
25 /// read() will fire the event the changes the number
26 /// by adding 2
27 ///
28 onPressed: () =>
29 context.read<NumberModel>().incrementNumberByTwo(),
30 child: Text('Increment'),
31 ),
32 */
33 FloatingActionButton(
34 onPressed: () => context.read<NameChangeModel>().changeNa\
35 me(),
36 tooltip: 'Increment',
37 child: Icon(Icons.add),
38 ), // Th
39 ],
40 ),
41 ),
42 ),
43 ],
44 );
45 }
46 }

This time we have reduced the number of widget rebuilds and


it’s clearly visible. Not only that, if you compare the red bar of
Frame rendering times, with the stateful widget, you will see the
difference.
3. Inherited Widget, Provider and State Management in Flutter 171

This time, when we click the button to increment number, only First
row widget gets rebuilt. It is clearly visible in the image.
Next, we will see the code of third row widget.

Third Row Widget listens to Name


Clear Model
Now, things are getting interesting. We have pressed the button to
change the name, and the second row widget is the subscriber.
The third row widget subscribes to the name clear model event.
Here is the code:

1 import 'package:flutter/material.dart';
2 import 'package:provider/provider.dart';
3 import '../model/name_clear_model.dart';
4
5
6 class ThirdRowWidget extends StatelessWidget {
7 const ThirdRowWidget({Key key}) : super(key: key);
8
9 @override
10 Widget build(BuildContext context) {
11 return Row(
12 mainAxisSize: MainAxisSize.min,
13 children: [
14 Expanded(
15 child: Padding(
16 padding: EdgeInsets.all(20.0),
17 child: Column(
18 children: [
19 Text('${context.watch<NameClearModel>().name}'),
20 /**
3. Inherited Widget, Provider and State Management in Flutter 172

21 * ElevatedButton(
22 /// this is our another [NumberModel] listener
23 /// read() will fire the event the changes the number
24 /// by adding 2
25 ///
26 onPressed: () =>
27 context.read<NumberModel>().incrementNumberByTwo(),
28 child: Text('Increment'),
29 ),
30 */
31 FloatingActionButton(
32 onPressed: () => context.read<NameClearModel>().clearName\
33 (),
34 tooltip: 'Increment',
35 child: Icon(Icons.add),
36 ), // Th
37 ],
38 ),
39 ),
40 ),
41 ],
42 );
43 }
44 }

If you press the third button, the third row widget will listen to the
event declared in clear name model.
Let’s see the last image where we will see that every button-press
restricts the unnecessary widget rebuilds. Scaffold, AppBar widgets
have never been rebuilt, although it happened in the case of stateful
widget.
On the right hand side, we can clearly see that Scaffold and AppBar
widgets have never been rebuilt whenever we’ve pressed the button.
How many times we have pressed, that really doesn’t matter. The
widget rebuilds are restricted in great amount.
3. Inherited Widget, Provider and State Management in Flutter 173

Finally the view folder and main


method
We’ve split the stateless widgets into more reusable widgets. As
a result, the the view folder has three widgets that chain to one
another.
We’ll see them to understand the code organization.
Let us start with the main method.

1 import 'package:flutter/material.dart';
2 import 'package:provider/provider.dart';
3 import 'model/number_model.dart';
4 import 'model/name_change_model.dart';
5 import 'model/name_clear_model.dart';
6 import 'view/french_test.dart';
7
8 void main() {
9 runApp(
10 /// Providers are above [FrenchTestApp] instead of inside\
11 it
12 MultiProvider(
13 providers: [
14 ChangeNotifierProvider(create: (_) => NumberModel()),
15 ChangeNotifierProvider(create: (_) => NameChangeModel()),
16 ChangeNotifierProvider(create: (_) => NameClearModel()),
17 ],
18 child: FrenchTestApp(),
19 ),
20 );
21 }

We’ve kept our three providers above our flutter app.


Next, we’ll see the code of FrenchTestApp in view folder.
3. Inherited Widget, Provider and State Management in Flutter 174

1 import 'package:flutter/material.dart';
2 import 'french_test_home.dart';
3
4 class FrenchTestApp extends StatelessWidget {
5 // This widget is the root of your application.
6 @override
7 Widget build(BuildContext context) {
8 return MaterialApp(
9 title: 'Flutter Demo',
10 theme: ThemeData(
11 // This is the theme of your application.
12 primarySwatch: Colors.blue,
13 ),
14 home: FrenchTestHome(),
15 );
16 }
17 }

Smaller reusable widgets help us to follow what is happening


exactly.
That is also the advantage of stateless widgets. Unlike stateful wid-
get, stateless widgets don’t come with a huge boilerplate. Splitting
code into smaller reusable code also makes it readable, which we
cannot do with stateful widget.

Here comes our Home Page

In the view folder we’ve kept two more stateless widgets which will
actually render the three controller widgets.
3. Inherited Widget, Provider and State Management in Flutter 175

1 import 'package:flutter/material.dart';
2
3 import 'french_test_first_view.dart';
4
5 class FrenchTestHome extends StatelessWidget {
6 const FrenchTestHome({Key key}) : super(key: key);
7
8 @override
9 Widget build(BuildContext context) {
10 return Scaffold(
11 appBar: AppBar(
12 title: Text('French Test'),
13 ),
14 body: FrenchTestFirstView(),
15 );
16 }
17 }

Since we have one page or screen, we’ll identify the widget by name
FrenchTestFirstView. And the code is like the following.

1 import 'package:flutter/material.dart';
2 import '../controller/first_row_widget.dart';
3 import '../controller/second_row_widget.dart';
4 import '../controller/third_row_widget.dart';
5
6 class FrenchTestFirstView extends StatelessWidget {
7 const FrenchTestFirstView({Key key}) : super(key: key);
8
9 @override
10 Widget build(BuildContext context) {
11 return Center(
12 child: Column(
13 children: [
14 FirstRowWidget(),
3. Inherited Widget, Provider and State Management in Flutter 176

15 SecondRowWidget(),
16 ThirdRowWidget(),
17 ],
18 ),
19 );
20 }
21 }

We’ve successfully split the long widget tree into smaller reusable
widgets.
And our code is functioning perfectly reducing the widget rebuilds
process.
For more Flutter related Articles and Resources¹⁸

Riverpod, a better Provider for state


management
Riverpod is a provider, but different. If this statement doesn’t make
any sense, then please read on.
Riverpod is another state-management package library. Just like
Provider.
We’ve already seen how to use the most popular flutter package,
Provider, for state management. Now we’re going to learn Riverpod
to manage Flutter state in a better way.
Why do we need Riverpod? Provider is not bad either!
Yes, that’s true. We have shown earlier how Provider state manage-
ment package reduces widget-rebuilds.
However, in Flutter community, developers started complaining
that provider package had some limitations. One of them, provider
is not compile safe.
¹⁸https://sanjibsinha.com
3. Inherited Widget, Provider and State Management in Flutter 177

And another insufficiency was dependency injection. And all of


these limitations came for one reason. The provider package de-
pends on Flutter framework. It is a wrapper class of inherited
widget.
So, the answer is Riverpod - the response to all the limitations of
state management packages for Dart and Flutter apps.
Moreover, the new state management package, Riverpod, created
by the same person Remi Rousselet, is also easy to maintain, test,
and much less error-prone.

Riverpod has many options


Unlike provider, Riverpod state management package has too many
choices. When you have too many good food on your table, it
becomes difficult to choose the best one!
You need to choose from any of them.

1 Provider
2
3 StateProvider
4
5 StateNotifierProvider
6
7 ChangeNotifierProvider
8
9 StreamProvider
10
11 FutureProvider
12
13 ScopedProvider

From this list of options, we will always choose one according to


3. Inherited Widget, Provider and State Management in Flutter 178

our need. That means, one option is always the best for one specific
problem.
We’ll come to that point later. In this chapter. Because it needs a
very detailed introspection.
As an example, when we want to watch or fetch a data from
our model class we can use the simplest Provider from Riverpod
package.
Now, we are going to learn how to use the simplest Provider from
the Riverpod package.
Just to make this chapter more interesting we will use our old
friend, the Provider state management package, also. In fact, you
can always use two packages side by side.

The greatest advantage of Riverpod


As I have just said, there are too many options that come with
Riverpod.
However, one of the greatest advantages is Riverpod helps us find
the programming errors at compile time rather than at runtime.
What we have seen before?
An ugly Provider Not Found pops up soemtimes. This error is no
more there. It’s a great freedom indeed.
To start with we should add these dependencies in our
pubspec.yaml file.
3. Inherited Widget, Provider and State Management in Flutter 179

1 dependencies:
2 flutter:
3 sdk: flutter
4 flutter_riverpod:
5 provider:

We’ve not mentioned the version so that the packages can depend
on the latest one available.
Just like our previous Provider chapter, we have used a model class
for this Riverpod starter.

The Data Model on which we’ll work


in Riverpod
As you see in the above image, there is one class member variable,
a String type. Below that we have a method through which we will
pass any string value and we can watch it on the screen.
The floating action button below will change the string data below.
For the floating action event part we’ve used our old Provider
package, not Riverpod. Because with the help of Riverpod Provider
we will only watch the value.
Here is the model class:
3. Inherited Widget, Provider and State Management in Flutter 180

1 import 'package:flutter/widgets.dart';
2 import 'package:flutter_riverpod/flutter_riverpod.dart';
3
4 class ProviderModel extends ChangeNotifier {
5 String _littleMonk = 'I am any String data!';
6 String get littleMonk => _littleMonk;
7
8 String fetchName(String str) {
9 _littleMonk = str;
10 return _littleMonk;
11 }
12
13 void changeName() {
14 _littleMonk = 'Now I am Little Monk';
15 notifyListeners();
16 }
17 }
18
19 final classTypeProviderModel = Provider<ProviderModel>((r\
20 ef) {
21 return ProviderModel();
22 });

Riverpod makes it really simple, because we can return the whole


model class like this:

1 final classTypeProviderModel = Provider<ProviderModel>((r\


2 ef) {
3 return ProviderModel();
4 });

Now, we can use “classTypeProviderModel” anywhere in our app,


however large it is. As we have earlier said, Riverpod is Flutter
independent.
3. Inherited Widget, Provider and State Management in Flutter 181

In the above code, the “ref” parameter is of type ProviderReference.


We’ll see how we can use it to resolve dependencies between
providers.
We need to understand that Provider object is now globally accessi-
ble. Here the “provided” object is ProviderModel class that has one
state and some methods to change that state.
However, it does not mean that the provided object is now globally
accessible.
Of course, like any other global function we can call it from
anywhere. But we can also restrict the return value by scoping it
locally.

Understanding Riverpod Scope


Now we will use this value provided by the Provider object inside
different widget tree. Although Riverpod is Flutter independent, but
in Flutter everything is widget!
Let us see the entry point of our app first.

1 import 'package:flutter/material.dart';
2 import 'package:flutter_riverpod/flutter_riverpod.dart';
3 import 'provider/controller/provider_example_widget.dart';
4
5 void main() {
6 runApp(ProviderScope(child: App()));
7 }
8
9 class App extends StatelessWidget {
10 // This widget is the root of your application.
11 @override
12 Widget build(BuildContext context) {
13 return MaterialApp(
3. Inherited Widget, Provider and State Management in Flutter 182

14 title: 'Flutter Demo',


15 theme: ThemeData(
16 primarySwatch: Colors.blue,
17 ),
18 home: Home(),
19 );
20 }
21 }
22
23 class Home extends StatelessWidget {
24 const Home({Key key}) : super(key: key);
25
26 @override
27 Widget build(BuildContext context) {
28 return Container(
29 child: Scaffold(
30 appBar: AppBar(
31 title: Text('Riverpod Examples'),
32 ),
33 body: ProviderExampleWidget(),
34 ),
35 );
36 }
37 }

Please watch this part first:

1 void main() {
2 runApp(ProviderScope(child: App()));
3 }

It means the Riverpod package uses just one, yes, a single Inherit-
edWidget. And we should place it above the whole widget tree.
I’m not going to detail how it can store state of all Provider objects.
But we can use them anywhere.
3. Inherited Widget, Provider and State Management in Flutter 183

How to watch a Provider object in


Riverpod?
We can watch it by using Consumer builder like the following code
snippet:

1 import 'package:flutter/material.dart';
2 import 'package:flutter_riverpod/flutter_riverpod.dart';
3 //import 'package:provider/provider.dart';
4 import '../model/any_type_provider_model.dart';
5
6 class ProviderExampleWidget extends StatelessWidget {
7 const ProviderExampleWidget({Key key}) : super(key: key);
8
9 @override
10 Widget build(BuildContext context) {
11 return Column(
12 children: [
13 SizedBox(
14 height: 10.0,
15 ),
16 Padding(
17 padding: const EdgeInsets.all(18.0),
18 child: Text(
19 'Riverpod Provider example where we watch a String member\
20 variable'
21 ' that we have passed through a class method.',
22 style: TextStyle(
23 fontSize: 20.0,
24 fontWeight: FontWeight.bold,
25 ),
26 ),
27 ),
28 Padding(
3. Inherited Widget, Provider and State Management in Flutter 184

29 padding: const EdgeInsets.all(18.0),


30 child: Center(
31 child: Consumer(
32 builder: (context, watch, child) {
33 final x = watch(classTypeProviderModel);
34 return Text(
35 x.fetchName('We can now pass any string data...'),
36 style: TextStyle(
37 fontSize: 50.0,
38 ),
39 );
40 },
41 ),
42 ),
43 ),
44 SizedBox(
45 height: 10.0,
46 ),
47 ],
48 );
49 }
50 }

In the above code this part holds the trick:

1 Consumer(
2 builder: (context, watch, child) {
3 final x = watch(classTypeProviderModel);
4 return Text(
5 x.fetchName('We can now pass any string data...'),
6 style: TextStyle(
7 fontSize: 50.0,
8 ),
9 );
10 },
11 ),
3. Inherited Widget, Provider and State Management in Flutter 185

We’ll show you a simpler method in a minute. That will drastically


reduce the boilerplate code.
However, the above method reduces the widget rebuilds more than
any other method. Here the Text widget is only rebuilt.
In the next code snippet we will see how we can pass “ScopedReader
watch” through build() method and drastically reduce the boiler-
plate code.

How we can mix Riverpod Provider


with old Provider package?
We’re doing this just for fun! After all, coding is fun as long as you
enjoy doing something new from nowhere!
In this app structure we have a controller folder as usual. As I
always maintain the model-view-controller pattern.
You’ll get the full code in this GitHub repository: The Riverpod all
code repository for this book¹⁹
In the controller folder, we have two widgets.
The first one will use “ScopedReader watch” as I’ve just mentioned.

1 import 'package:flutter/material.dart';
2 import 'package:flutter_riverpod/flutter_riverpod.dart';
3 import 'elevated_button_widget.dart';
4 import '../model/any_type_provider_model.dart';
5
6 class ProviderExampleWidget extends ConsumerWidget {
7 const ProviderExampleWidget({Key key}) : super(key: key);
8
9 @override
¹⁹https://github.com/sanjibsinha/riverpod_examples
3. Inherited Widget, Provider and State Management in Flutter 186

10 Widget build(BuildContext context, ScopedReader watch) {


11 final littleMonk = watch(classTypeProviderModel);
12 return Column(
13 children: [
14 SizedBox(
15 height: 10.0,
16 ),
17 Padding(
18 padding: const EdgeInsets.all(8.0),
19 child: Text(
20 'Riverpod Provider example where we watch a String member\
21 variable'
22 ' that we have passed through a class method.',
23 style: TextStyle(
24 fontSize: 20.0,
25 fontWeight: FontWeight.bold,
26 ),
27 ),
28 ),
29 Center(
30 child: Padding(
31 padding: const EdgeInsets.all(8.0),
32 child: Text(
33 littleMonk.littleMonk,
34 style: TextStyle(fontSize: 30.0),
35 ),
36 ),
37 ),
38 SizedBox(
39 height: 10.0,
40 ),
41 Center(
42 child: Padding(
43 padding: const EdgeInsets.all(8.0),
44 child: Text(
3. Inherited Widget, Provider and State Management in Flutter 187

45 littleMonk
46 .fetchName('Now we can pass any data to change above data\
47 ,'
48 ' and watch it!'),
49 style: TextStyle(fontSize: 30.0),
50 ),
51 ),
52 ),
53 SizedBox(
54 height: 10.0,
55 ),
56 ElevatedButtonWidget(),
57 ],
58 );
59 }
60 }

Now we can watch the Riverpod Provider object in a simpler way


than before.
To do that we need a local object inside the build() method.

1 final littleMonk = watch(classTypeProviderModel);

After that watching Provided object becomes much simpler.


Now, we can easily access the model class member state like this:

1 Text(
2 littleMonk.littleMonk,
3 style: TextStyle(fontSize: 30.0),
4 ),

We can also change the state by passing a string data like this:
3. Inherited Widget, Provider and State Management in Flutter 188

1 Text(
2 littleMonk
3 .fetchName('Now we can pass any data to change above data\
4 ,'
5 ' and watch it!'),
6 style: TextStyle(fontSize: 30.0),
7 ),

However, we can also change the above provided object state by


using our old Provider package and mix it with the Riverpod.
To do that we have used another custom widget and keep it inside
the controller folder.

1 ElevatedButtonWidget(),

Let us see how we can now use the old Provider package to change
the provided object state by pressing a button.

The old Provider package is really


gold!
Yes, that’s true. we have enough freedom to choose from any
available options. We prefer the ChangeNotifierProvider<T> widget
and return the Consumer<T> widgets just like before.
3. Inherited Widget, Provider and State Management in Flutter 189

1 import 'package:flutter/material.dart';
2 import 'package:provider/provider.dart';
3 import '../model/any_type_provider_model.dart';
4
5 class ElevatedButtonWidget extends StatelessWidget {
6 const ElevatedButtonWidget({
7 Key key,
8 }) : super(key: key);
9
10 @override
11 Widget build(BuildContext context) {
12
13 return ChangeNotifierProvider<ProviderModel>(
14 // <--- ChangeNotifierProvider
15 create: (context) => ProviderModel(),
16 child: Column(
17 children: [
18 Container(
19 padding: const EdgeInsets.all(15),
20 color: Colors.blue[200],
21 child: Consumer<ProviderModel>(
22 // <--- Consumer
23 builder: (context, myModel, child) {
24 return FloatingActionButton(
25 onPressed: () => myModel.changeName(),
26 child: Icon(Icons.add),
27 tooltip: 'Change Name',
28 );
29 },
30 ),
31 ),
32 SizedBox(
33 height: 20.0,
34 ),
35 Container(
3. Inherited Widget, Provider and State Management in Flutter 190

36 padding: const EdgeInsets.all(15),


37 color: Colors.redAccent,
38 child: Consumer<ProviderModel>(
39 // <--- Consumer
40 builder: (context, myModel, child) {
41 return Text(myModel.littleMonk);
42 },
43 ),
44 ),
45 ],
46 ),
47 );
48 }
49 }

Riverpod Provider and the old Provider package reduce the widget
rebuilds
As we have pressed the button the It changes the state of the
provided object globally. However, as you can see the locally
scoped data state has not been affected at all.
As we’ve tracked the widget rebuilds we can also see that on the
right hand side the statistics show how it benefits our app by
reducing widget rebuilds.
When we’ve clicked the button, it only affects our custom Elevat-
edButtonWidget() widget.
For more Flutter related Articles and Resources²⁰
²⁰https://sanjibsinha.com
4. Theme, Styling, Fonts
and Images Best
Practices in Flutter
Application wise global theme always reduces the workload. More-
over, from a central point we can control how our flutter app should
look like.
Although by default flutter comes with some help, still we need to
know how to use them wisely.
Since we’re talking about theme, we should know one thing first.
Theme is an inherited widget. And for that reason we can access
Theme from anywhere in our app.
We have seen the same thing in Provider package. Instead of
manually writing inherited widget, we use Provider package in
flutter.
Because provider is a wrapper around inherited widget, it manages
state easily.
The same thing happens when we use theme.
We actually take advantage of inherited widget.
For more Flutter related Articles and Resources²¹

What is theme flutter?


We can use theme to define many things, such as color, text style,
and even fonts.
²¹https://sanjibsinha.com
4. Theme, Styling, Fonts and Images Best Practices in Flutter 192

Although in this article we’ll take a look at color only, yet that
would be extensive. Color defines many things in a flutter app.
Moreover, style depends on color. So an attempt to impose applica-
tion wide uniformity is necessary.
Let’s take a look at the flutter app, we’re going to build.
This app will let user choose the correct answer to find the synonym
of a word.
As a result, we have a question and four elevated buttons on the
screen. Now user can click any button to check whether that answer
is correct or not.

Figure 4.1 – Using global theme to define color in flutter

Using global theme to define color in


flutter
As you can see, we’ve done many things at one go. From AppBar
to the design of the elevated buttons we’ve maintained uniformity
in color.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 193

As we progress, we’ll find, how we’ve done that application wide.


Globally.
Although to change the sate of the app, we use provider package,
we’re not going to the detail. We limit our discussion to the layout
part only.
However, we have to add the dependency in the pubspec.yaml file,
in the beginning.

1 dependencies:
2 flutter:
3 sdk: flutter
4
5 provider: ^5.0.0

Now, we’ll see how we can impose the uniformity of color red
across the whole application.
How do you get the theme color in flutter?
We want various shades of color red across the whole app. Not only
one shade of red, but you’ll find that we can use various shades of
red. From dark to light, using global theme.
To start with we have a main method that runs the app:

1 void main() {
2 runApp(
3 /// Providers are above [StylingThemingApp] instead of in\
4 side it,
5 /// so that tests can use [StylingThemingApp] while mocki\
6 ng the providers
7 MultiProvider(
8 providers: [
9 ChangeNotifierProvider(create: (_) => QuestionAndAnswerMo\
10 del()),
11 ],
4. Theme, Styling, Fonts and Images Best Practices in Flutter 194

12 child: StylingThemingApp(),
13 ),
14 );
15 }

We have a model class where we have managed application wide


state through change notifier provider.
However, we manage the application wide color theme through
Material app. We provide a Theme Data to the Material App
constructor. It helps us to share a common color theme.
Let us find, how we can do that.

1 class StylingThemingApp extends StatelessWidget {


2 const StylingThemingApp({Key key}) : super(key: key);
3
4 @override
5 Widget build(BuildContext context) {
6 return MaterialApp(
7 title: 'Styling Theming App',
8 theme: ThemeData(
9 primarySwatch: Colors.red,
10 ),
11 home: StylingThemingFirstView(),
12 );
13 }
14 }

This line of code plays the crucial role here.


4. Theme, Styling, Fonts and Images Best Practices in Flutter 195

1 return MaterialApp(
2 title: 'Styling Theming App',
3 theme: ThemeData(
4 primarySwatch: Colors.red,
5 ),
6 home: StylingThemingFirstView(),
7 );

Inside MaterialApp constructor, we’ve defined our theme by pass-


ing ThemeData constructor. Again, we provide another property to
the ThemeData constructor.

1 primarySwatch: Colors.red,

We could’ve chosen primary instead of primarySwatch. The main


difference between two is enormous.
The primarySwatch property lets us use different shades of color
red.

The model class and the main view


We’ve organized our code and kept them in separate folders. As
always I prefer MVC pattern.
Our model class looks like this:
4. Theme, Styling, Fonts and Images Best Practices in Flutter 196

1 import 'package:flutter/widgets.dart';
2
3 class QuestionAndAnswerModel extends ChangeNotifier {
4 List<Map<String, Object>> questions = [
5 {
6 'question': 'What is the synonym of Mendacity?',
7 'answers': ['truthfulness', 'daring', 'falsehood', 'enemy\
8 '],
9 },
10 {
11 'question': 'What is the synonym of Culpable?',
12 'answers': ['gay', 'guilty', 'falsehood', 'enemy'],
13 },
14 {
15 'question': 'What is the synonym of Rapacious?',
16 'answers': ['guilty', 'daring', 'falsehood', 'greedy'],
17 },
18 ];
19 int counter = 0;
20
21 String answerChecking = 'Click the accurate button for co\
22 rrect answer!';
23
24 void incrementCounter() {
25 counter++;
26 notifyListeners();
27
28 if (counter > 2) {
29 counter = 0;
30 }
31 checkAnswer();
32 }
33
34 void checkAnswer() {
35 if (counter == 0) {
4. Theme, Styling, Fonts and Images Best Practices in Flutter 197

36 answerChecking = 'Synonym of Rapacious is Greedy.';


37 } else if (counter == 1) {
38 answerChecking = 'Synonym of Mendacity is Falsehood.';
39 } else if (counter == 2) {
40 answerChecking = 'Synonym of Culpable is Guilty.';
41 } else {
42 answerChecking = 'Click the accurate button for correct a\
43 nswer!';
44 }
45 }
46 }

Now there are two ways, we can work with this model class and
methods. We can use the List map() method or in a Listview.builder
we can directly work with the list.
Here, we’ve used List map() method in our main screen.

1 class StylingThemingFirstView extends StatelessWidget {


2 const StylingThemingFirstView({Key key}) : super(key: key\
3 );
4
5 @override
6 Widget build(BuildContext context) {
7 return Container(
8 child: Scaffold(
9 appBar: AppBar(
10 title: Text('New Quiz App'),
11 ),
12 body: Center(
13 child: Padding(
14 padding: const EdgeInsets.all(8.0),
15 child: Column(
16 children: [
17 Text(
18 'Start the Quiz, test your English vocabulary:'
4. Theme, Styling, Fonts and Images Best Practices in Flutter 198

19 ' Find the right Synonym!'


20 ' Click any button and check your answer!',
21 style: TextStyle(
22 fontSize: 20.0,
23 fontWeight: FontWeight.bold,
24 ),
25 ),
26 SizedBox(height: 20.0),
27 QuestionWidget(
28 /// static method Provider.of<T>(context), which will beh\
29 ave similarly to watch
30 ///
31 questions:
32 Provider.of<QuestionAndAnswerModel>(context).questions,
33 // questions: context.watch<QuestionAndAnswerModel>().que\
34 stions,
35 counter: context.watch<QuestionAndAnswerModel>().counter,
36 ),
37 ...(context.watch<QuestionAndAnswerModel>().questions[con\
38 text
39 .watch<QuestionAndAnswerModel>()
40 .counter]['answers'] as List<String>)
41 .map(
42 (answer) => ButtonDEcorationWidget(
43 answer: answer,
44 ),
45 )
46 .toList(),
47 CheckAnswerWidget(),
48 ],
49 ),
50 ),
51 ),
52 ),
53 );
4. Theme, Styling, Fonts and Images Best Practices in Flutter 199

54 }
55 }

Controller widgets to control the


flow of business logic
We have four controller widgets that we have used in our above
main view page.
Between these four custom controller widgets, two are important.
Because they take the lead role in defining the shades of color we
use in container widget that contains the elevated buttons and text.
In those widgets we define the border of the container and also the
shade of text inside the button.
Let us take a quick look at the other two controller widgets first.

1 class QuestionWidget extends StatelessWidget {


2 const QuestionWidget({
3 Key key,
4 @required this.questions,
5 @required this.counter,
6 }) : super(key: key);
7
8 final List<Map<String, Object>> questions;
9 final int counter;
10
11 @override
12 Widget build(BuildContext context) {
13 return Text(
14 context
15 .watch<QuestionAndAnswerModel>()
16 .questions[context.watch<QuestionAndAnswerModel>().counte\
4. Theme, Styling, Fonts and Images Best Practices in Flutter 200

17 r]
18 ['question'],
19 style: TextStyle(
20 fontSize: 25.0,
21 fontWeight: FontWeight.bold,
22 ),
23 );
24 }
25 }

As you see, we’ve not used our global color theme inside this Text
style. We’ll do that later.
The other controller widget that checks whether the answers are
correct or not, doesn’t also uses the global color theme.

1 class CheckAnswerWidget extends StatelessWidget {


2 const CheckAnswerWidget({
3 Key key,
4 }) : super(key: key);
5
6 @override
7 Widget build(BuildContext context) {
8 return Text(
9 context.watch<QuestionAndAnswerModel>().answerChecking,
10 style: TextStyle(
11 fontSize: 20.0,
12 ),
13 );
14 }
15 }
4. Theme, Styling, Fonts and Images Best Practices in Flutter 201

What is primary color in Flutter?


As we have seen that primarySwatch is Material Color. Swatch is
a category and a range of color belongs to that category.
Let us see how we can define the color of text inside the elevated
buttons, first.
Then we’ll see how we can impose uniformity among the other
container components.

1 class ElevatedButtonWidget extends StatelessWidget {


2 ElevatedButtonWidget({Key key, this.answer}) : super(key:\
3 key);
4 final String answer;
5
6 @override
7 Widget build(BuildContext context) {
8 return ElevatedButton(
9 /// when you pass false to the listen parameter
10 /// like Provider.of<T>(context,listen: false) it will be\
11 have similar to read
12 ///
13 onPressed: () =>
14 // context.read<QuestionAndAnswerModel>().incrementCounte\
15 r(),
16 Provider.of<QuestionAndAnswerModel>(context, listen: fals\
17 e)
18 .incrementCounter(),
19 child: Text(
20 answer,
21 style: TextStyle(
22 fontSize: 25.0,
23 color: Theme.of(context).primaryColorLight,
24 ),
25 ),
26 );
4. Theme, Styling, Fonts and Images Best Practices in Flutter 202

27 }
28 }

How we can use the global theme here?


This line of code defines the uniformity.

1 color: Theme.of(context).primaryColorLight,

Since we’ve chosen the the light shade of red, the text inside
elevated buttons is of light shade.
Now we can click any button to check our answer, and the unifor-
mity will be maintained.

Figure 4.2 – Imposing uniformity of color through global theme

Imposing uniformity of color


through global theme
We’ve clicked the button to check the first answer. Although the
new questions appear, the uniformity of color remains the same.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 203

More about color

Now there are a few other things to take care of.


We keep each elevated button inside a container. Let us see the full
code, so we can discuss that matter in great detail.

1 class ButtonDEcorationWidget extends StatelessWidget {


2 const ButtonDEcorationWidget({
3 Key key,
4 this.answer,
5 }) : super(key: key);
6
7 final String answer;
8
9 @override
10 Widget build(BuildContext context) {
11 return Column(
12 children: [
13 SizedBox(
14 height: 10,
15 ),
16 Container(
17 margin: EdgeInsets.symmetric(
18 vertical: 1,
19 horizontal: 1,
20 ),
21 decoration: BoxDecoration(
22 border: Border.all(
23 /// here primarySwatch will work
24 ///
25 color: Theme.of(context).primaryColorDark,
26 width: 5,
27 ),
28 ),
29
4. Theme, Styling, Fonts and Images Best Practices in Flutter 204

30 /// each container has an elevated button that has border\


31 around
32 /// now, the padding between the border and the button
33 padding: EdgeInsets.all(8),
34 child: ElevatedButtonWidget(
35 answer: answer,
36 ),
37 ),
38 SizedBox(
39 height: 10,
40 ),
41 ],
42 );
43 }
44 }

We’ll concentrate on two key points. Firstly, watch this line of code:

1 color: Theme.of(context).primaryColorDark,

Secondly, watch this line of code:

1 padding: EdgeInsets.all(8),

First of all, we have set the border to primary color dark. That
means each button will have a dark red border of certain width.
At the same time, each button will maintain a padding. It ensures
that there will be a definite distance between each button and the
border.
We can not only set the background and foreground color, but also
maintain the consistency in our color scheme from one single place.
How about changing the color from red to green?
Let’s try.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 205

Figure 4.3 – How do you change the theme color globally in flutter

How do you change the theme color


globally in flutter
We’ve changed the whole color theme globally from one place now:

1 return MaterialApp(
2 title: 'Styling Theming App',
3 theme: ThemeData(
4 primarySwatch: Colors.lightGreen,
5 ),
6 home: StylingThemingFirstView(),
7 );

Configuring color theme globally in flutter is not difficult. Still


we need to understand the core conceptions working behind these
factors.
To be a better flutter developer we need to dig deep and learn the
core conceptions.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 206

Using multiple Google fonts in


Flutter
Using multiple fonts in Flutter is not difficult. However, you need
to follow a few rules.
Again, these rules are flexible. Because many options are available,
you can choose any.
Moreover, you can use any custom font. And besides, you can use
Google font package. The advantage of this font package is multiple.
It gives you access to over 1,000 open-sourced font families to use.
Although I’ve described and explained the code snippets, still if you
want to get all the code in one place, then please visit my GitHub
repository for this project alone.
How to use a custom font in Flutter?
What is the best way to use a custom font?
Will you use a custom-built font by designers? Or, you will use
accessible over-1,000 Google font families?
Although the choice is your, still I think, stick with Google fonts.
For a quick development, it’s the best choice. Choosing from open-
sourced Google font families is much easier than other available
options.
To begin with, add a fonts section in pubspec.yaml file.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 207

1 fonts:
2 - family: Anton
3 fonts:
4 - asset: assets/fonts/Anton-Regular.ttf
5
6 - family: LibreBaskerville
7 fonts:
8 - asset: assets/fonts/LibreBaskerville-Regular.ttf
9 - asset: assets/fonts/LibreBaskerville-Bold.ttf
10 weight: 700
11
12 - family: SyneMono
13 fonts:
14 - asset: assets/fonts/SyneMono-Regular.ttf
15
16 - family: TrainOne
17 fonts:
18 - asset: assets/fonts/TrainOne-Regular.ttf

We’re going to use four open-sourced Google fonts. Each entry in


this list should have a “family” key with the font family name. And
the “fonts” key points to a list of asset paths. It is actually the folder
where you’ve kept the fonts.

What is Flutter default font?


The default font of MaterialApp is Roboto. It’s also a Google font.
However, we want to customize it in our flutter app.
As we’ve said earlier, we can do that adopting two ways. We’ve
already shown one way in the above example.
Our first target is simple. Override the flutter default font and
declare a global theme based font.
To do that, we should start with MaterialApp widget. But, we must
run our app first.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 208

1 import 'package:flutter/material.dart';
2 import 'views/lbta_material_scaffold.dart';
3
4 void main() {
5 runApp(ListviewBuilderThemingApp());
6 }

Next, we need the Material App design part where we can set the
theme.

1 class ListviewBuilderThemingApp extends StatelessWidget {


2 const ListviewBuilderThemingApp({Key key}) : super(key: k\
3 ey);
4
5 @override
6 Widget build(BuildContext context) {
7 return MaterialApp(
8 title: 'Global Theme with Listview Builder',
9 theme: ThemeData(
10 primarySwatch: Colors.amber,
11 accentColor: Colors.purple,
12 fontFamily: 'LibreBaskerville',
13
14 /// global title theme for the rest of the app
15 ///
16 textTheme: ThemeData.light().textTheme.copyWith(
17 headline6: TextStyle(
18 fontFamily: 'LibreBaskerville',
19 fontSize: 18.0,
20 // fontWeight: FontWeight.bold,
21 ),
22 ),
23
24 /// one title theme for the app bar
25 ///
4. Theme, Styling, Fonts and Images Best Practices in Flutter 209

26 appBarTheme: AppBarTheme(
27 textTheme: ThemeData.light().textTheme.copyWith(
28 headline6: TextStyle(
29 fontFamily: 'Anton',
30 fontSize: 22.0,
31 // fontWeight: FontWeight.bold,
32 ),
33 ),
34 ),
35 ),
36 home: ScaffoldingLBTA(),
37 );
38 }
39 }

We want that global title theme should differ with the AppBar title
theme.
We’ve done that in the above code.
We’ve defined the Material App part, but we need to Scaffold the
body part.

What fonts are available in Flutter?


We want to show different type of fonts. Hence we must make them
available in our flutter app.
Now we need Scaffold widget.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 210

1 class ScaffoldingLBTA extends StatelessWidget {


2 const ScaffoldingLBTA({Key key}) : super(key: key);
3
4 @override
5 Widget build(BuildContext context) {
6 return Scaffold(
7 appBar: AppBar(
8 title: Text('Global Theme with Listview Builder'),
9 ),
10 body: LBTAFirstView(),
11 );
12 }
13 }

Then we need the first view where we can declare other views. And
in those views, we can separately define our custom fonts.

1 class LBTAFirstView extends StatelessWidget {


2 const LBTAFirstView({Key key}) : super(key: key);
3
4 @override
5 Widget build(BuildContext context) {
6 return ListView(
7 children: [
8 AboutApp(),
9 FirstTextView(),
10 SecondTextView(),
11 GoogleFontView(),
12 ],
13 );
14 }
15 }

Based on the fonts family keys we’ve already set in the pub-
spec.yaml file, we can move ahead.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 211

As you see in the above code, there are four views listed as children.
We’ll come to the GoogleFontView() part later. Because we have
used Google font package there, it has no relation with the other
three custom view page.

How do you style a text flutter?

Let us start with the AboutApp() custom view widget.


There is nothing special in this custom view widget. A simple text
that we want to keep inside a container. To make the container
distinguishable we have splashed it with a color. We’ve also set the
margin in a way, so that it touches the AppBar wall.
Pretty simple step, although we’ve controlled it before in our
MaterialApp global theming part.

1 class AboutApp extends StatelessWidget {


2 const AboutApp({Key key}) : super(key: key);
3
4 @override
5 Widget build(BuildContext context) {
6 return Container(
7 /// this margin indicates the boundary with the outer spa\
8 ce
9 ///
10 margin: EdgeInsets.fromLTRB(20, 0, 20, 0),
11 color: Theme.of(context).primaryColorLight,
12
13 /// this padding indicates the space between the outer bo\
14 rder
15 /// and the inner content
16 padding: EdgeInsets.all(8),
17 child: Text(
18 'This app is all about using Listview.builder() method'
4. Theme, Styling, Fonts and Images Best Practices in Flutter 212

19 ' or ListTile to show uniformity in theming. We\'ll see'


20 ' how we can change fonts and use images. '
21 'Here we have set the margin of container in a way, so it\
22 touches '
23 'the app bar wall.',
24 /* style: TextStyle(
25 fontFamily: 'LibreBaskerville',
26 fontSize: 20,
27 color: Theme.of(context).primaryColorDark,
28 ), */
29 style: Theme.of(context).textTheme.headline6,
30 ),
31 );
32 }
33 }

However, we’ve not finished yet. Now we will create two more
views based on the font family we’ve had declared in our pub-
spec.yaml file.
In those views, we’ll set the text style explicitly.

How do you change the text color in


Flutter?
Yes, that will be our first challenge in the next step. We’ll not only
change the text color, but we’ll also make sure two more things.
We place these two text samples in two separate containers, with
different colors.
Before proceeding to the next code, let me explain a few steps we’ve
adopted in the above-mentioned code.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 213

1 /* style: TextStyle(
2 fontFamily: 'LibreBaskerville',
3 fontSize: 20,
4 color: Theme.of(context).primaryColorDark,
5 ), */
6 style: Theme.of(context).textTheme.headline6,

In the AboutApp widget we take the custom title theme. We had


defined that in our MaterialApp widget.
Now, we could have set the text theme two ways. Either we could
have set it explicitly, or we could have followed the global title
theme.
As you see, we’ve commented out one and adopted another.
The advantage of setting the text theme explicitly is we can control
many things that also includes color of the text.

How do I add Google fonts to


Flutter?
Now, we come to the final point.
Although we’ve seen how to add different Google font families by
adding the dependencies. But we’ve not finished our journey yet.
Based on those dependencies we can add two more simple Text
widgets.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 214

1 class FirstTextView extends StatelessWidget {


2 const FirstTextView({Key key}) : super(key: key);
3
4 @override
5 Widget build(BuildContext context) {
6 return Container(
7 margin: EdgeInsets.all(8),
8 padding: EdgeInsets.all(8),
9 color: Colors.lime,
10 child: Text(
11 'This is a first text view, where we\'ll display '
12 'text belongs to the font family SyneMono',
13 style: TextStyle(
14 fontFamily: 'SyneMono',
15 fontSize: 20,
16 fontStyle: FontStyle.italic,
17 ),
18 ),
19 );
20 }
21 }

Here the font family is “SyneMono”. The color of the container is


lime. And the font size is 20.
The second text widget looks like this:
4. Theme, Styling, Fonts and Images Best Practices in Flutter 215

1 class SecondTextView extends StatelessWidget {


2 const SecondTextView({Key key}) : super(key: key);
3
4 @override
5 Widget build(BuildContext context) {
6 return Container(
7 margin: EdgeInsets.all(8),
8 padding: EdgeInsets.all(8),
9 color: Theme.of(context).textTheme.headline6.color.withGr\
10 een(250),
11 child: Text(
12 'This is a second text view, where we\'ll display '
13 'text belongs to the font family TrainOne',
14 style: TextStyle(
15 fontFamily: 'TrainOne',
16 fontSize: 20,
17 fontStyle: FontStyle.italic,
18 color: Theme.of(context).textTheme.headline6.color.withRe\
19 d(255),
20 ),
21 ),
22 );
23 }
24 }

Here, in the above code snippet, the font family is “TrainOne”.


However, we’ve controlled the color of the text in a different way
using the global text title theme.
Watch this line:

1 color: Theme.of(context).textTheme.headline6.color.withRe\
2 d(255),

We’ve also set the color of the container in the same way.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 216

1 Container(
2 margin: EdgeInsets.all(8),
3 padding: EdgeInsets.all(8),
4 color: Theme.of(context).textTheme.headline6.color.withGr\
5 een(250),
6 child: Text(
7
8 ...

We’ve not finished our app. Yet, up to this point it looks like this:

Figure 4.4 – Adding dependencies of Google fonts and display them through
Text widgets in different containers

Adding dependencies of Google fonts and display them through


Text widgets in different containers

Adding Google fonts package to


Flutter
We’ll add the google_fonts package for Flutter first. To do that we
should add the dependency to our package’s pubspec.yaml file:
4. Theme, Styling, Fonts and Images Best Practices in Flutter 217

1 dependencies:
2 google_fonts: ^2.0.0

What’s the advantage? Let’s take a look at that first.


This google_fonts package for Flutter allows us to easily use any of
the 977 fonts from fonts.google.com in your Flutter app.
If you count the variants of these fonts, the number will easily cross
1,000.
However, in our example, we’ve used only two. Quite familiar
name, though. They are “lato” and “opensans”.

1 class GoogleFontView extends StatelessWidget {


2 const GoogleFontView({Key key}) : super(key: key);
3
4 @override
5 Widget build(BuildContext context) {
6 return Container(
7 margin: EdgeInsets.all(8),
8 child: Column(
9 children: [
10 Container(
11 padding: EdgeInsets.all(15),
12 color: Colors.purpleAccent,
13 child: Text(
14 'This is the first instance, we\'re using Google font "la\
15 to" in bold',
16 style: GoogleFonts.lato(
17 fontWeight: FontWeight.bold,
18 fontSize: 20.0,
19 ),
20 ),
21 ),
22 Container(
23 padding: EdgeInsets.all(15),
4. Theme, Styling, Fonts and Images Best Practices in Flutter 218

24 color: Colors.amberAccent,
25 child: Text(
26 'The second instance of using Google font "opensans" in b\
27 old',
28 style: GoogleFonts.openSans(
29 fontWeight: FontWeight.bold,
30 fontSize: 20.0,
31 ),
32 ),
33 ),
34 ],
35 ),
36 );
37 }
38 }

Don’t forget to import the package on the top:

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

As we’ve used two different Text widgets, in two different contain-


ers, now we are all set to go.
This time our app looks like this:
4. Theme, Styling, Fonts and Images Best Practices in Flutter 219

Figure 4.5 – Adding google_fonts package for Flutter and display them on app’s
screen

As we see each font here works as a method that passes parameters


using which we can control the size, style, weight, color, etc.
That’s a great advantage indeed.

1 style: GoogleFonts.openSans(
2 fontWeight: FontWeight.bold,
3 fontSize: 20.0,
4 ),

We’ve covered a great distance. Now we can control each font of


our flutter app.

How to display image in Flutter?


Displaying image in flutter is not difficult at all. However, you need
to follow a few rules.
Again following a few rules is not hard, though. But you need to
know them.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 220

How do you know them?


Well, this article is going to describe the mechanism.
First, we’ll use Image asset method. And then we’ll use Image
network method, where we’ll display image from web. Certainly,
for the network method, we need internet connection.

How do I use Image Widget in Flutter?

We can use Image widget in various ways. Moreover, each way is


quite simple. Of course, you need to know the rules.
With reference to the rules, the first rule takes us to the asset method
of Image widget.
To understand how the asset method works, let us first see an image
of our flutter app.

Figure 4.6 – Image on the Mobile screen from asset method


4. Theme, Styling, Fonts and Images Best Practices in Flutter 221

Display image in Flutter using Image


widget asset method
In the theming app that we have seen before, we just added an
image above the quiz questions.

How did I display the image?

The following code snippet will show you, how we did that.

1 class FirstImage extends StatelessWidget {


2 const FirstImage({
3 Key key,
4 }) : super(key: key);
5
6 @override
7 Widget build(BuildContext context) {
8 return Container(
9 height: 150,
10 margin: EdgeInsets.all(10),
11 padding: EdgeInsets.all(2),
12 child: Image.asset(
13 'assets/images/s.png',
14 width: 150,
15 height: 150,
16 fit: BoxFit.fitHeight,
17 ),
18 );
19 }
20 }

In the above code, this part is important.


4. Theme, Styling, Fonts and Images Best Practices in Flutter 222

1 child: Image.asset(
2 'assets/images/s.png',
3 width: 150,
4 height: 150,
5 fit: BoxFit.fitHeight,
6 ),

As you see, we mentioned the image path as a parameter in the


Image.asset() method.
And there are other parameters too, such as width and height.
Another named parameter is “fit” that maintains the size of image
in the container.
However, this code will work only on one condition. Beforehand,
we need to add the asset dependency in our pubspec.yaml file in
the following manner.

1 flutter:
2 uses-material-design: true
3 assets:
4 - assets/images/

How to display image from internet


in Flutter?
If we want to display image from internet in flutter, we need
internet connection first. When we use the Image.asset() method,
the source of image is local. Therefore, it’s fast.
Nonetheless the Image.network() method works fine only with the
internet connection, still developers use it. Even though there are a
few ifs and buts involved here.
Firstly, we need internet connection.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 223

Secondly, the source of the image plays an important role. The


source should remain alive as long as your app works in the
production level.
Finally, we need to be careful about the alternative source.
Why so?
Consider a situation where your network-image-server is down.
Then your app is unable to display any image.
To meet that requirement we have to put up an alternative way.
Because of so much hassles, you need to be careful and decide
which one to adopt.

How do I display an image from API in


Flutter?
Now we’re going to see how we can do that. It’s neither difficult,
nor it takes a lot of pain. On the contrary, it’s very basic.
We need to pass the full path of the source.

1 class SecondImage extends StatelessWidget {


2 const SecondImage({
3 Key key,
4 }) : super(key: key);
5
6 @override
7 Widget build(BuildContext context) {
8 return DecoratedBox(
9 decoration: BoxDecoration(
10 color: Colors.white,
11 border: Border.all(),
12 borderRadius: BorderRadius.circular(20),
13 ),
14 child: Image.network(
4. Theme, Styling, Fonts and Images Best Practices in Flutter 224

15 'https://i0.wp.com/sanjibsinha.com/wp-content/uploads/202\
16 1/04/a-1.jpg'),
17 );
18 }
19 }

As you see, we take the image from this website.


And it looks like this:

Figure 4.7 – How to display image from API in Flutter

We can scroll the app, and the second image that comes from API
is quite prominent.

A Comparison between Old and New


Updated Buttons
Although changing state is important in Flutter, yet we must take
care of handling it efficiently.
Why?
4. Theme, Styling, Fonts and Images Best Practices in Flutter 225

The main reason is widget rebuilds. We need to reduce widget


rebuilds.
Otherwise our app becomes less performant. On the contrary, we
want our app runs faster. And it only happens when we make a
performant flutter app.
Now, we all know that we need different type of buttons in flutter
to change state.

How do you make a button flutter?


There are several ways. Whatsoever, all the buttons do the same job.
We can press any button and pass a callback to change the state of
the flutter app.
To test any button you can pass a print() function as a callback and
see how it works.
In this article, we are going to talk about three buttons that Flutter
team has recently announced as deprecated.
These buttons are RaisedButton, FlatButton, and OutlineButton.
Instead of these buttons Flutter team suggests three other buttons.
They are ElevatedButton, TextButton, and OutlinedButton.
Now, question is: are they different? If so, in what way?
Although those deprecated buttons have still been working in
hundred thousand flutter apps, Flutter team suggests to migrate to
the new updated buttons.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 226

How do you use the raised button in


flutter?
I hope after reading till this part, this question doesn’t arise any
more!
Flutter team tells us to use ElevatedButton instead of RaisedButton.
If you run any app with these three deprecated buttons, the app still
runs fine, but it’s now better to migrate.
Let us first see how these three deprecated buttons had worked
before.

1 Column(
2 children: [
3 RaisedButton(
4 color: Colors.redAccent,
5 textColor: Colors.white,
6 onPressed: () {
7 print('Pressed Raised Button');
8 },
9 child: Text('Press RaisedButton',
10 style: Theme.of(context).textTheme.headline4,
11 ),
12 ),
13 SizedBox(height: 25,),
14 FlatButton(
15 color: Colors.lightGreenAccent,
16 textColor: Colors.white,
17 onPressed: () {
18 print('Pressed FlatButton');
19 },
20 child: Text('Press FlatButton',
21 style: Theme.of(context).textTheme.headline4,
22 ),
4. Theme, Styling, Fonts and Images Best Practices in Flutter 227

23 ),
24 SizedBox(height: 25,),
25 OutlineButton(
26 borderSide: BorderSide(color: Colors.black,),
27 textColor: Colors.redAccent,
28 onPressed: () {
29 print('Pressed OutlineButton');
30 },
31 child: Text('Press OutlineButton',
32 style: Theme.of(context).textTheme.headline4,
33 ),
34 ),
35 ],
36 ),

You may have already used these three deprecated buttons. So


nothing new.
In the style section we have followed a global theme. Otherwise we
could have made it look different.

Deprecated buttons in Flutter

On the right hand side of our screen, we can see another interesting
development. We can press any button several times, but that does
not increase widget rebuilds.
Why does it happen?
If we watch the inheritance tree of RaisedButton, it makes clear.
The inheritance tree looks like this:

1 OBJECT->DIAGNOSTICABLETREE->WIDGET->STATELESSWIDGET->MATE\
2 RIALBUTTON->RAISEDBUTTON.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 228

Since the RaisedButton is a child class of state less widget, it does


not increase widget rebuilds. Whatever number we press the button,
it does not rebuild the widget.
Although the Flutter team makes it deprecated, it’s served us wisely.

How do you decorate the button in


flutter?
In case of those three deprecated buttons, the decoration part was
simpler than the new updated ones.
Yes, decoration plays a big role in any flutter app. It must look good.
No doubt, the ElevatedButton, TextButton, and OutlinedButton
look better than the previous ones. And as long as we consider
decoration, there are different methods, which we’re going to see.
In the first way, we can decorate the updated new buttons this way:

1 Column(
2 children: [
3 ElevatedButton(
4 style: ButtonStyle(
5 backgroundColor: MaterialStateProperty.all(Colors.redAcce\
6 nt),
7 foregroundColor: MaterialStateProperty.all(Colors.white),
8 ),
9 onPressed: () {
10 print('Pressed Elevated Button');
11 },
12 child: Text('Press ElevatedButton',
13 style: TextStyle(
14 fontFamily: 'Anton',
15 fontSize: 25,
16 ),
4. Theme, Styling, Fonts and Images Best Practices in Flutter 229

17 ),
18 ),
19 SizedBox(height: 25,),
20 TextButton(
21 style: ButtonStyle(
22 backgroundColor: MaterialStateProperty.all(Colors.blue),
23 foregroundColor: MaterialStateProperty.all(Colors.white),
24 ),
25 onPressed: () {
26 print('Pressed FlatButton');
27 },
28 child: Text('Press FlatButton',
29 style: TextStyle(
30 fontFamily: 'LibreBaskerville',
31 fontSize: 25,
32 ),
33 ),
34 ),
35 SizedBox(height: 25,),
36 OutlinedButton(
37 style: ButtonStyle(
38 backgroundColor: MaterialStateProperty.all(Colors.green),
39 foregroundColor: MaterialStateProperty.all(Colors.white),
40 ),
41 onPressed: () {
42 print('Pressed OutlineButton');
43 },
44 child: Text('Press OutlineButton',
45 style: TextStyle(
46 fontFamily: 'Anton',
47 fontSize: 25,
48 ),
49 ),
50 ),
51 ],
4. Theme, Styling, Fonts and Images Best Practices in Flutter 230

52 ),

As you can see, this style part is same for all three new buttons that
replace the new ones.

1 style: ButtonStyle(
2 backgroundColor: MaterialStateProperty.all(Colors.green),
3 foregroundColor: MaterialStateProperty.all(Colors.white),
4 ),

However, it does not assure us about reducing the widget rebuilds.


Even we hover our mouse over these buttons, widgets get rebuilt.
Moreover, pressing any button increase the number of widget
rebuilds.
Take a look at the following image:

Figure 4.8 – Updated new buttons replace deprecated buttons in flutter

On the right hand side of the screen, we have tracked the number
of widget rebuilds. Now it is much higher.
Why does it happen?
The reason is simple, if you take a look at the inheritance tree of
ElevatedButton widget, it makes clear.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 231

1 OBJECT->DIAGNOSTICABLETREE->WIDGET->STATEFULWIDGET->BUTTO\
2 NSTYLEBUTTON->ELEVATEDBUTTON

We can clearly see that elevated button is a child class of stateful


widget. That’s the main reason why it rebuilds the widgets.

How do you change the shape of the button


in flutter?
Flutter team has added more functionalities in these new updated
buttons. For that reason, we can decorate and change the shape of
the buttons. Not only that, we can add elevation shadow making
the buttons look great.
Even though it enhances widget rebuilds, we can change the
decoration code in this way:

1 Column(
2 children: [
3 ElevatedButton(
4 style: ElevatedButton.styleFrom(
5 primary: Colors.blueGrey,
6 onPrimary: Colors.white,
7 ),
8 onPressed: () {
9 print('Pressed Elevated Button');
10 },
11 child: Text('Press ElevatedButton',
12 style: TextStyle(
13 fontFamily: 'Anton',
14 fontSize: 25,
15 ),
16 ),
17 ),
18 SizedBox(height: 25,),
4. Theme, Styling, Fonts and Images Best Practices in Flutter 232

19 TextButton(
20 style: TextButton.styleFrom(
21 elevation: 40.0,
22 backgroundColor: Colors.yellow,
23 ),
24 onPressed: () {
25 print('Pressed FlatButton');
26 },
27 child: Text('Press FlatButton',
28 style: TextStyle(
29 fontFamily: 'LibreBaskerville',
30 fontSize: 25,
31 ),
32 ),
33 ),
34 SizedBox(height: 25,),
35 OutlinedButton(
36 style: OutlinedButton.styleFrom(
37 primary: Colors.white,
38 backgroundColor: Colors.red,
39 side: BorderSide(
40 color: const Color(4278190000),
41 ),
42 elevation: 40.0,
43 ),
44 onPressed: () {
45 print('Pressed OutlinedButton');
46 },
47 child: Text('Press OutlineButton',
48 style: TextStyle(
49 fontFamily: 'Anton',
50 fontSize: 25,
51 ),
52 ),
53 ),
4. Theme, Styling, Fonts and Images Best Practices in Flutter 233

54 ],
55 ),

Now, the main difference is in the style section. Now each class
takes the style from within. Take a look at the OutlinedButton style:

1 style: OutlinedButton.styleFrom(
2 primary: Colors.white,
3 backgroundColor: Colors.red,
4 side: BorderSide(
5 color: const Color(4278190000),
6 ),
7 elevation: 40.0,
8 ),

It’s not same any more. Furthermore, it also looks different.


New updated buttons look different than the old ones
However, we cannot reduce the widget rebuilds.

Figure 4.9 – How widgets build the UI of the application

As a result, for a big complicated app, it may affect the app’s


performance if we cannot use it wisely.
4. Theme, Styling, Fonts and Images Best Practices in Flutter 234

How to use DevTools in Flutter?


As the name suggests, DevTools is primarily a tool. In addition, it
lets us know the performance of the app. And besides that, it also
helps us in debugging.
All together, Flutter or Dart DevTools is very handy. However, as a
developer,you must know how to use it.
First of all, to open DevTools you need to run your app first. Then
you’ll click the DevTools link so that it opens either in Chrome or
Mozilla browser.
In Visual Studio Code Editor at the bottom right corner you will
find the DevTools. Watch the next image.

Figure 4.10 – DevTools in Visual Studio Code Editor

Flutter or Dart DevTools in Visual Studio


Code

What is Flutter tool?


4. Theme, Styling, Fonts and Images Best Practices in Flutter 235

People often find it ambiguous and confuse flutter with DevTools.


Certainly, both are tools all the same they are not equal in meaning.
On the contrary, Flutter is a UI software tool kit, whereas DevTools
gives you support regarding the application you make with Flutter.
Now, question is what can we do with the help of DevTools?

Flutter DevTools showing the app structure

On the left side we can see the emulator. And on the right side
the DevTools shows the app structure. Since we’ve clicked the
OutLinedButton, it gives us all the details about the button.
Moreover, it inspects the whole UI layout and gives us details of the
state of flutter app.

Figure 4.11 – DevTools inspects the whole UI layout and gives us details
4. Theme, Styling, Fonts and Images Best Practices in Flutter 236

How to check the Jank performance in


Flutter?

DevTools is the answer. In addition, the next image will show you
three different types of bars. Moreover, these bars in the DevTools
will show you how the app performs.
The Jank performance issues come with slow frame that makes your
app slower.

Figure 4.12 – Devtools shows the Jank performance issues

Although our target is to make the app as faster as possible, still we


need to check it always. In the above image the bars in red-accent
colors are all jank frames.
Besides CPU or Network profiling, you can debug memory issues
by watching this full report.
These are main advantages of DevTools.
For more Flutter related Articles and Resources²²
²²https://sanjibsinha.com
5. How to handle
collections of items, all
about List and Map and
Code Structure in Flutter
How do I add a list in Flutter? How do I show a list in Flutter?
Adding a list and showing the list is quite simple in Flutter. We need
to follow a guideline, yet that is not too difficult.
We’re going to learn how we can add a list. We’ll also learn how to
show that list on virtual device.
Suppose, we’re creating an expense list. However, to do that, we
need a class like this:

1 import 'package:flutter/foundation.dart';
2
3 class ExpenseList {
4 String id;
5 String title;
6 double amount;
7 DateTime date;
8
9 ExpenseList({
10 @required this.id,
11 @required this.title,
12 @required this.amount,
13 @required this.date,
14 });
15 }
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 238

Moreover, to organize our code we keep this class in model folder.


For more Flutter related Articles and Resources²³

How do I add a list in Flutter?


Furthermore, now we need a state less widget. And in that widget
let’s add it manually at first.

1 class ExpenseFirstPage extends StatelessWidget {


2 ExpenseFirstPage({Key key}) : super(key: key);
3
4 final List<ExpenseList> expenseList = [
5 ExpenseList(
6 id: '1',
7 title: 'Fruits',
8 amount: 200.00,
9 date: DateTime.now(),
10 ),
11 ExpenseList(
12 id: '2',
13 title: 'Vegetables',
14 amount: 200.08,
15 date: DateTime.now(),
16 ),
17 ];
18
19 ...
20
21 // code is incomplete for brevity

As you can see, we’ve added two expense list using the class.
Of course, flutter allows us to do that.
²³https://sanjibsinha.com
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 239

And it looks like this:


5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 240

Figure 5.1 – Adding a list in flutter


5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 241

We’ve already added a list, and in addition to that, we show it on


the screen.

How do I show a list in flutter?


We can show the list many ways. It depends on how you’d like to
design your flutter app.
Additionally, we can change our build method in flutter.
We’ve seen the first instance on screen. Even though it was for
adding purpose, we can show the list as well.
In that case, the build method looks like this:

1 @override
2 Widget build(BuildContext context) {
3 return Center(
4 child: ListView(
5 padding: EdgeInsets.all(8),
6 children: [
7 Container(
8 width: double.infinity,
9 margin: EdgeInsets.all(8),
10 padding: EdgeInsets.all(8),
11 color: Theme.of(context).accentColor,
12 child: Card(
13 child: Text(
14 'Chart',
15 style: Theme.of(context).textTheme.headline5,
16 ),
17 elevation: 10,
18 ),
19 ),
20 Column(
21 crossAxisAlignment: CrossAxisAlignment.center,
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 242

22 children: expenseList.map((expense) {
23 return Row(
24 mainAxisAlignment: MainAxisAlignment.end,
25 children: [
26 Icon(Icons.insert_emoticon_rounded),
27 Card(
28 child: Text(
29 'Item: ${expense.title}',
30 style: Theme.of(context).textTheme.headline5,
31 ),
32 elevation: 10,
33 ),
34 Card(
35 child: Text(
36 'Expense: ${expense.amount.toString()}',
37 style: Theme.of(context).textTheme.headline6,
38 ),
39 elevation: 10,
40 ),
41 ],
42 );
43 }).toList(),
44 ),
45 ],
46 ),
47 );
48 }

Now, we can change the design the list a little bit. So it looks like
this.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 243

Showing a list in flutter


We’ve used a Column widget inside the Row. As a result, the title
shows above the time.
To do that we’ve tweaked our previous code.
So, it looks like this:

1 @override
2 Widget build(BuildContext context) {
3 return Center(
4 child: ListView(
5 padding: EdgeInsets.all(8),
6 children: [
7 Container(
8 width: double.infinity,
9 margin: EdgeInsets.all(8),
10 padding: EdgeInsets.all(8),
11 color: Theme.of(context).accentColor,
12 child: Card(
13 child: Text(
14 'Chart',
15 style: Theme.of(context).textTheme.headline5,
16 ),
17 elevation: 10,
18 ),
19 ),
20 Column(
21 crossAxisAlignment: CrossAxisAlignment.center,
22 children: expenseList.map((expense) {
23 return Container(
24 margin: EdgeInsets.all(5),
25 padding: EdgeInsets.all(5),
26 child: Row(
27 mainAxisAlignment: MainAxisAlignment.end,
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 244

28 children: [
29 Icon(Icons.insert_emoticon_rounded),
30 Column(
31 children: [
32 Card(
33 child: Text(
34 'Item: ${expense.title}',
35 style: Theme.of(context).textTheme.headline5,
36 ),
37 elevation: 10,
38 ),
39 SizedBox(
40 height: 10,
41 ),
42 Card(
43 child: Text('${expense.date.toString()}',
44 style: TextStyle(
45 fontSize: 15.0,
46 fontWeight: FontWeight.bold,
47 )),
48 elevation: 10,
49 ),
50 ],
51 ),
52 Card(
53 child: Text(
54 '${expense.amount.toString()}',
55 style: Theme.of(context).textTheme.headline6,
56 ),
57 elevation: 10,
58 ),
59 ],
60 ),
61 );
62 }).toList(),
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 245

63 ),
64 ],
65 ),
66 );
67 }

Still we’re not happy with the design.


Why?
Because we want something more. On the right hand side, we want
a “delete” button.
What can we do?
First of all, we need to make a room for that.

How to delete a list in Flutter?


We’ll add a button later. But before that we need to show it first.
Just like the following image.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 246

Figure 5.2 – How to delete a list in flutter?


5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 247

We’re almost there.


However, we need to change the design again.
So we change the build method. And it looks bigger than the
previous one.

1 @override
2 Widget build(BuildContext context) {
3 return Center(
4 child: ListView(
5 padding: EdgeInsets.all(8),
6 children: [
7 Container(
8 width: double.infinity,
9 margin: EdgeInsets.all(8),
10 padding: EdgeInsets.all(8),
11 color: Theme.of(context).accentColor,
12 child: Card(
13 child: Text(
14 'Chart',
15 style: Theme.of(context).textTheme.headline5,
16 ),
17 elevation: 10,
18 ),
19 ),
20 Column(
21 crossAxisAlignment: CrossAxisAlignment.center,
22 children: expenseList.map((expense) {
23 return Container(
24 margin: EdgeInsets.all(5),
25 padding: EdgeInsets.all(5),
26 child: Row(
27 mainAxisAlignment: MainAxisAlignment.end,
28 children: [
29 Icon(Icons.insert_emoticon_rounded),
30 Column(
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 248

31 children: [
32 Card(
33 child: Text(
34 '${expense.title}',
35 style: Theme.of(context).textTheme.headline5,
36 ),
37 elevation: 10,
38 ),
39 SizedBox(
40 height: 10,
41 ),
42 Card(
43 child: Text('${expense.date.toString()}',
44 style: TextStyle(
45 fontSize: 12.0,
46 fontWeight: FontWeight.bold,
47 )),
48 elevation: 10,
49 ),
50 ],
51 ),
52 Card(
53 child: Text(
54 '${expense.amount.toString()}',
55 style: Theme.of(context).textTheme.headline6,
56 ),
57 elevation: 10,
58 ),
59 Expanded(
60 child: Text(
61 ' -> DELETE',
62 softWrap: false,
63 overflow: TextOverflow.fade,
64 style: Theme.of(context).textTheme.headline6,
65 ),
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 249

66 )
67 ],
68 ),
69 );
70 }).toList(),
71 ),
72 ],
73 ),
74 );
75 }

We’ll build the full app. But we cannot do that in one single post.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 250

Figure 5.3 – Finally it looks this, although not very good looking! So, next we’ll
try to improve the design
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 251

So take a look at this Expense List GitHub Repository.²⁴

What is ListView Flutter? How do I


use ListView in Flutter?
ListView is the most common scrolling widget. Furthermore it
allows its children to scroll endlessly.
And it’s the main advantage of ListView.
There are other advantages too as well. In flutter, for that reason
we keep ListView handy.
This article is continuation of the previous article - How do I add a
list in Flutter? How do I show a list in Flutter. However, we’re going
to build the app from scratch this time.
This time our main challenge is to design the app in a better way.
To keep your memory afresh, let’s take a look at the previous design.
The last image was the last position where we’d left it. But, this
time, the first look of our flutter app will be like this:
²⁴https://github.com/sanjibsinha/expense_list
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 252

Figure 5.4 – Using a ListView to show a List of items

As you can see, this time our expense list app looks far better than
before. However it’s not finished yet. It’s just the beginning.
What is ListView in Flutter?
Theoretically the ListView is a widget that allows user to scroll.
However, it has many other purposes.
It displays its children widget in a scrolling manner. As a result, we
can accommodate many other widgets inside it.
By the way, some widgets are invisible. But, they allow the visible
widgets to show the designs.
ListView belongs to the invisible side. Yet it’s one of the rarest
invisible widget, that scrolls.
As we progress, let’s try to understand the app design.
We’re going to make a flutter app that will add expenses in a list.
Moreover, it’ll have a delete button, so we can delete any of the
item from the list.
Before going to the back-end programming let’s concentrate on the
front-end design part. At least in this segment, we’ll design our app
first.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 253

In the next segment, we’ll build the back-end part. So stay tuned.
Another advantage of the ListView is we can place Column, Row
widgets inside it, as our design needs.
First, we have a model class:

1 import 'package:flutter/foundation.dart';
2
3 class ExpenseList {
4 String id;
5 String title;
6 double amount;
7 DateTime date;
8
9 ExpenseList({
10 @required this.id,
11 @required this.title,
12 @required this.amount,
13 @required this.date,
14 });
15 }

Secondly, we have a stateless widget. Even though it’s stateless at


present, we’ll make it stateful when time comes.
But, as we’ve said, in this segment, we concentrate on the design
part.

Is Flutter front end or backend?


The following code will show you how we can pass across both.
Flutter is both frontend and backend.
We’ve started our app like this:
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 254

1 class ExpenseFirstPage extends StatelessWidget {


2 ExpenseFirstPage({Key key}) : super(key: key);
3
4 ...

Then we add a list of expense. As a starter, we’ve added the expenses


manually.

1 final List<ExpenseList> expenseList = [


2 ExpenseList(
3 id: '1',
4 title: 'Fruits',
5 amount: 200.00,
6 date: DateTime.now(),
7 ),
8 ExpenseList(
9 id: '2',
10 title: 'Vegetables',
11 amount: 200.08,
12 date: DateTime.now(),
13 ),
14 ];

Why we’ve done that? Just to display two items at the very
beginning.
Next, we need the build method, where we use ListView to place
our other visible widgets.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 255

1 @override
2 Widget build(BuildContext context) {
3 return Center(
4 child: ListView(
5 padding: EdgeInsets.all(8),
6 children: [
7 Container(
8 child: Card(
9 child: Text('Chart'),
10 elevation: 10,
11 ),
12 ),
13 Container(
14 child: Column(
15 children: expenseList.map((e) {
16 return Column(
17 crossAxisAlignment: CrossAxisAlignment.start,
18 children: [
19 Row(
20 children: [
21 Container(
22 margin: EdgeInsets.all(8),
23 padding: EdgeInsets.all(8),
24 decoration: BoxDecoration(
25 color: Colors.yellow[100],
26 border: Border.all(
27 color: Colors.red,
28 width: 5,
29 ),
30 ),
31 child: Card(
32 child: Text(
33 '\$${e.amount}',
34 style: TextStyle(
35 fontSize: 20,
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 256

36 fontWeight: FontWeight.bold,
37 ),
38 ),
39 ),
40 ),
41 ],
42 ),
43 ],
44 );
45 }).toList(),
46 ),
47 ),
48 ],
49 ),
50 );
51 }
52 }

Consequently, we’re able to display the design.

Figure 5.5 – Using a ListView to show a List of items

To get the above image, this part plays a vital role.


5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 257

1 Column(
2 children: expenseList.map((e) {
3 return Column(
4 crossAxisAlignment: CrossAxisAlignment.start,
5 children: [
6 Row(
7 children: [
8 Container(
9 margin: EdgeInsets.all(8),
10 padding: EdgeInsets.all(8),
11 decoration: BoxDecoration(
12 color: Colors.yellow[100],
13 border: Border.all(
14 color: Colors.red,
15 width: 5,
16 ),
17 ),
18 child: Card(
19 child: Text(
20 '\$${e.amount}',
21 style: TextStyle(
22 fontSize: 20,
23 fontWeight: FontWeight.bold,
24 ),
25 ),
26 ),
27 ),
28 ],
29 ),
30 ],
31 );
32 }).toList(),
33 ),

Inside the Card widget we place a row of items. In the first row we
keep the amount. To add a dollar sign before the amount we have
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 258

to use string interpolation.


We’ll learn it in a separate segment.

Can we place Column inside Row widget in


Flutter?

Yes, we can do. Furthermore, we can design that as we wish. Next,


our challenge is to place the title and time one after another in a
Column.
Besides, we need to place that Column widget inside the Row.
So it looks like this:

Figure 5.5 – Column widget inside Row widget

As the image shows, we’ve done that successfully.


How we’ve done it?
We don’t have to rewrite the full code. Instead, we can tweak the
main Column section.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 259

1 Column(
2 children: expenseList.map((e) {
3 return Column(
4 crossAxisAlignment: CrossAxisAlignment.start,
5 children: [
6 Card(
7 elevation: 10,
8 child: Row(
9 children: [
10 Container(
11 margin: EdgeInsets.all(8),
12 padding: EdgeInsets.all(8),
13 decoration: BoxDecoration(
14 color: Colors.yellow[100],
15 border: Border.all(
16 color: Colors.red,
17 width: 5,
18 ),
19 ),
20 child: Card(
21 child: Text(
22 '\$${e.amount}',
23 style: TextStyle(
24 fontSize: 20,
25 fontWeight: FontWeight.bold,
26 ),
27 ),
28 ),
29 ),
30 Column(
31 children: [
32 Container(
33 margin: EdgeInsets.all(8),
34 padding: EdgeInsets.all(8),
35 decoration: BoxDecoration(
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 260

36 color: Colors.yellow[100],
37 border: Border.all(
38 color: Colors.red,
39 width: 5,
40 ),
41 ),
42 child: Card(
43 child: Text(
44 '\$${e.amount}',
45 style: TextStyle(
46 fontSize: 20,
47 fontWeight: FontWeight.bold,
48 ),
49 ),
50 ),
51 ),
52 Container(
53 margin: EdgeInsets.all(8),
54 padding: EdgeInsets.all(8),
55 decoration: BoxDecoration(
56 color: Colors.yellow[100],
57 border: Border.all(
58 color: Colors.red,
59 width: 5,
60 ),
61 ),
62 child: Card(
63 child: Text(
64 '\$${e.amount}',
65 style: TextStyle(
66 fontSize: 20,
67 fontWeight: FontWeight.bold,
68 ),
69 ),
70 ),
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 261

71 ),
72 ],
73 ),
74 ],
75 ),
76 ),
77 ],
78 );
79 }).toList(),
80 ),

But, we’re unhappy about one thing. We could have formatted date
in a better way. Although formatting date in flutter is easy.
However, we need to add a package in the flutter dependency.

1 dependencies:
2 flutter:
3 sdk: flutter
4 intl: ^0.17.0
5
6 import 'package:intl/intl.dart';

Once we’ve added the “intl” package, we can import it as mentioned


above.

How do I change the date format in


Flutter?
Of course, changing the date format plays a crucial role. In addition,
the look of the app is important.
So the next image will show you how we had done that.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 262

Figure 5.4 – How do I change the date format in Flutter

Now, it looks much better than before.


Additionally, we’ve reorganized our code. One method will display
the amount. And the other will display the title and date.
To display the amount, we’ve used this Container method:

1 Container displayAmount(ExpenseList e) {
2 return Container(
3 margin: EdgeInsets.all(8),
4 padding: EdgeInsets.all(8),
5 decoration: BoxDecoration(
6 color: Colors.yellow[100],
7 border: Border.all(
8 color: Colors.red,
9 width: 5,
10 ),
11 ),
12 child: Card(
13 child: Text(
14 '\$${e.amount}',
15 style: TextStyle(
16 fontSize: 20,
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 263

17 fontWeight: FontWeight.bold,
18 ),
19 ),
20 ),
21 );
22 }
23 }

The code is quite explicit to show we’ve used various properties of


the Container widget.
On the other hand, we’ve used another Column widget to display
the title and date.

1 Column displayTaskAndDate(ExpenseList e) {
2 return Column(
3 crossAxisAlignment: CrossAxisAlignment.start,
4 children: [
5 Container(
6 margin: EdgeInsets.all(5),
7 padding: EdgeInsets.all(8),
8 decoration: BoxDecoration(
9 color: Colors.blue[100],
10 border: Border.all(
11 color: Colors.red,
12 width: 5,
13 ),
14 ),
15 child: Card(
16 child: Text(
17 '${e.title}',
18 style: TextStyle(
19 fontSize: 25,
20 fontWeight: FontWeight.bold,
21 backgroundColor: Colors.blue[100]),
22 ),
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 264

23 ),
24 ),
25 Container(
26 margin: EdgeInsets.all(2),
27 padding: EdgeInsets.all(5),
28 child: Card(
29 child: Text(
30 DateFormat('yyyy/MM/dd').format(e.date),
31 style: TextStyle(
32 fontSize: 20,
33 fontWeight: FontWeight.normal,
34 fontStyle: FontStyle.italic,
35 ),
36 ),
37 ),
38 ),
39 ],
40 );
41 }

You can understand how we’ve designed this Column widget.


However, one line is very important to grasp the conception of date
formatting.

1 DateFormat('yyyy/MM/dd').format(e.date),

Moreover, there are other ways of using this date format package
also.
Now, we can use these two methods inside the build method:
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 265

1 @override
2 Widget build(BuildContext context) {
3 return Center(
4 child: ListView(
5 padding: EdgeInsets.all(8),
6 children: [
7 Container(
8 child: Card(
9 child: Text('Chart'),
10 elevation: 10,
11 ),
12 ),
13 Container(
14 child: Column(
15 children: expenseList.map((e) {
16 return Column(
17 crossAxisAlignment: CrossAxisAlignment.start,
18 children: [
19 Card(
20 elevation: 10,
21 child: Row(
22 children: [
23 displayAmount(e),
24 displayTaskAndDate(e),
25 ],
26 ),
27 ),
28 ],
29 );
30 }).toList(),
31 ),
32 ),
33 ],
34 ),
35 );
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 266

36 }

For the complete project code please visit the GitHub repository.

How do you add items to a list in


Flutter?
Adding items to a list in Flutter is not difficult. Although we need
to understand one key concept.
The user must have an interface through which she can send the
inputs to the server.
Either we can store the data in a database. Or, we can just display
them as we keep adding them.
Moreover, we can also delete any item based on the unique ID.
Anyway, in this segment we’re going to continue the expense list
flutter app. And we just keep building it.
If you’ve not read the previous two posts, please go through them,
so that you may have an idea what’s going on.
Until now, we have learned how to display a list using a ListView.
However, there was no question of managing state. As we’ve just
displayed a few items.
And, we’ve added them manually. In addition to displaying them,
our main focus was the design part. The frontend of Flutter.
How do you create a dynamic list in Flutter?
We’re going to create a dynamic list, and for that, we need to
maintain state.
Therefore, we have to convert the main thread to stateful widget.
The stateless widget would only work, if we handled the state either
with the provider, or the Riverpod package.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 267

However, this is a small app. Hence, we can manage with the


Flutter’s in-built stateful widget.
Moreover, we’re going to add items to the list dynamically. So we
don’t need the manually added items anymore.
When we open the app, it looks like this:

Figure 5.5 – How to create dynamic list in flutter

Now, we’re going to add Fruits as our title and the amount is 300.00.
To input the data, we need two text fields first. Next, we need a
button to submit that data, so it gets added to the list.
At the same time, we need to add another functionality. We want
to delete any item from the list as well.
After submitting the data the list gets updated like this:
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 268

Figure 5.6 – Adding items to a list in flutter

How do I use ListView in Flutter?


As we can see in the above image, our app does look perfect. We
can see the soft keys, as well.
Because, our main widget uses ListView, it doesn’t break. We can
scroll the app endlessly.
We’ve started coding the app just like the following:

1 import 'package:flutter/material.dart';
2 import '../models/expense_list.dart';
3 import '../controllers/display_task_and_data.dart';
4 import '../controllers/display_amount.dart';
5
6 class ExpenseFirstPage extends StatefulWidget {
7 ExpenseFirstPage({Key key}) : super(key: key);
8
9 @override
10 _ExpenseFirstPageState createState() => _ExpenseFirstPage\
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 269

11 State();
12 }
13
14 class _ExpenseFirstPageState extends State<ExpenseFirstPa\
15 ge> {
16
17 ...
18
19 the code is incomplete...

Next, we need to declare the type of the List. In addition, we’ve


created a List class in our model folder.
By the way, for the full code snippet, please visit my GitHub
repository.
We also need two methods - one for adding the items and another
for deleting a particular item.

1 final List<ExpenseList> expenseList = [];


2
3 void addTaskAndAmount(String title, double amount) {
4 final expense = ExpenseList(
5 id: DateTime.now().toString(),
6 title: title,
7 amount: amount,
8 date: DateTime.now(),
9 );
10 setState(() {
11 expenseList.add(expense);
12 });
13 }
14
15 void deleteExpenseList(String id) {
16 setState(() {
17 expenseList.removeWhere((element) => element.id == id);
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 270

18 });
19 }

For adding the items to the expense list app, we pass two parameters.
One is a string data type and the other is a double data type.
Next we need two text editing controller that flutter supplies.

1 final titleController = TextEditingController();


2 final amountController = TextEditingController();

How do you add items to the List?

We’re going to use these text editing controllers inside the Text field
widget. And every action takes place inside the build method.

1 override
2 Widget build(BuildContext context) {
3 return Center(
4 child: ListView(
5 padding: EdgeInsets.all(8),
6 children: [
7 Container(
8 child: Card(
9 child: Text('Chart'),
10 elevation: 10,
11 ),
12 ),
13 Container(
14 child: Card(
15 elevation: 10,
16 child: Column(
17 children: [
18 TextField(
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 271

19 controller: titleController,
20 ),
21 TextField(
22 controller: amountController,
23 ),
24 TextButton(
25 onPressed: () {
26 addTaskAndAmount(
27 titleController.text,
28 double.parse(amountController.text),
29 );
30 },
31 child: Text(
32 'SUBMIT',
33 style: TextStyle(
34 fontSize: 25,
35 fontWeight: FontWeight.bold,
36 ),
37 ),
38 ),
39 ],
40 ),
41 ),
42 ),
43 Container(
44 child: Column(
45 children: expenseList.map((e) {
46 return Column(
47 crossAxisAlignment: CrossAxisAlignment.center,
48 children: [
49 Card(
50 elevation: 10,
51 child: Row(
52 children: [
53 displayAmount(e),
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 272

54 displayTaskAndDate(e),
55 ],
56 ),
57 ),
58 TextButton(
59 onPressed: () {
60 deleteExpenseList(e.id);
61 },
62 child: Text(
63 'DELETE',
64 style: TextStyle(
65 fontWeight: FontWeight.bold,
66 fontSize: 25,
67 ),
68 ),
69 ),
70 ],
71 );
72 }).toList(),
73 ),
74 ),
75 ],
76 ),
77 );
78 }
79 }

Not only we’re going to add items to the list, but at the same
time, we’re going to display them. Furthermore, we should keep
a DELETE button with each item, as well.
Granted, we could have used Icon of the deletion. But, instead
we’ve chosen the Text Button widget.
For managing state and handling the two functions, these two code
snippets are important.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 273

1 TextField(
2 controller: titleController,
3 ),
4 TextField(
5 controller: amountController,
6 ),
7 TextButton(
8 onPressed: () {
9 addTaskAndAmount(
10 titleController.text,
11 double.parse(amountController.text),
12 );
13 },
14 child: Text(
15 'SUBMIT',
16 style: TextStyle(
17 fontSize: 25,
18 fontWeight: FontWeight.bold,
19 ),
20 ),
21 ),
22
23 ....
24
25 TextButton(
26 onPressed: () {
27 deleteExpenseList(e.id);
28 },
29 child: Text(
30 'DELETE',
31 style: TextStyle(
32 fontWeight: FontWeight.bold,
33 fontSize: 25,
34 ),
35 ),
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 274

36 ),

The above section is responsible for adding items to the list. So it


looks like this after adding two items.

Figure 5.7 – Dynamically adding items to the List flutter

However, at the same time, we want to check whether our delete


buttons are working as well.
The second part of the above code snippet points to that.
Therefore, if want to delete the first item, we can now easily do
that.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 275

Figure 5.8 – Deleting any item of the list in flutter

We’ve successfully deleted the first item. Moreover, now we can


again add items to the same list.

How to manage null-safety in Flutter


The Dart language now supports sound null safety.
What does that mean?
It means, variables cannot contain null, unless you say so. There-
fore, by-default, in Dart types are non-nullable.
What is the benefit of it?
Of course, the biggest benefit is compiler optimization.

1 // In null-safe Dart, none of these can ever be null.


2 var i = 12; // Inferred to be an int always.
3 String name = fetchFileName();
4 final aClass = AClass();

What happens if we want that a variable might have a null value?


5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 276

Well, null-safe Dart takes care of that also.

1 int? aNullableVariable = null;

Adding a question mark to the type declaration solves that problem.


However, in Flutter, the following line of code in pubspec.yaml file
matters a lot.

1 version: 1.0.0+1
2
3 environment:
4 sdk: ">=2.12.0 <3.0.0"

Moreover, it tells us to upgrade Flutter. And, at the same time, the


flutter sdk should be greater than 2.12.0.
So that if we run a flutter app this message displays on your VS
Code terminal.

What is null safety in Flutter

In addition, on the lower left corner of the image, the null-safe


message appears.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 277

Figure 5.9 – Null safety in Flutter

Since a flutter app should be multi-screens, or multi-pages; there-


fore you need to structure your project in a planned way. A good
structure points to code organizations.

How to structure your flutter app?


Let us think about a simple exercise tracking app.
As a matter of fact, this app presumes 30 minutes of walking burns
300 grams of calories, and 30 minutes of running burns 500 grams
of calories.
Although it’s not accurate, yet just for a test case, we presume this
for our simple app.
Now comes both the frontend and backend part of coding.
The frontend deals mainly with designs. On the contrary, in the
backend we validate user’s inputs.
The first part starts with opening a view page that takes two inputs.
And, you enter either walking, or running. In the minute field as
you type 30, it calculates the amount of calorie burning.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 278

Meanwhile, the app will do some validation. If you type something


else instead of Walking or Running, the amount of calorie burn will
be 0.
Although it’s a very small app, still it’s lifting a lot of frontend and
backend workload. Furthermore, it also manages state efficiently.

How do I program my Flutter App?


Programming a flutter app is neither difficult, nor it’s a rocket
science. However, there are hundreds of widgets that follows a lot
of rules and regulations.
Unless we understand them, we cannot program our flutter app in
a planned, structured way.
To start with we must have the main method.

1 import 'package:flutter/material.dart';
2
3 import 'views/fit_fat_app.dart';
4
5 void main() {
6 runApp(FitFatApp());
7 }

As we’re following the MVC structure, we have already created


three folders inside “lib”. The models folder store data, or the
business logic. The views folder store different pages, or screens.
And, finally, the controllers folder has the widgets that maintain a
bridge between models and views.
Next, we need the Material App design widget that will import the
app’s first page.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 279

1 import 'package:flutter/material.dart';
2
3 import 'fit_fat_first_page.dart';
4
5 class FitFatApp extends StatelessWidget {
6 // This widget is the root of your application.
7 @override
8 Widget build(BuildContext context) {
9 return MaterialApp(
10 title: 'Flutter Demo',
11 theme: ThemeData(
12 primarySwatch: Colors.blue,
13 ),
14 debugShowCheckedModeBanner: false,
15 home: FitFatFirstPage(),
16 );
17 }
18 }

The first page points to the second page. So, now we need that page,
too.

1 import 'package:flutter/material.dart';
2 import '../controllers/fit_fat_text.dart';
3 import '../controllers/display_fit_fat.dart';
4
5 class FitFatSecondPage extends StatelessWidget {
6 @override
7 Widget build(BuildContext context) {
8 return Container(
9 child: ListView(
10 children: [
11 FitFatText(),
12 DisplayFitFat(),
13 ],
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 280

14 ),
15 );
16 }
17 }

As we can see, the view part is finished as the second page imports
two controller widgets.
Consequently, one of the controller widget is stateful, because we
take user inputs, and holds that in memory temporarily. Moreover
that change should reflect on the screen like this:

Figure 5.10 – How to structure your flutter app

What is the best architecture for


Flutter?
Because it differs from developer to developer, we don’t debate the
issue here. We’re following MVC structure, and it works well.
The first controller widget is a static one, although it designs the
Container widget.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 281

1 import 'package:flutter/material.dart';
2
3 class FitFatText extends StatelessWidget {
4 const FitFatText({
5 Key? key,
6 }) : super(key: key);
7
8 @override
9 Widget build(BuildContext context) {
10 return Container(
11 margin: EdgeInsets.all(10),
12 padding: EdgeInsets.all(5),
13 decoration: BoxDecoration(
14 shape: BoxShape.rectangle,
15 color: Colors.yellow[100],
16 border: Border.all(
17 color: Colors.blueAccent,
18 width: 2,
19 ),
20 ),
21 child: Text(
22 'Input time and type of exercise and find how many calori\
23 es you burn',
24 textAlign: TextAlign.center,
25 style: TextStyle(
26 fontWeight: FontWeight.normal,
27 fontSize: 20,
28 ),
29 ),
30 );
31 }
32 }

The second controller widget plays the main role in our app. Not
only it takes user’s inputs, processes the data with the help of our
models data class, but it also calculates the calorie, validating the
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 282

text fields, etc.

1 import 'package:flutter/material.dart';
2 import '../models/fit_fat.dart';
3 import 'type_of_exercise.dart';
4 import 'minute_and_calorie.dart';
5
6 class DisplayFitFat extends StatefulWidget {
7 DisplayFitFat({Key? key}) : super(key: key);
8
9 @override
10 _DisplayFitFatState createState() => _DisplayFitFatState(\
11 );
12 }
13
14 class _DisplayFitFatState extends State<DisplayFitFat> {
15 final List<FitFat> fitFat = [];
16 final exerciseType = TextEditingController();
17 final timeTaken = TextEditingController();
18
19 double burnCalory(String exerciseType, double timeTaken) {
20 double calory;
21 if (exerciseType == 'Running' && timeTaken == 30) {
22 calory = 500;
23 return calory;
24 } else if (exerciseType == 'Walking' && timeTaken == 30) {
25 calory = 300;
26 return calory;
27 } else {
28 calory = 0;
29 return calory;
30 }
31 }
32
33 void takeInput(String exerciseType, double timeTaken) {
34 final exercise = FitFat(
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 283

35 id: DateTime.now().toString(),
36 typeOfExercise: exerciseType,
37 minutes: timeTaken,
38 calory: burnCalory(exerciseType, timeTaken),
39 );
40 setState(() {
41 fitFat.add(exercise);
42 });
43 }
44
45 @override
46 Widget build(BuildContext context) {
47 return Column(
48 children: [
49 Container(
50 child: Card(
51 elevation: 10,
52 child: Container(
53 margin: EdgeInsets.all(10),
54 padding: EdgeInsets.all(5),
55 decoration: BoxDecoration(
56 shape: BoxShape.rectangle,
57 color: Colors.blue[50],
58 border: Border.all(
59 color: Colors.blueAccent,
60 width: 2,
61 ),
62 ),
63 child: Column(
64 children: [
65 TextField(
66 decoration: InputDecoration(
67 border: OutlineInputBorder(),
68 hintText: 'Type Walking or Running',
69 ),
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 284

70 controller: exerciseType,
71 ),
72 TextField(
73 decoration: InputDecoration(
74 border: OutlineInputBorder(),
75 hintText: 'Type 30 to get the exact calory-burn',
76 ),
77 controller: timeTaken,
78 ),
79 TextButton(
80 onPressed: () {
81 takeInput(
82 exerciseType.text,
83 double.parse(timeTaken.text),
84 );
85 },
86 child: Text(
87 'SUBMIT',
88 style: TextStyle(
89 fontSize: 20,
90 fontWeight: FontWeight.bold,
91 ),
92 ),
93 ),
94 ],
95 ),
96 ),
97 ),
98 ),
99 Container(
100 child: Column(
101 children: fitFat.map((e) {
102 return Card(
103 elevation: 15,
104 child: Column(
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 285

105 children: [
106 TypeOfExercise(
107 f: e,
108 ),
109 MinuteAndCalorie(
110 f: e,
111 ),
112 ],
113 ),
114 );
115 }).toList(),
116 ),
117 ),
118 ],
119 );
120 }
121 }

After processing the user’s data and validating it, it also manages
to display the data on the screen. With reference to that, we’ve seen
the image above.
However, if we crammed code of two more displaying widgets
inside, it would look like a spaghetti code.
Flutter always encourages to break your code into small segments.
Therefor, we’ve imported two more widgets.
Watch these lines:
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 286

1 import 'type_of_exercise.dart';
2 import 'minute_and_calorie.dart';
3
4 child: Column(
5 children: fitFat.map((e) {
6 return Card(
7 elevation: 15,
8 child: Column(
9 children: [
10 TypeOfExercise(
11 f: e,
12 ),
13 MinuteAndCalorie(
14 f: e,
15 ),
16 ],
17 ),
18 );
19 }).toList(),
20 ),

So we need two more controller widgets one after another. As a


result we can see the changed data on our screen.

1 import 'package:flutter/material.dart';
2 import '../models/fit_fat.dart';
3
4 class TypeOfExercise extends StatelessWidget {
5 const TypeOfExercise({
6 Key? key,
7 required this.f,
8 }) : super(key: key);
9
10 final FitFat f;
11
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 287

12 @override
13 Widget build(BuildContext context) {
14 return Container(
15 margin: EdgeInsets.all(10),
16 padding: EdgeInsets.all(5),
17 decoration: BoxDecoration(
18 shape: BoxShape.rectangle,
19 color: Colors.green[100],
20 border: Border.all(
21 color: Colors.redAccent,
22 width: 2,
23 ),
24 ),
25 child: Text(
26 f.typeOfExercise,
27 style: TextStyle(
28 fontWeight: FontWeight.bold,
29 fontSize: 25,
30 ),
31 ),
32 );
33 }
34 }

As a matter of fact, we need to maintain the design. Moreover, we


should be careful about not breaking the design.

Role of widgets in Flutter


Basically, these two widgets maintain the design and reflect the
changed user’s inputs on the screen.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 288

1 import 'package:flutter/material.dart';
2 import '../models/fit_fat.dart';
3
4 class MinuteAndCalorie extends StatelessWidget {
5 const MinuteAndCalorie({
6 Key? key,
7 required this.f,
8 }) : super(key: key);
9
10 final FitFat f;
11
12 @override
13 Widget build(BuildContext context) {
14 return Row(
15 children: [
16 Container(
17 margin: EdgeInsets.all(5),
18 padding: EdgeInsets.all(5),
19 decoration: BoxDecoration(
20 shape: BoxShape.rectangle,
21 color: Colors.yellow[100],
22 border: Border.all(
23 color: Colors.blueAccent,
24 width: 2,
25 ),
26 ),
27 child: Text(
28 'Minutes: ${f.minutes}',
29 style: TextStyle(
30 fontWeight: FontWeight.bold,
31 fontSize: 20,
32 ),
33 ),
34 ),
35 Container(
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 289

36 margin: EdgeInsets.all(5),
37 padding: EdgeInsets.all(5),
38 decoration: BoxDecoration(
39 shape: BoxShape.rectangle,
40 color: Colors.yellow[100],
41 border: Border.all(
42 color: Colors.blueAccent,
43 width: 2,
44 ),
45 ),
46 child: Text(
47 'Calory Burned: ${f.calory}',
48 style: TextStyle(
49 fontWeight: FontWeight.bold,
50 fontSize: 20,
51 ),
52 ),
53 ),
54 ],
55 );
56 }
57 }

Still we need a final touch to finish showing our code snippets. The
models folder store the data class that create new instances of user’s
inputs each time we press the submit button.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 290

1 class FitFat {
2 String id;
3 String typeOfExercise;
4 double minutes;
5 double calory;
6
7 FitFat({
8 required this.id,
9 required this.typeOfExercise,
10 required this.minutes,
11 required this.calory,
12 });
13 }

Otherwise, our app would remain unfinished.


Although we’ve added some validation in the user’s inputs section
and our app’s working well, still we’re not happy with the look.
When a user opens the flutter app she must see a blank page and
two icons of add buttons.
Once she clicks any one of the add icons, a fresh page with two text
fields will appear.
So, our app now looks like this, at the very beginning.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 291

Figure 5.11 – Restructuring Flutter App

How do I improve my flutter app


performance?
To make our flutter app more performant, we want to take some
definite steps. For instance, we should break our code in small
widgets and pass parameters through their constructors.
Although this part is not difficult, yet it requires a good grasp on
object oriented programming. As Dart is out-and-out an object
oriented programming language, you have plenty of scope to break
your code in small modules.
Now, we want a blank page at the beginning, then we want to
navigate from first page to the second page as usual.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 292

1 import 'package:flutter/material.dart';
2
3 class FitFatBlankPage extends StatelessWidget {
4 const FitFatBlankPage({Key? key}) : super(key: key);
5
6 @override
7 Widget build(BuildContext context) {
8 return Column(
9 children: [],
10 );
11 }
12 }

At the same time, to navigate from Appbar widget we need two


separate Scaffold widget in those two pages. Otherwise, we couldn’t
have used the Floating Action buttons, also.
Hence, the first page code changes to this:

1 import 'package:flutter/material.dart';
2 import 'fit_fat_second_page.dart';
3
4 import 'fit_fat_blank_page.dart';
5
6 class FitFatFirstPage extends StatelessWidget {
7 @override
8 Widget build(BuildContext context) {
9 return Scaffold(
10 appBar: AppBar(
11 title: Text('Stay Fit and Burn Fat'),
12 actions: [
13 IconButton(
14 onPressed: () {
15 Navigator.push(
16 context,
17 MaterialPageRoute(
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 293

18 builder: (context) => FitFatSecondPage(),


19 ),
20 );
21 },
22 icon: Icon(Icons.add),
23 ),
24 ],
25 ),
26 body: FitFatBlankPage(),
27 floatingActionButton: FloatingActionButton(
28 onPressed: () {
29 Navigator.push(
30 context,
31 MaterialPageRoute(
32 builder: (context) => FitFatSecondPage(),
33 ),
34 );
35 },
36 child: Icon(Icons.add),
37 ),
38 );
39 }
40 }

To make the second page Appbar look different, we have to work


on that part, too.
As a result, when we press any one of the add icons, it takes us to
the second page. Consequently, the Appbar text shows that it’s a
different page.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 294

Figure 5.12 – Navigating from Appbar and floating action button

As we can clearly see that, our app is working as we had planned


earlier. We’ve successfully restructured our flutter app and navi-
gated from one page to the other.
However, we need to deal with the navigation properly.

How do you deal with navigation in


Flutter?
As there are more than one options, we need to choose any one.
And we’ve chosen Material page route. So in the second page, we
have an option to go back to the previous page on the Appbar.
Meanwhile we have also changed the Appbar section of the second
page.
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 295

1 import 'package:flutter/material.dart';
2 import '../controllers/fit_fat_text.dart';
3 import '../controllers/display_fit_fat.dart';
4
5 class FitFatSecondPage extends StatelessWidget {
6 @override
7 Widget build(BuildContext context) {
8 return Scaffold(
9 appBar: AppBar(
10 title: Text('Start Building Exercise Chart'),
11 actions: [
12 IconButton(
13 onPressed: () {
14 Navigator.push(
15 context,
16 MaterialPageRoute(
17 builder: (context) => FitFatSecondPage(),
18 ),
19 );
20 },
21 icon: Icon(Icons.add),
22 ),
23 ],
24 ),
25 body: Container(
26 child: ListView(
27 children: [
28 FitFatText(),
29 DisplayFitFat(),
30 ],
31 ),
32 ),
33 );
34 }
35 }
5. How to handle collections of items, all about List and Map and Code Structure
in Flutter 296

While we’ve changed the Appbar text of the second page, we should
have changed the icon button also. So that either, we can go back
to the previous page, or move forward to the third page.
Of course, you can now do it yourself. Try it. We’ll definitely discuss
navigation and routing in a separate chapter.
For more Flutter related Articles and Resources²⁵
²⁵https://sanjibsinha.com
6. Everything about
Flutter Navigation and
Route
In this chapter we’ll learn how we can navigate from one screen or
page to another screen. How we can pass data from one screen to
another.
We’ll learn about Route and Navigator widgets. What are there
functions and how Flutter provides many features that we can use
to handle navigation.
The full code snippet is available in the respective GitHub reposi-
tory.
The full code repository for this chapter²⁶
Moreover, for updated flutter tutorials don’t forget to visit:
Updated Flutter Tutorials²⁷

Why do you use onGenerateRoute in


flutter?
Why do we need onGenerateRoute navigation in Flutter?
In some cases, we want the user to log in to enter the app. Otherwise
the new user can also register on that same page.
As a result, on opening the app, the route takes the user to a certain
page.
²⁶https://github.com/sanjibsinha/better_flutter_chapter_seven
²⁷https://flutter.sanjibsinha.com
6. Everything about Flutter Navigation and Route 298

Good news is, Flutter has a built-in feature for achieving such a feat.
Moreover, we can control the navigation at the very beginning of
our Flutter app.
To do this, in flutter MaterialApp widget, we use onGenerateRoute
property.
Material design and material components play a key role in build-
ing a Flutter app. In addition, the material design system unites
style, branding, interaction and motion using a set of material
components.
And these material components work under material design’s
principles. That makes the usage of onGenerateRoute property in
flutter.

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.
6. Everything about Flutter Navigation and Route 299

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 setting\
19 s) {
20 if (settings.name != '/second') {
21 return null;
22 }
23
24 return MaterialPageRoute<void>(
25 settings: settings,
26 builder: (BuildContext context) => MDSecondPage(),
27 fullscreenDialog: true,
28 );
6. Everything about Flutter Navigation and Route 300

29 }
30 }

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?
6. Everything about Flutter Navigation and Route 301

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.
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:
6. Everything about Flutter Navigation and Route 302

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.

How Does Flutter Navigator work?

Therefore, our first page must have that mechanism.


6. Everything about Flutter Navigation and Route 303

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\
15 SecondPage()),
16 );
17 },
18 child: Container(
19 margin: const EdgeInsets.all(10.0),
20 padding: const EdgeInsets.all(10.0),
21 child: ClipRRect(
22 borderRadius: const BorderRadius.only(
23 topLeft: Radius.circular(15.0),
24 topRight: Radius.circular(15.0),
25 bottomLeft: Radius.circular(15.0),
26 bottomRight: Radius.circular(15.0),
27 ),
28 child: Image.network(
29 'https://sanjibsinha.com/wp-content/uploa\
30 ds/2021/07/Can-you-code-in-WordPress-How-do-I-learn-WordP\
31 ress-coding-.jpg'),
32 ),
33 ),
34 ),
35 );
6. Everything about Flutter Navigation and Route 304

36 }
37 }

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


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

How Does Flutter Navigator push work?

Let us take a look at the first page.


6. Everything about Flutter Navigation and Route 305

Figure 6.1 – Navigating to the first page


6. Everything about Flutter Navigation and Route 306

As we can use the Gesture Detector, we can now tap the image. And
that takes us to the second page.
6. Everything about Flutter Navigation and Route 307

Figure 6.2 – Navigating to the second page


6. Everything about Flutter Navigation and Route 308

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/uploa\
23 ds/2021/06/What-is-toList-flutter-What-is-map-in-Dart-.jp\
24 g'),
25 ),
26 ),
27 ),
28 );
29 }
30 }
6. Everything about Flutter Navigation and Route 309

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 looks like the following image.
6. Everything about Flutter Navigation and Route 310

Figure 6.3 – All the categories at a first glance

The above image displays categories of different type of news. If we


6. Everything about Flutter Navigation and Route 311

click any one of the category, it will take us to the detail page.

Figure 6.4 – The News detail page

To achieve this, in model folder we have category class.


6. Everything about Flutter Navigation and Route 312

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 \
15 data like the following code.
16
17 import 'package:flutter/material.dart';
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,
6. Everything about Flutter Navigation and Route 313

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,
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,
6. Everything about Flutter Navigation and Route 314

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 like the following image.
6. Everything about Flutter Navigation and Route 315

Figure 6.5 – Another View of News 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.
6. Everything about Flutter Navigation and Route 316

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 SliverGridDelegateWithMaxCros\
3 sAxisExtent(
4 maxCrossAxisExtent: 200,
5 crossAxisSpacing: 20.0,
6 mainAxisSpacing: 20.0,
7 ),
8 children: DUMMY_CATEGORIES.map(
9 (e) {
6. Everything about Flutter Navigation and Route 317

10 return AllCategories(
11 id: e.id,
12 title: e.title,
13 color: e.color,
14 );
15 },
16 ).toList(),
17 ),
18
19 // code is incomplete for brevity, please consult the Git\
20 Hub repository

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.
6. Everything about Flutter Navigation and Route 318

1 Widget build(BuildContext context) {


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

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.
6. Everything about Flutter Navigation and Route 319

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.

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.
And we keep the data part in our model folder.
6. Everything about Flutter Navigation and Route 320

Figure 6.6 – Category detail screen in a new design

The above image displays the categories. If we click any category,


we can view the detail page as we see in the following image.
6. Everything about Flutter Navigation and Route 321

Figure 6.7 – News items belong to a certain category

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
6. Everything about Flutter Navigation and Route 322

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.
6. Everything about Flutter Navigation and Route 323

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 \
27 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
6. Everything about Flutter Navigation and Route 324

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.

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.
6. Everything about Flutter Navigation and Route 325

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 looks like the following image.
6. Everything about Flutter Navigation and Route 326

Figure 6.8 – The new look of the News detail page

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
6. Everything about Flutter Navigation and Route 327

theme while creating the Material App.

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

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.
6. Everything about Flutter Navigation and Route 328

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.
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 Cate\
5 goryNewsScreen(),
6 NewsDetailScreen.routeName: (ctx) => const NewsDe\
7 tailScreen(),
8 },

In case, our route name doesn’t work, we can also use a fallback.
6. Everything about Flutter Navigation and Route 329

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.

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\
10 <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(
6. Everything about Flutter Navigation and Route 330

17 appBar: AppBar(
18 title: Text(categoryTitle!),
19 ),
20
21 // 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: [
15 'c1',
16 'c5',
17 ],
18 title: 'Take more outdoor walks',
19
20 // code is incomplete
6. Everything about Flutter Navigation and Route 331

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.
6. Everything about Flutter Navigation and Route 332

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 repos-
itory.

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

In the News App stateless widget we define our Material App


widget where we use the route widget.
6. Everything about Flutter Navigation and Route 333

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,
11 canvasColor: const Color.fromRGBO(255, 254, 229, \
12 1),
13 textTheme: ThemeData.light().textTheme.copyWith(
14 bodyText2: const TextStyle(
15 color: Color.fromRGBO(20, 51, 51, 1),
16 ),
17 bodyText1: const TextStyle(
18 color: Color.fromRGBO(20, 51, 51, 1),
19 ),
20 headline6: const TextStyle(
21 fontSize: 20,
22 fontWeight: FontWeight.bold,
23 )),
24 ),
25 // home: CategoriesScreen(),
26 initialRoute: '/', // default is '/'
27 routes: {
28 '/': (ctx) => const CategoriesScreen(),
29 CategoryNewsScreen.routeName: (ctx) => const Cate\
30 goryNewsScreen(),
31 NewsDetailScreen.routeName: (ctx) => const NewsDe\
32 tailScreen(),
33 },
34
35 onUnknownRoute: (settings) {
6. Everything about Flutter Navigation and Route 334

36 return MaterialPageRoute(
37 builder: (ctx) => const CategoriesScreen(),
38 );
39 },
40 );
41 }
42 }

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.

1 body: GridView(
2 padding: const EdgeInsets.all(25),
3
4 /// the first page displays CategoryItem controll\
5 er
6 children: dummyCategories
7 .map(
8 (catData) => CategoryItem(
9 id: catData.id,
10 title: catData.title,
11 color: catData.color,
12 ),
13 )
14 .toList(),
15
6. Everything about Flutter Navigation and Route 335

16 ...
17 // 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.

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);
6. Everything about Flutter Navigation and Route 336

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.

1 Widget build(BuildContext context) {


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

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. More-
over, 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.
6. Everything about Flutter Navigation and Route 338

Next, we click any category and reach the News Item that belongs
to that Category.
Now we wan to see the News detail page.

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\
11 <String, String>;
12 final categoryTitle = routeArgs['title'];
13 final categoryId = routeArgs['id'];
14 final categoryNews = dummyNews.where((news) {
15 return news.categories.contains(categoryId);
16 }).toList();
17 return Scaffold(
18 appBar: AppBar(
6. Everything about Flutter Navigation and Route 339

19 title: Text(categoryTitle!),
20 ),
21 body: ListView.builder(
22 itemBuilder: (ctx, index) {
23 return NewsItem(
24 id: categoryNews[index].id,
25 title: categoryNews[index].title,
26 imageUrl: categoryNews[index].imageURL,
27 nature: categoryNews[index].nature,
28 );
29 },
30 itemCount: categoryNews.length,
31 ),
32 );
33 }
34 }

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,
6. Everything about Flutter Navigation and Route 340

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 )
38 ],
39 ),
40 Padding(
41 padding: const EdgeInsets.all(20),
42 child: Row(
43 mainAxisAlignment: MainAxisAlignment.spac\
44 eAround,
45 children: <Widget>[
46 Row(
6. Everything about Flutter Navigation and Route 341

47 children: <Widget>[
48 const Icon(
49 Icons.work,
50 ),
51 const SizedBox(
52 width: 6,
53 ),
54 Text(natureText),
55 ],
56 ),
57 ],
58 ),
59 ),
60 ],
61 ),

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.
6. Everything about Flutter Navigation and Route 342

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.
6. Everything about Flutter Navigation and Route 343

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 tex\
7 t) {
8 return Container(
9 margin: const EdgeInsets.symmetric(vertical: 10),
10 child: Text(
11 text,
12 style: Theme.of(context).textTheme.headline6,
13 ),
14 );
15 }
16
17 Widget buildContainer(Widget child) {
18 return Container(
19 decoration: BoxDecoration(
20 color: Colors.white,
21 border: Border.all(color: Colors.grey),
22 borderRadius: BorderRadius.circular(10),
23 ),
24 margin: const EdgeInsets.all(10),
25 padding: const EdgeInsets.all(10),
26 height: 150,
27 width: 300,
28 child: child,
29 );
30 }
31
32 @override
33 Widget build(BuildContext context) {
34 final mealId = ModalRoute.of(context)!.settings.argum\
35 ents as String;
6. Everything about Flutter Navigation and Route 344

36 final selectedNews = dummyNews.firstWhere((meal) => m\


37 eal.id == mealId);
38 return Scaffold(
39 appBar: AppBar(
40 title: Text(selectedNews.title),
41 ),
42 body: SingleChildScrollView(
43 child: Column(
44 children: <Widget>[
45 SizedBox(
46 height: 300,
47 width: double.infinity,
48 child: Image.network(
49 selectedNews.imageURL,
50 fit: BoxFit.cover,
51 ),
52 ),
53 buildSectionTitle(context, 'News Detail'),
54 Text(selectedNews.detail),
55 ],
56 ),
57 ),
58 );
59 }
60 }

What Next?
If you find these information useful, I am happy. For any Flutter
related query, you may send an email to [email protected].
And at the same time don’t forget to visit the website where I write
regularly on Flutter only.
6. Everything about Flutter Navigation and Route 345

You’ll get the updated Flutter tips and tricks. So how about take the
trips? :)
Dedicated to updated Flutter articles²⁸

²⁸https://flutter.sanjibsinha.com

You might also like