Nothing Special   »   [go: up one dir, main page]

Flutter Notes

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 56
At a glance
Powered by AI
Flutter is a cross-platform SDK that allows building native mobile apps using one codebase. It provides better control and performance compared to alternatives like React Native since it does not convert code to platform primitives.

The main prerequisites for installing Flutter are the Flutter SDK, Android SDK, and an IDE like VS Code. The Android SDK can be installed automatically via Android Studio or manually via command line tools.

Multiple provider instances can be attached to a single class using the MultipleProviders widget, which takes a Providers parameter containing a list of provider instances.

FLUTTER

Flutter is a SDK that allows to build native cross platform (iOS, android) apps with one
programming language and codebase.

Flutter architecture
All information about the architecture of flutter is given here -
https://flutter.dev/docs/resources/technical-overview

Flutter Does NOT use platform primitives (Advantage over React native or any other cross
platform alternative)
This means actually that unlike react native (in which code is compiled into native
equivalents or native alternative. Like to render a button, react native code may be
converted to the native code like UIButton for iOS or widget. Button for android), flutter
provides its custom implementation by providing its own engine which controls every
pixel drawn on the screen thereby fully controling the whole screen giving flutter –
a. Better Control – because now we don’t have to worry about limitations while
converting into native code. We have access to the whole screen and thus we can
create more freely with less limitations.
b. Lot better Performance – (search more on this)

Installing Flutter
To install flutter is easy. Just follow the doc here - https://flutter.dev/docs/get-
started/install/windows. There are 4 main pre-requisites (as specified by flutter doctor) for
flutter –
a. Flutter - This includes following three things (all of which are given in doc)–
i. Installing git as pre-requisite
ii. Then downloading flutter SDK
iii. Then setting the flutter path for using flutter from cmd
b. Android SDK – This was the main sticking point where you wasted time. There are 2
methods of doing this –
I. Automatically Via Android Studio (preferred)
You did not want to use android studio (because compared to VS code it
takes up lot of resources and thus slows PC down and also uses 1.5 GB extra
storage for the IDE) but it is the best way because it is very easy & time
saving due to allowance to manage SDK and AVD (for creating virtual
device) through GUI.
II. Manually Via Android SDK command line tools
This can be downloaded from the site itself. But the problem is that
everything has to be done through command line (older versions provided an
application(.exe) to manage SDK and AVD but it is now obsolete and in latest
version of SDK tools there was no .exe files, to force you to install android
studio, and thus everything had to be done using cmd) of which you have no
experience and internet lacks resources for it and thus will take a lot of time
just to install the SDK. But to install SDK in this way, go through the below
links –
Google search - installing android sdk without android studio
https://www.maketecheasier.com/install-android-sdk-in-windows/

III. Semi-Automated via Android SDK command line tools with


SDKMangaer.exe and AVD manager.exe
This is the best option if you don’t want to install android studio because its
GUI based application - SDKManger.exe which allows you to install SDK
packages Through GUI. But it was giving problems in licenses because the
SDK tools was obsolete (25.0.2) and you were not able to update it further.
But there is a work around given in below links to update the tools to higher
version –

Google search - installing android sdk without android studio


https://stackoverflow.com/questions/43685301/how-to-install-the-gui-android-sdk-
manager-without-installing-android-studio/51429889#51429889 (Workaround to
provide)
https://www.andreszsogon.com/using-android-8-and-9-emulators-without-android-
studio/
https://stackoverflow.com/questions/37505709/how-do-i-download-the-android-
sdk-without-downloading-android-studio
https://www.androidcentral.com/installing-android-sdk-windows-mac-and-linux-
tutorial

c. IDE – You need to install VS Code or Android studio to code on dart. You will use VS
Code because it is much lighter and less resource intensive than Android Studio. So
install VS code and install flutter and dart extensions in it.

d. Device – This the device on which the app will run. There are 2 options –
I. External Device – use your phone by enabling USB Debugging. This is a
marginally better because it will not take up the resources of your PC which
the emulator will take (but in New PC it won’t make much of a difference).
II. Emulator – For this, you need to create new device from AVD Manager in
Android studio. Make a new custom device (don’t choose from prebuild
devices like pixel as they are more resource intensive and you can’t change
many of their advanced settings) so that you can create a light device using
less resources of your PC and this also allows to use GPU acceleration
resulting in a better performance.
Creating & Running Flutter programs
To follow these steps, you need to run 2 CMD commands as given below –
a. To create just go to the directory where you want store the app and run command –
flutter create <app_name>
b. To Run navigate to the app directory and run command –
flutter run

Project Structure in a default Flutter App


This topic explains the files and folders present in any flutter default app –
a. .idea folder – this is a folder containing files for android studio. When working with
VS code no need to do anything with it.
b. Android folder – This is a normal android project with your flutter complied code.
This contains the whole android project into which the flutter code will be kind of
merged into (how? Search more) and then this android project (after merger) will be
built into the android device.
c. Build folder – This holds the output of your flutter app. This is generated and
managed by flutter SDK.
d. iOS folder – This holds the ios project which will be built into iPhone device.
e. Lib folder – lib stands for library and the main folder where all dart files will be coded
and thus 99 % of work will be in this folder.
f. Test folder – The folder for creating automated test code files.
g. .gitignore file – to work with GitHub
h. .metadata & .packages files – generated by SDK to store some info needed to build
the app (.metadata file) and to store some internal dependencies for the project to
work (.packages file).
i. pubspec.yaml & pubspec.lock - pubspec.yaml manages the dependencies of your
project like what third party packages your package might be using. It also configures
the fonts or images used in image (Search about yaml). pubspec.lock is auto
generated by flutter which is kind of detailed form of pubspec.yaml file.
BASIC_FLUTTER _APP (Quiz app)
In this app we will learn the basics of flutter using an application which takes response to
questions through buttons and displays personality based on scoes of button click.

(DART) Named arguments

class Info {
  String name;
  int age;
  Info({String uName, int uAge = 0}) {
    name = uName;
    age = uAge;
  }
}

int main() {
  Info u1 = Info(uName: "Nikhil");
  Info u2 = Info(uName: "Sakshi", uAge: 16);
  Info u3 = Info(uAge: 15, uName: "Makhan");
}

In the above example – uName and uAge are named argument. These are mainly used for 2
things –

a. To provide optional arguments while calling the function – Like while create u1
object above, it was optional for us to skip the uAge argument (in which case the
default value assigned to uAge i.e. 0 is stored as age of nikhil). So, we can pass as
many arguments as we want and to tell the argument is for which parameter just
pass argument in the following the syntax - <parameter_name: argument_value>
b. To Pass the arguments in any order – this is very useful for functions with lot of
parameters because while calling the function we won’t have to remember the order
in which to pass the arguments.

NOTE – in flutter we can make some of the parameters required while some parameters
optional by using @required before the required parameter and if the argument for that
parameter is not provided while calling the function then there will be a compiler error.

(DART) Constructor Short-cut


There is shortcut to initialize the data members without a constructor body. In this if we use
the this.<data_mem_name> as constructor argument, then the complier implicitly
understands that we want to store the argument received while object creation into the
data member.
The following is a Demonstration of the above –

(DART) One-Lined Function


in flutter, we can create one lined function by using ‘=>’ as shown below –
void main() => runApp(MyApp());

Creating Hello World App


This is guide explaining the code to create a hello world app (or an app to just display
something) –

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  Widget build(BuildContext context) {
    return MaterialApp(home: Text("Hello World!"),);
  }
}

Code Explanation -
 Now the create a hello world app, we need to display a text onto the screen. Now as
everything in flutter is a widget, this hello world text will also be a widget. But now
what is a widget programmatically in flutter – a widget is an object. So, we will need
to create a class to create or use a widget. Let’s call this class MyApp class.
 Now a widget is a portion of UI so for creating a widget we will need to handle pixels
of the UI which is not trivial. Thankfully its already done by flutter using build
method of stateless widget class. So, to get this method in our MyApp class, we need
to extend the stateless widget class.
 Now we can easily use build function in our class to build & return a widget. As we
will provide our definition own of build function (to build a hello world widget), we
have to override the build function and overriding means taking same parameters &
return same type as defined in Base Class function definition. As we get a widget in
return, we return an object of Widget class. And it takes as argument a BuildContext
object (it will some metadata about the widget, its position in the widget tree and
the overall application. Search more about this).
 But how to actually make the hello world widget? For that also flutter provides a
class called MaterialApp which wraps various widgets (passed through its
constructor) into single widget required for an app using material design.
Now this function provides various parameters which are optional. But how can
flutter recognise the argument which we pass is meant for which parameter. So,
flutter provides named parameters. So now will pass a built-in text widget as follows
text(‘Hello Word!’) to create the text widget for home: parameter and MaterialApp
will wrap it into a single widget and return it.
 Now to display this widget (i.e. actual work of painting or rendering on screen) we
call runApp() in which our MyApp widget object has to passed. The widget object
will be used to call the build() given in MyApp to build the widget and then the
build-up widget will get painted onto the screen (the painting is done by runApp
because it uses a class which is extending some rendering class. And the rendering
class does the actual drawing in screen - https://medium.com/manabie/how-flutter-
renders-widgets-fd6eca945a04#:~:text=Flutter%20will%20go%20through%20your,when
%20Element%20calls%20createRenderObject(). Or offline).

Now the above hello world App can be beautified by making the below changes to the build
function (Using scaffold()) –

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Hello World App')),
        body: Text('Hello World!'),
      ),
    );
  }
}
Code Explanation –
 The main part in the modified code is using scaffold widget. Scaffold helps in
implementing the basic material design visual layout structure.
 But how? By passing widgets to the named arguments provided by it. Scaffold will
create a basic app widget by combining the widgets provided as arguments. So, in
above we pass we use 2 of its named parameters - appBar & body.
 appBar needs a widget which will be displayed as app bar. So, for creating the app
bar also, flutter provides an inbuilt widget called – AppBar().
 In the main body we want to display the hello world message so it means that in
body: parameter we just need to pass text widget with our message.

(DART) Lists

List are dart equivalent of arrays. They are implanted using classes and the syntax is as
follows-
List<type> list_name = [list_data] (creates dynamic array i.e. Elements can be added later)
Or
(creates static array i.e. elements can’t be added later)

(DART) Access Modifiers

Actually, dart doesn’t have your typical access modifiers (public, private, protected). Instead
it just uses ‘_’ which makes member method, data member, or class as file public (that is it
will be visible in the file only. For other files it is private). In dart this called private access
modifer. Read this in detail in this doc - offline.

BEWARE - Now in course, Max makes a class private and then makes its members also
separately private. But you had doubt that what is the need of creating a member
separately private when class itself is private. I mean if we can’t create instance of class or
extend it outside the file then its members can’t be accessed and thus, they implicitly
become private but consider the following case
https://stackoverflow.com/questions/53495089/dart-should-the-instance-variables-be-private-or-
public-in-a-private-class –
(DART) Multiple constructors

In dart, we can create multiple constructors with different names this is shown in the
following code snippet –
In the above we create 2 constructors and 2 nd constructor is a named constructor. Now
nothing new just remember the syntax – if before a function we have the class name and
dot then it is a named constructor.

(DART) CALLBACK IN FLUTTER/DART (INCOMPLETE)

Look at what Function class does and What typedef cb = void Function(paramters) does.
Look into detail how function pointer (passed through callback) is called in flutter.

(DART) Maps

The Map object is a simple key/value pair. Keys and values in a map may be of any type. A
Map is a dynamic collection. In other words, Maps can grow and shrink at runtime.
Maps can be declared in two ways –
 Using Map Literals –
To declare a map using map literals, you need to enclose the key-value pairs within a
pair of curly brackets "{ }".
Here is its syntax −
var identifier = { key1:value1, key2:value2 [,
…..,key_n:value_n] }

 Using a Map constructor –


To declare a Map using a Map constructor, we have two steps. First, declare the
map and second, initialize the map.
The syntax to declare a map is as follows −
var identifier = new Map()
Now, use the following syntax to initialize the map −
map_name[key] = value

Example: Adding Values to Map Literals at Runtime


void main() {
var details = {'Usrname':'tom','Password':'pass@123'};
details['Uid'] = 'U1oo1';
print(details);
}

It will produce the following output −


{Usrname: tom, Password: pass@123, Uid: U1oo1}

(DART) Iterators (INCOMPLETE)

An example (maybe pointer internally as they are in c++ because use case is exactly same as
demonstrated below Bute still search more) –

(DART) Collection

Collection is an object that represents a group of objects, which are called elements. So, this
just an array but storing objects within it (instead of a data type).

So to take example consider collection – List which is created as follows –

List<int> nums = new List<int>(3);

Now list nums is a collection because it is an object (of List class) and the object will store 3
objects of class int (yes, int is a class in dart).

(DART) Iterable
 Now the most basic collection in dart is Iterable which stores elements in such a way
that they can be accessed sequentially only. This is proved by below code –
Iterable<int> iterable = [1, 2, 3];
int value = iterable.elementAt(1);

You can instead read elements with elementAt(), but internally it steps through the
elements of the Iterable until it reaches that position which is sequential access (not
random access) and op [] isn’t defined for Iterable so you can only access elements
sequentially.

 The elements of the Iterable are accessed by getting an Iterator using the iterator
constructor, and using it to step through the values (in c++ iterator is pointer so this
may also be pointer it is helping in sequential access which pointer also does).
Stepping with the iterator is done by calling Iterator.moveNext, and if the call
returns true, the iterator has now moved to the next element, which is then
available as Iterator.current.

 List (which is collection storing elements in such a way that random access can be
done) and Set (storing objects which come only once inside the collection) are
modified Iterable because they extend Iterable class. Whereas Map, even though
using a different structure internally, is Iterable because its keys are Iterable (search
more on this). Most classes in the dart:collection library as also Iterable.

 Iterable is an abstract class and thus can’t be instantiated directly but by assigning
list or Set object to create its instance like given below –
Iterable<int> iterable = [1, 2, 3];

(DART) Final Vs Const

The differences between the two are -

1. final is mainly used as runtime constant and const is compile time constant –
A compile-time constant is a value that can be (and is) computed at compile-time. A
runtime constant is a value that is computed only while the program is running. If
you run the same program more than once:
a. A compile-time constant will have the same value each time the application is
run.
b. A runtime constant can have a different value each time the application is
run.

So, based on above use-cases are –


 final –
If you don’t know what it’s value will be at compile-time and it will be
calculated/grabbed at runtime. For example, when you can need to get data
from an API (this happens when running your code) like below –
final birthday = getBirthDateFromDB()
 const –
If you are sure that a value isn’t going to be changed when running your
code. For example, when you declare a sentence that always remains the
same like below –
const birthday = "2008/12/26"

2. (Extension of above) Both final and const prevent a variable from being reassigned
(similar to how final works in Java or how const works in JavaScript).
The difference has to do with how memory is allocated. Memory is allocated for a
final variable at runtime, and for a const variable at compile-time.
Mainly that a final variable may be an instance variable, but a const must be a static
variable on a class. This is because instance variables are created at runtime, and
const variables--by definition--are not. Thus, const variables on a class must be
static (they can’t be used otherwise whereas final can be used without static),
which means simply that a single copy of that variable exists on a class, regardless of
whether that class is instantiated.
(Note – final can also be used for declaring compile-time constants it is just that
const is preferred for it because that is the definition of const and when 2 things can
achieve the same task, you should use prefer concept which is defined for the
particular task).

3. Now, when everything which can be done using const can also be done using final
(cause final can also declare compile time constants) then why use const? Actually,
there is one thing that final can’t do that is make object stored in it also final. See,
const modifies values whereas final modifies variables. So, when we use const
on a variable the object stored in var also becomes a constant and thus
completely immutable. Whereas when we use final on a variable, the stored
value/object in the variable doesn’t become constant. We can’t reassign but that
doesn’t mean that the object already stored itself can’t be modified i.e. we can
change the fields of the object which is stored in a final variable thus it is not
completely immutable.

So, both const and final cannot be reassigned, but fields of the object being stored
in a final variable, as long as they aren't const or final, can be reassigned (unlike
const).

To understand this completely see the below example –

void main() {
    final listA = [5, 6, 7,];
    listA.add(8);//perfectly fine
    print(listA[3]);//prints 8 meaning 8 is added to list
    
    /*But the above same is not doable with const (which is a good thin
g as it will make list Completly immutable)
    final listA = [5, 6, 7,];
    listA.add(8);//This will give error as const will make value also c
onst
    */
}

4. Initailization (SEARCH MORE ON THIS)–


Actually, the left side of pic, which tells about final, is incorrect. Final can be
initialized in line (during declaration) or with factory constructor syntax as shown in
below example code.
With factor constructor, final will be initialized after constructor is called thus left
side is wrong

Initializing inside constructor body gives error (as shown in below comment).
(Search MORE on GOOGLE - initializing final in flutter) –
Quiz(this._qAnda, this._indx, this._c);

  /*IMPORTANT - see why the below dosen't work but above one works 
  Quiz(List<Map<String, Object>> qAnda, int indx, Function _c){
    _qAnda = qAnda; //This will throw an error that final can’t be
initialized here
  }*/
One reason for above can be (your speculation) just that dart doesn’t allow use of
‘=’ op on final variable but with the 1st way we aren’t using any equal operator thus
dart can handle the 1st way and thus allows it. The value will be put into memory
when the object is created.

(DART) GETTER

Getter is a special type of property. It is syntactically a mixture of data member (property


in dart) member method. An example of getter is given below.

class Result extends StatelessWidget {
  final _totalScore;

  String get resultPhrase {
    if (_totalScore <= 5)
      return 'You are Innocent';
    else if (_totalScore <= 10)
      return 'You are Casual';
    else
      return 'You are Edgy';
  }

  Result(this._totalScore);

  @override
  Widget build(BuildContext context) {
    return Text(resultPhrase);
  }
}

As you can see in above syntax – syntactically you can note the following –

a) Defined like a function - with keyword get, it is necessary to give a return type for it.
No parenthesis after getter name because it is a method which can never receive any
argument (It just gives, never receives anything). And it also has a body so it has 2 of
3 things a function definition has (return type, parameters, & body).
b) Used like a property - we just used it inside Text widget just like any data
member/variable/property.

Creating a Question answer app

This shows mainly the how to place multiple widgets into a widget & working of a button.
The main features explained are –

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() { return MyAppState(); }
}

class MyAppState extends State<MyApp> {
  int indx = 0;
  var ques = ["what's your favourite animal", "what's your favourite colour"];

  void changeQuestion() {
    setState(() { // changing state to Re-render UI
      indx = (indx + 1) % 2;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Q&A App')),
        body: Center(
          child: Column(
            children: [
              Text(ques[indx]),
              RaisedButton(
                child: Text("ANS 1"),
                onPressed: changeQuestion,
              ),
              RaisedButton(
                child: Text("ANS 2"),
                onPressed: changeQuestion,
              ),
              RaisedButton(
                child: Text("ANS 3"),
                onPressed: changeQuestion,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

 Now we use Column() to place widgets in column order. In flutter everything is


widget – things we see are certainly widgets (like buttons, text, app-bar are all
widgets which we can see) but things we don’t see are also widgets (Column() is an
example of that. These widgets are layout widget which helps us in structuring our
app by controling how other widgets are drawn onto the screen. Some other
examples are Row(), Grid(), ListView()).
Now this has a named parameter called children. This parameter takes list of
widgets as its argument. So that is what we are doing inside column(), we are
passing 4 children widget – one text widgets for questions and 3 RasiedButton
widgets.
 Now this RaisedButton() creates a button. Now it has a child widget which is used to
display some content inside the button like text, image, icon. Passing functions in 2
ways – pointer to a function (or using call-back) and anonymous function (maybe
lambda equivalent of dart).
Now as it is button it has to have some task when pressed. This is defined by
onPressed data member which is made @required by flutter. Now this needs a
function (not function value but function itself) that means it uses call-back. Now, in
flutter callback can be implemented in 2 ways –
a. Pointer to a function (same as c) – just pass the name of the function without
the parenthesis.
b. Anonymous function (lambda equivalent) - non named function using syntax –
() {<function definition>}.
Useful when function is only used in place.
 Now, in dart there are two types of widgets - Stateless & Stateful widgets. To
understand these widgets first you need to understand what state in a widget is.

State (int terms of widget) is data/info used by widget.


(As widgets are classes thus data/info used by widget is nothing but data members)

Now, Stateful widgets are those in which - data inside the widget can change. So, it
means that any widget class which has a data member which can change inside the
class and that change is persisted (i.e. the change must be maintained in the next
object created) has to be a stateful widget. So, internal state is just a static data
member. In this app, we have question index (int indx) as a data member whose
value is changed inside the class (when a button is pressed), then this class must be a
stateful widget.
This is taught in course also – a Stateful widget has some internal state which can
change whereas Stateless widget’s internal state doesn’t ever change (main
difference). This internal state is nothing but data member value defined inside the
class. Now there can also be an external state (That means the member(s) receive
the data in data member from outside the class by passing values using constructor
i.e. when you get data as input from user while creating the object). Both widgets
can have external state changed.
Now, you did not quite understand how to create stateful widgets and thus your
doubts are given in NOTE BELOW. READ IT TO MAEK FURTHER NOTES.

Now, whenever any state is changed, flutter re-renders the UI (this is done by
explicitly calling setState() method to indicate to flutter that out state has changed
so you should re-render the UI.
Note - word ‘explicitly’ is important. If we change data without using setState() UI
won’t be re-rendered.
We use setState() by passing a function into it (call-back) which contains the logic
for state change.
setState() forces flutter to re-render the UI by calling the build method of the class
in which it is used. build() re-builds the widget tree but the whole UI is not re-
rendered. Instead just that part which is changed is re-rendered and this done
using the element and render object tree).

* NOTE - Actually, you don’t quite understand the method of creating Stateful
Widgets maybe because the whole idea of how dart manages the stateful widgets is
not known to you thus you have doubt like below –
1. (Main Doubt) why to have 2 classes for creating stateful class? Now if we
change data member inside the class that change should be persistent i.e. if we
create a new widget object the data changed through previous widget has to
persist and thus the new object should also have changed internal state value.
So, in oops, you would have done this by making the internal state data
member as – static. But we can’t do the same here because we are working with
a framework and thus, we have to work how it wants us to work. Actually, one
reason can be that (Search actual reason) – because flutter maintains a widget,
element, and render object tree it may need to rebuild one of the trees and thus
it can’t just let us make a static variable and infer from it when to re-render UI.
2. (Doubt) why flutter makes us create 2 classes for stateful widgets (why can’t
we have static members instead inside the class. And if there is a static
member then we have to define it as stateful widget) and how they are
connected to each other.
3. (Doubt) Intuitively understand the method to create a stateful
widget programmatically in flutter i.e. -
a. (Doubt) how runApp() calls createState() (function) to
create and manage states and how widgets get build (i.e.
how the build method gets executed by runApp())
b. (Doubt) How setState() function works under the hood in
flutter.
c. (Doubt) what is State<MyApp> (it is maybe a generic class
so search about it).
Look at this link if it helps - https://proandroiddev.com/flutter-a-hitchhiker-guide-to-
stateless-and-stateful-widgets-cc9f9295253b.
So, for now just remember the method to create stateful widgets (as taught in
course) and later understand how flutter (framework) manages the stateful widgets
so that you can deeply understand why we need to create stateful widgets the way
we create them.

Now we better the code (using conventions), beautify the app a bit more, and change text
of the answer buttons according to question by following code changes –

main.dart

import 'package:flutter/material.dart';

//Leave above one line to import your own packages
import 'package:flutter_basics_app/answer.dart';
import 'package:flutter_basics_app/ques_ans_text.dart';
import 'package:flutter_basics_app/question.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return MyAppState();
    /* Method 2 Code
    return MyAppState([ ['Cow', 'Rabbit', 'Cats'], ['Blue', 'White'] ]);*/
  }
}

class MyAppState extends State<MyApp> {
  int indx = 0;
  /*Method 1 - List of QuesAnsText objects
    var qAnda = [
    QuesAnsText( "what's your favourite Animal", ["Cow", "Rabbit", "Cats"] ),
    QuesAnsText("what's your favourite Colour", ["Blue", "White", "Black"] ),
  ];*/

  //Method 2 - using a Map() object
  var qAnda = [
    {
      "question": "what's your favourite animal",
      "answer": ["Cow", "Rabbit", "Cats"],
    },
    {
      "question": "what's your favourite Colour",
      "answer": ["Blue", "White", "Black"],
    },
  ];

  /*Method 3 - 2-D list having answers list according to question indx 
  var ans = List.generate(2, (i) => List<String>(3));
  
  MyAppState(this.ans);*/

  void changeQuestion() {
    setState(() {
      indx = (indx + 1) % 2;
    });
  }

  @override
  Widget build(BuildContext context) {
    /* Method 3 code. (Exact same for method 2 just with little changes to ans
wers from 2-D list)
    List<Widget> answerButtons = new List<Widget>();
    for (int i = 0; i < qAnda[indx].getAnsLen(); i++) {
      answerButtons.insert(i, Answer(qAnda[indx].getAnsI(i), changeQuestion));
    }*/

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Q&A App')),
        body: Center(
          child: Column(
            children: [
              Question(qAnda[indx]['question']),
              ...(qAnda[indx]['answer'] as List<String>).map((answerText) {
                return Answer(answerText, changeQuestion);
              }).toList(),

              /*Method 2 & 3 Code
              ...answerButtons,*/
            ],
          ),
        ),
      ),
    );
  }
}
answer.dart

import 'package:flutter/material.dart';

//Answer widget builds a RaisedButton widget contiang ans text
class Answer extends StatelessWidget {
  final String text;
  @required
  final Function callback;

  Answer(this.text, this.callback);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 250,
      child: RaisedButton(
        child: Text(text),
        color: Colors.blue,
        textColor: Colors.white,
        onPressed: callback,
      ),
    );
  }
}

question.dart

import 'package:flutter/material.dart';

//Answer widget builds a Container widget contiang question text
class Question extends StatelessWidget {
  final String que;

  Question(this.que);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      child: Text(
        que,
        style: TextStyle(fontSize: 20),
        textAlign: TextAlign.center,
      ),
    );
  }
}

Better the code with following conventions -


 First, we change the code into files into 3 files – main.dart, question.dart &
answer.dart but why?
Actually, this is a common convention – have one UI element widget in a file. This
gives 2 benefits –
a. Better code base management – It let’s us have as little code as possible
in lib/main.dart and all the other code split into various files so no file gets way
too big thus giving better management of codebase.
b. Better performance – search how?
And to use these files – we import these files using the syntax (Note – another
convention - make a separate block, by leaving a line between our own imports and
system file imports like material.dart) –

import 'answer.dart';
import 'question.dart';
(NOTE – until you don’t get the practice, make your code first into the main.dart
file and later shift the code in other files. This is so that you don’t get confused to
get the logic and shift between files to know what is happening. This is also what
happens in course. He makes code in main.dart dart and later on shifts code in
different files and makes changes accordingly)
 Second, we use final keyword for our data members or states of our Stateless
widgets because our data members can’t be changed from inside the class. They can
only be changed externally through constructor when a new object is created.

Now let’s start styling our app -


 Firstly, lets style the question text (in question.dart) in the following ways –

For this we start by increasing the font size of the question. This can be done by the
using the named parameter called style: it takes as argument a TextStyle widget.

Then let’s centre the text. This can be done by Centre widget also but in course there
was also a different approach. In this approach we first use the named parameter
called textAlign: It can be provided an Enum called TextAlign.center which aligns the
text in centre. But using it alone won’t make text centred because by default Text
widget only provides as much space of screen as needed by text. But then how to
give the text full width of the screen. This can be done by enclosing it in a Container
widget which in-turn has a named parameter called width: and setting its value to
double.infinity you give the container the full width of the screen. Now when the
above Text widget will be provided to the child: named parameter the text widget
will also have the full width of the screen and then the text appear centred.
Note – Container widget has the following layout on screen –

 Now let’s Style our button (in answer.dart file) in the following ways –

To add colour – we can give colour through named para color: in following ways –
a. Either we can create a Color widget and in it pass the colour value as follows –
color: Color(Colors.lightBlue[300].value)
b. (course method) to give a basic colour, Colors class has static members which
store the hex value of the colour So we can use them.
Now it is better than above because we don’t instantiate the object of colour as
we can call static members without create objects of the class in the following
syntax –
color: Colors.blue,

NOTE – This syntax may seem similar to Enums but the difference Enums can
only store numbers whereas a static member is a variable and thus can store
different datatype values like here itself blue is storing hex code of blue colour.

Make button wider – Now you can also make the button look bigger with 2 ways –
a. One way is to use the named parameter called padding: and add padding to left
and right using EdgeInsets.only(left: value, right: value)
b. (course method) We can simply enclose out button within a container and give
the container the full width of screen (using double.infinity). This will also make
button wide.

Now let’s change text of the answer buttons according to change in question (in
main.dart)–
 Before moving on a note on a topic - Lifting the state up – This came very naturally
to you (because it’s quite logical) but still for understanding.
So, this lifting the state up concept tells to store the state into the parent widget
instead of the widget using the state.
This can be understood by the analogy of SUN 🌞
In the world, everyone can see (observe) or feel (listen) the sun because always
located above us i.e lifting the state up.

This is what you did here, indx state will be used by answer.dart (to update the index
so that it increments and shows the next question) as well as question.dart (to know
which question to pass into the Question widget constructor). So, we stored the indx
state inside their parent MyApp widget (actually inside the MyAppState class but
this is attached to MyApp class so it is indirectly inside the MyApp widget class) so
that it is accessible to both.

 Method 1 (course method) - Changing the answers using Maps Class


We saw above we can create a class and use it as a data structure to store questions
and answers together. But there is also an alternate DS, provided by dart, to store
questions and answer in a single data structure and that is Map which is just and
associative array (key => value pair). We can create a list of Map’s, in which each
map will contain 2 keys – ‘question’ (value will be string question) and ‘answer’
(value will be list of answers to the question).

Now for Column widget needs list of widgets so how can we convert the list of
answers in a map into a list of widgets? This can be done by map() function
provided by List class (actually iterable class but as list extedns iterable so it also
has it). map() function allows to apply a function over each of the elements of list
on which it is called, replacing each element with a new one.
As map() takes a function as argument, So we can create an anonymous function and
pass it as argument to map().
The argument to the anonymous function, provided by framework, will be each item
of list on which map() is called. And the anonymous function should return the type
of item we want in the converted list. In our case we want a list of widgets so we
return Answer widget.
Now we still get an error because map doesn’t actually return a list but an iterable
(search what is it). But we can convert iterable into a list using toList() method.

But on which list we call the map() function? We call it on the list stored
corresponding to answer key in our list of Map’s. But it will still give error, why?
Because dart is not able to infer that value corresponding to ‘answer’ key, in map, is
actually a list. For this we need to tell dart that the value is defiantly a list and we do
this using Typecasting using ‘as’ operator (which is also known as .cast()).
NOTE - there is also another operator ‘is’ (which is known as .retype()). for type
casting but there is a difference between the above two (search more).
But if we do the above the Column widget will store the list of button widgets as a
list itself (i.e. there will be a nested list). But we need individual items of button
widget list. This can be done by spread operator (…) which tells dart to store the
items of (nested) list individually inside the (parent) list.

 Method 2 - changing the answers using 2-D array of widgets


Here the idea is to couple answers with its corresponding question index using a 2-D
array of list. (See method 2 commented code above for this approach).

 Method 3 - Changing the answers – by creating a List of objects of a Class having


question and answers (as list) text as data members (idea from the course). This will
couple together the questions and ans together in an object and list indx can be used
to display a access an object of questions and answers from list. (look code for
method 3 above to understand. Make QuesAnsText Class yourself)

NOTE – This is not the full complete app. In the last 1-1.5 hours of the 2nd module (flutter
basics), the instructor provided score for each choice and displayed the personality based
on total score of choices and then provided how to reset quiz. But there was not much
new to learn thus you have not recorded it. Few new things learnt were -
 Passing anonymous function’s pointer instead of address of function for call-back.
 Getter (see if it comes in flutter or dart. If in dart add it in dart category)
EXPENSOR
This app calculates expenses of a user and shows it in a good-looking UI. So, we will learn
about a lot of build in Widgets for UI designing.

Column and Row Widgets

A Row is a widget used to display child widgets in a horizontal manner And Column in
vertical manner. The Row & Column widget don’t scroll.
Most Properties of Row and Column are given in here –
https://medium.com/jlouage/flutter-row-column-cheat-sheet-78c38d242041 or Offline &
Row Column Course Summary Offline

Container Widget

The Container widget is used to contain a child widget with the ability to apply styling and
aligning properties. For explanation on commonly used properties see the below doc -
https://medium.com/jlouage/container-de5b0d3ad184 or Offline

Container vs Columns/Rows

Both have their differences (but both are really important) Thus the most important thing is
use them in combination to harness their full potential. The differences are given below –
Card Widget

This is also a very important widget. It gives the card layout which we are mostly used in
modern UI’s. Properties explanation can be found in official docs -
https://api.flutter.dev/flutter/material/Card-class.html

(DART) String Interpolation

In dart, like in other languages, we can concatenate strings using + sign. But if the we have
to concatenate a lot of stings then it becomes cumbersome (try converting DateTime.now()
into human readable string using + and you will know what I am talking about).
So, Dart provides alternate syntax using ‘$’ (Dollar sign). You can access the value of an
expression inside a string by using   ${expression}. The example of the syntax is as
follows–

DateTime date = DateTime.now();
String format = "${date.day}/${date.month}/${date.year}";

The call to  toString()  is unnecessary (although harmless). In above case:  toString()  is
already defined for DateTime object and Dart automatically calls   toString() .

NOTE – If the expression is an identifier/variable, the  {}  can be skipped.

Formatting Dates

For formatting date, either you can the date, month, or year format. But to get other
formats (like displaying month by name or getting last 2 digits of year), by minimal code, is
to use following packages from pub.dev –
a) DateFormat (Preferred – because of ease of use and documentation availability here
- https://pub.dev/documentation/date_format/latest/date_format/date_format-library.html )
b) Intl (Maybe this provides a more format but documentation is lacking. See it from
course - https://www.udemy.com/course/learn-flutter-dart-to-build-ios-android-apps/learn/lecture/15179244#overview ).

Getting Input Data from Text Editing

There are 2 methods to get input from a text field (Which we have used in input_card.dart)

a. (PREFFRED) Using TextEditingController object – This class provides a controller for


some text field using which  Listeners can then read
the text and selection properties to learn what the user has typed or how the
selection has been updated.
Why preferred over the below -
 This has an advantage that the text gets automatically cleared from TF after
being submitted using a button.
 And also, flutter provides a lot of input related methods in
TextEdtingContoller so don’t need to write code for them.

Example on how to use this is provided in official docs in the link -


https://flutter.dev/docs/cookbook/forms/retrieve-input.

b. Using onChanged – this property takes a function as its value and passes a string
value into that function denoting the updated/changed value of Text Field. This
can be used to store in a var and thus we will get the input.

NOTE - if you use this make sure that user is forced to change the text every time
before submitting (that is text field is cleared after accepting the input) so that you
don’t get the data can’t be null message. This is because if user presses button
without changing previous contents then a null value will be passed as argument
the onChanged function (because nothing changed. Previous state was intact).

Lifting the state up

This app also gives an important lesson on lifting the state up which is demonstrated by the
file transaction_ui.dart as explained below –

Lift the state up in a separate stateful widget (not in MyHomePage widget so that
MyhomePage can remain stateless because scaffold and app bar don’t need to change just
because a transaction got added. They will remain same and thus keeping MyHomePage
as stateless will boost the performance because build will not be called again and again to
build scaffold, materailapp, and app bar (in stateful build is called again as soon as state is
changed using setState()).

That separate widget is transcation_ui.dart given in below code –

NOTE – this will be removed later (thus there is no file named as such in final app) when you
create Modal Bottom Sheet. This is because bottom sheet comes up when floating action
button is pressed which is in build of _HomePage. Thus, we have to define the function,
which displays bottom sheet, in the _HomePage. But bottom sheet uses InputCard which
takes addTransaction() method as parameter. So we also have to bring addTransaction()
to main.dart. But AddTransaction is the function that changes state and is part of the
State of transaction_ui.dart. Adding it to homepage means moving the whole state logic
of transaction_ui.dart to main.dart in which case transaction_ui.dart becomes redundant
as everything has to be shifted to main.dart.

How to make column/row scrollable (using SingleChildScrollView or ListView)

Problem (pixels overflowed When keyboard comes up) - If the widget in which the
textfields are present is not scrollable then when we type input to text field using soft
keyboard, we will see a blackyellow line showing pixel overflowed. This is because when
keyboard comes up, the size of our viewport/screen size available (i.e. size of body in
scaffold) gets reduced (this you can see using flutter inspector and is also given here -
https://medium.com/@rubensdemelo/flutter-forms-improving-ui-ux-with-singlechildscrollview-
7b91aa981475). Now in a reduced viewport, the size of column will also reduce and in that
reduced column size (in fact in column size), if the widgets don’t fit, we will get this
overflowing error (By this error flutter tells us that there may be content you want to show
but flutter isn’t able to display it due to lack of availability of screen space. The pixels value
shows the screen space (in terms of pixels) needed to display all the widgets). Now to
overcome this problem we use some ScrollView object on the viewport (By wrapping the
viewport widget inside the ScrollView object, SingleChildScrollView is a ScrollView Object).
By adding scrollView object we say to flutter that it is ok for widgets to go out of
viewport/screen size. If some widget goes out of screen size you just hide it and when user
scrolls you show it and hide other widget on top according to the screen space you have.

Solution – there are 2 methods to solve this problem -

a. Make column scrollable using SingleChildScrollView (used in main.dart) – this is a


ScrollView extended widget in which if we wrap a column that column gets scrolling
feature.

While using this, take care of the following -

 (SEARCH MORE ABOUT THIS) Make sure that the parent widget of
SingleChildScrollView has a defined height so that it doesn’t get confused as
to what is the height on which it has to apply scrolling. This is done by
wrapping SingleChildScrollView in a container to which we can apply some
definite height. Or in our case, as part of scaffold body because it has a height
of the viewport. But if the parent container of SingleChildScrollView doesn’t
have height (like a column takes as much height as it can get which is
infinite hight. This is explained in course BUT SEARCH MORE ON
GOOGLE – why SingleChildScrollView/ListView don’t get applied
on a column but works on scaffold body property without any
parent). then it gets confused as to what is the height scrolling will be
applied to and thus you don’t get scrolling feature.
 Apply SingleChildScrollView on the widget covering our whole viewport and
not its child widget which is being hidden by keyboard - In Expensor, instead
of applying scrollView on the scaffold body: column, which constitutes our
whole viewport, you tried to apply it on one of its child widget, Column
widget with list of transactions, as that was the part of UI which was being
hidden by keyboard (you did as above by wrapping column in
SingleChildScroll which in-turn was wrapped in a Container with height
property defined). But remember, the container (which wraps our
SingleChildScroll and transaction list column) will be part of our viewport
Column. Now with keyboard coming up, viewport Column will reduce but
because container will have constant height it won’t reduce and thus if size of
container exceeds the size of column you will again get the error. So, Apply
ScrollView on the widget covering our whole viewport and not its child
widget which is being hidden by keyboard.

b. (Short cut to above) use a ListView - Instead of wrapping of column with


singleChildScrollView we have shortcut that is to use ListView. It is a
singleChildScrollView with column in it.

The same concept of defining the height is applied on ListView also i.e. Make sure
the parent widget of ListView has a defined height so that it doesn’t get confused as
to what is the height on which it has to apply scrolling (eXplaination given in this
video).

ListView can be used in 2 ways –


a. ListView(children: []) – in this we can pass a list of widgets in [] and they will
be displayed with scrolling feature. Same effect as SIngleChildScrollView
with Column but less code so preferred over it.
b. ListView.builder() – this feature is used to render widgets only which are
visible. How to program it is given here at 7:25 and used in transaction.dart

ListView.builder() has following advantages over SingleChildScroll &


ListView(children: []) –

ListView.builder() is optimized as it only renders widgets which are visible.


whereas ListView(children: []) or SinlgeChildScoll renders all the widgets
without considering whether they are visible or not. Now in long lists,
ListView(children: []) will fail because loading all the widgets means that app
will consume a lot of memory, a lot of performance for widgets that aren’t
even visible. And will result in lags because will have to maintain all the
widgets.
Get a specific keyboard for input in textfield

This is done using keyboadtype: named parameter in textfield and InputTextType classes
static properties. Like the below shows how to get number keyboard –

keyboardType: TextInputType.number,

in iOS for double values this may not work because it may not give ‘.’ for entering double
value, in that case use –

keyboardType:TextInputType.numberWithOptions(decimal:true),

Convert to a double to a string rounded to x decimal places

Use toSrtingAsFixed as shown below (it will round double to 1 decimal place) –

double price = 10.290;
String n = price.toStringAsFixed(1); //ans – 10.3

FloatingActionButton Widget

Used to add a floating action button. Some of the important named parameters you learnt –

i. Child: parameter – takes the widget which is to be displayed in the button. Generally
we use icons in this which we get by using Icon() widget. In Icon widget we use static
properties of ‘Icons’ class which are different icons you can use. An example of this
is –

FloatingActionButton(child: Icon(Icons.add));
ii. @required onPressed: - like any button, we have to give this a function callback. This
function is executed when button pressed.

Apart from this some other things you learnt are –

a. Adding floating button at bottom and positioning it in centr e –


This is done using the floatingActionButtonLocation: named parameter of
Scaffold(). Value for location can be provided by static members of
FloatingActionButtonLocation in the following way –

floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,

IconButton Widget To add some action to icons in AppBar

This a button which just adds an icon but makes it work as a button. This is mostly used for
adding press feature to an icon in AppBar (Common in modern apps). Example of this is in
our main.dart file as shown below -

home: Scaffold(
        appBar: AppBar(
          title: Text('Expensor'),
          actions: [
            IconButton(icon: Icon(Icons.add), onPressed: () {}),
          ],
        )

The main named parameters to note are –


a. actions: of AppBar – This is used to provide a list of widgets added to the AppBar for
making some action happen through them.
b. @required icon: of IconButton – this provides the icon which will be displayed as
IconButton.
c. @required onPressed: of IconButton – again as this is a button, we have to provide
some action which will happen when this icon is pressed. Done using function
callback.

ShowModalBottomSheet widget (more at - https://medium.com/flutter-community/flutter-beginners-guide-to-using-the-bottom-sheet-b8025573c433 )

Modal Bottom sheet is a widget which comes up from bottom and makes the screen in the
background overshadowed. Important things to remember -
 Programmatically – bottom sheet is shown using function
showModalBottomSheet(). This function takes 2 important arguments –
a. context: named parameter – this parameter takes the BuildContext object as
value in which MaterialApp is present as ancestor (more details about this in
problem 1 given below).
b. builder: named parameter – takes a function (through callback). The function,
whose pointer will be passed as value, should take a BuildContext object as
argument and should return the widget which will be displayed inside the
modal bottom sheet.

 (will be explained in detail later) Close automatically when input is accepted - This
is done through navigation class object shown below line of code –
Navigator.of(context).pop();
Navigator.of() teels the topmost widget right now. And pop() pop’s off removes that
topmost widget so calling this in the submit function (which will be called on press)
will pop the modal bottom sheet.

Problem 1 - While using this you got this error - No MediaQuery widget found inside
showModalBottomSheet.
Solution - This was because the showModalBottomSheet tried to access the ancestor of
type MaterialApp from the given context (this tells about the widgets which are currently
present in widget tree and at which position they are). Now if you are using context of build()
function and in that function you have the MaterialApp then the context will not have
MaterialApp because before calling build MaterailApp was not in widget tree. Which did not
have any context of MaterialApp. So, there are 2 solutions –
a. Use Builder widget to get new context with MaterialApp present in widget tree as
ancestor. This is done by wrapping the Scaffold in Builder() Widget as shown below –

Widget build(BuildContext c) {

    return MaterialApp(
      home: Builder(
        builder: (context) => Scaffold(),
)
);
)

b. Separate your MaterialAapp and Scaffold widgets into separate widgets such that the


build() method in which Scaffold() is built has context of MaterialApp. Example is shown
below -

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter App',
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  void modalSheet(BuildContext ctx) {
    showModalBottomSheet<void>(
        context: ctx, builder: (dummyCtx) => null);
  }

  @override
  Widget build(BuildContext c) {
    return Scaffold(
          appBar: AppBar(
            title: Text('Expensor'),
            actions: [
              IconButton(
                  icon: Icon(Icons.add), onPressed: () => modalSheet(co
ntext)),
            ],
          ),
);
}

As you can see above, we have created a separate widget MyApp for MaterialApp
and int its home: called HomePage widget which contains the Scaffold. Now as
build in HomePage is called inside the MaterialApp thus its context will have
MaterialApp in widget tree as an ancestor.

Problem 2 - Text field gets cleared whenever focus is shifted to some other widget
Solution - Make the input card as stateful widget –
the problem with textfeild getting cleared in modal sheet is because the Input
card is a stateless widget and as soon as focus is lost, it returns back to its
original state i.e. - empty. You will study in detail more on state management
later in the course
Theming the app (providing custom & global colours, fonts, etc.)

Theme is set in MaterailApp widget using theme: named parameter which takes
ThemeData() object as value(ThemeData is not some widget but a Class).

ThemeData has a lot of named parameters but most important ones which you learned in
course are –
a. primarySwatch: - this takes a Color (using static property) and this will be applied to
the appbar and can be used as value to any widget using color: named parameters
where you want to use the theme colour as follows –
color: Theme.of(context).primaryColor
b. accentColor: - This takes the secondary color of the theme. This will be implicitly
applied to elements like button. And can be applied manually like above just using
accentColor instead of primaryColor.
c. fontFamily: - this lets you define font that will be used for theme. For custom fonts
you need to add the fonts to pubspec.yamal file (method is given in the comments
of yamal file itself. Just remember that for bold you have to define the weight the
bold font takes which is give in official docs). Now after adding the font use the font
in MaterialApp as follows (Make sure after adding to the files you restart the app) –

MaterialApp(theme: ThemeData(fontFamily: 'Quicksand')

d. appBarTheme – let us suppose that you want to set a particular Theme for all
AppBars in the in the app then we pass AppBarTheme() (again a class not a widget).
More detail on this is given in next section.
Now as you can use the value of color in palace of any color: named parameter
similarly you can use this value in place of other style: named parameters Also
shown in next sec.
e. textTheme – this can be used to set themes for the whole app globally. This is
explained in detail in next to next section.

Advantage – the main advantage is that of using a hard-coated number vs macro constant
i.e. you don’t need to change it everywhere if you decide to once you change it at one place
in MaterialApp theme it will automatically get applied to all other.

Setting Custom Text for All App-Bar’s in Our App Using AppBarTheme class

As discussed above, we can use a named parameter appBarTheme and pass an


AppBarTheme() object to it. But in this section we discuss in detail what we can do with
AppBarTheme() object. In this object there are 5 named parameters for setting new values
for each component and some additional setting of which an AppBar is made of. In this app
we will see how to change the font of Text in the App bar which can be done by following
parameter –
a. textTheme: – as this deal with theme of text we pass a ThemeData object to it. Now
we don’t want to change every value of text theme in the AppBar but just the font
family. For that we get the primaryTextTheme set on the app bar using and copy its
existing value of fontFamily using new one with finction copyWith(). in this function
you can use the title: named parameter (this is deprecated and from doc you got
that new name for it is headline6: ) which takes the TextStyle() widget using which
you can eventually apply the fontsize. The code is shown as below –

MaterialApp(
      theme: ThemeData(
        appBarTheme: AppBarTheme(
          textTheme: ThemeData().primaryTextTheme.copyWith(
              headline6: TextStyle(fontFamily: 'OpenSans'),),),),),
NOTE – value for textTheme can also be replaced by the following line –

TextTheme().copyWith(
     headline6: TextStyle(

Now you can use this style of headline6 in place of other Styles where you need the
exact same style as follows –

style: Theme.of(context).textTheme.headline6,

Setting Same text Theme in MaterialApp Globally (For code reusability & maintainance)

This can be using textTheme: named parameter of the ThemeData() Class. It is very similar
to above just replace headline6 with bodyText2.
bodyText2 is the text style used for widgets which don’t define their own text style. So,
use this for the most used text style in the app so that code is reused the most. The code
for this is given below –

MaterialApp(
      theme: ThemeData(
        textTheme: TextTheme().copyWith(
          bodyText2: TextStyle(
            fontFamily: 'Quicksand',
            fontSize: 15,
            fontWeight: FontWeight.w500,
            color: Colors.grey[700],
          ),
        ),
NOTE – As stated above, if you want to use bodyText2 as style for some text, you don’t
need to provide any value to style: parameter, that is the default style flutter assigns to a
text if no style: value is provided.
You can use the bodyText1: parameter also, but flutter will not implicitly assign it, instead
we will have to use it in the style: named parameter of the text widget. Just replace
headline6 with bodyText2 in this syntax (above).

Providing Custom Images

To use a custom image, follow the below steps –


 Add the image in assets/images/ folder and provide its path in pubspec.yamal file
(given in comment).
 Then use Image.asset() (asset function of Image widget) which takes path to the file
in like this – assets/images/<file_name> [Note – we can also get an image from
other sources like – web (from URL), file (search more), memory (search more)].
Again fully restart as a new has been linked through pubsepc.yamal.
 Now you should see an image but also overflow pixel error. Because the size of
image is way more than that container can hold thus you need to provide fit:
parameter which takes a BoxFit enum value. BoxFit.cover is a great value because it
respects the boundaries of the container surrounding the image.
[Note - the direct parent should have some height/width. Not that you have a
column wrapped with a container so BoxFit will take the height and width of
container because it is not the direct parent. Direct parent Is column which doesn’t
have a height or width].

Sizedbox() Widget

Sometimes we just need to provide some space in-between for which we can use container
but there is another widget which specializes in it i.e. SizedBox() because it has few
paramters as compared to container and is mainly used for spacing only.
It does take a child widget but is optional so it can be used to just displays empty space
and it has a height and width attribute which can provide the amount space we would
like. The syntax of it as follows –

(Main difference with container is that – Search)

(DART) LIST METHODS – generate() & fold()


Two common methods of dart, which are very important because these give very common
operations done by loop (and thus reduce the code lenght), are described below –

 List.generate() – Returns a list with initialized values (done by the generator function
passed in it which decides the values to be generated).
More formerly - Creates a list with [length] positions (1st argument) and fills it with
values created by calling [generator] (2 nd arg) for each index in the range 0 … length -
1 in increasing order. Ex -

List.generate(7, (i) => i);

The above will generate a list of 7 values in which each element will hold its position
inside the list as its value. (For this you would have needed a loop code).

 <List_Name>.fold() - Reduces a collection to a single value by iteratively combining


each element of the collection with an existing value
Uses [initialValue] (1st argument) as the initial value, then iterates through the
elements and updates the value with each element using the [combine] function (2 nd
argument). Example of calculating the sum of an Iterable –

iterable.fold(0, (sum, element) => sum + element);

 <List_Name>.where() – This returns a new lazy [Iterable] with all elements that
satisfy the predicate [test] (1st and only argument). This predicate has to be a
function call-back which returns a Boolean value to indicate if this element (given in
argument) has to included in the new list or not. Given example returns a list which
includes dates which are after certain date – using where() [_transactions is a list of
objects. The object has a date property] –

_transactions.where((element) =>
element.date.isAfter(DateTime.now().subtract(Duration(days: 7))))
        .toList();

Stack() Widget

This widgets stacks one widget on top of another widget with the first widget in the list of
widget (specified using - children: []) being the bottommost widget.

FractionallySizedBox() Widget

This gives a box whose height and width are fraction of the height of its parent. This igiven
by specifying two named prameters – heightFactor and widthFactor.
Now as the height and width will be fractions of parent height the value of each factor
should be between 0 and 1 (0 being height equal to its parent and 1 being no height).

Note – This function only provides height and width factors. There is no parameter for
height or width of the container itself i.e. it will be totally based on parent height. As such
the parent container must have some height and width defined or else this will cause
error.

Flexible() Widget (GO DEEP IN IT STILL YOU KNOW ABOUT THIS THEORATICALLY ONLY)

(NOTE - THIS WIDGET CAN BE VERY TRICKY SO YOU NEED TO PLAY WITH THE WIDGETS
AND THEIR PARAMTERS TO DEEPLY UNDERSTAND WHAT YOU DO. GO DEEP IN THIS)

Now this widget forces a child widget inside a Column() or Row() to take up as much space
as much is provided to it using flex: attribute. To use this wrap each child of column() or
row() inside a Flexible() widget.
Two main named parameters you learned about are –
1. flex: – as stated above tells the amount to be take by the widget wrapped inside
Flexible(). This actually defines the ratio in which the space will be devided amost
the children wrapped with Flexible.
If flex: is not used then implicitly all the containers are provided value as 1 which
means they must take up same space
2. fit: - How a flexible child is inscribed into the available space.
If flex is non-zero, the [fit] determines whether the child fills the space the parent
makes available during layout. If the fit is [FlexFit.tight], the child is required to fill
the available space. If the fit is [FlexFit.loose], the child can be at most as large as
the available space (but is allowed to be smaller if the size of the child’s contents
are smaller).

Expanded() is just Flexible() with fit: set to FlexFit.tight and thus you don’t have a fit:
parameter defined for this widget.

Get a Look at Examples here

FittedBox() Widget

This Creates a widget that scales (so it will shrink or expand the child to take the full space
of the box) and positions its child within itself so that the box takes as much space as it is
allowed to take.
The way in which the content can be scaled to fit the box is provided using fit: named
parameter. The default value is BoxFit.contain if not provided. You can see what it means
and other values that can be provided with their meaning using the IDE.

Chart in Expensor

Now we will build the chart which shows what part is of the total spending is the spending
on a particular day. This can be done using simple widgets already taught but you can
download external packages which can be used to build more complex charts. The main
steps -
 The container that will hold all the bars will be a card.
 The card will have 7 bars, one for each weekday. Each Bar will have 3 components –
a. The expenditure on the particular day
b. A cylinder (test tubeish) to show the percent the above expenditure is of
the total expenditure in the last 7 days – the method to make it is described
-
 The main logic for creating the cylinder showing the expenditure
percent is that we will use Boxes.
 First one will be of a constant size depicting the cylinder itself – this can
be done easily by just using a container and giving it some colour and/or
border using BoxDecoration().
 On top of above will be the 2 nd box which will be coloured according to
the percent/fraction of the total expenditure on a particular day. This
cn be done in two way – either by using another container whose height
will be the fraction of height of the parent container or using
FraSizedBox().
c. Initials of the week day the above day is related to

We will be using the following new widgets for above task (all of which are described in
detail above) –

1. Stack() widget - placing one box on top of other we will use


2. FractionallySizedBox() widget – for colouring the top most container according to
the fraction/percent of expenditure (on a day) of the total expenditure.
3. Flexibale() widget – to provide equal space for each bar.
4. FittedBox() widget - to fit the text of total expense amount on a day in the given
box.

ListTile() Widget
This is a real code saving and useful Widget. Whenever we use ListView Builder mostly a list
element will be an element which will have a leading element then some title giving info
about the element and a subtitle giving additional info about element and at the end some
additional info about the element. Example of the ListTile used in our project is –

Leading: - leading show the object which will come in the ListTile element in the
above case the Circle with price
Title: - the info about what the transaction is given using this parameter. Like we
bought Gita in this transaction.
Subtitle: - additional info is that we have bought it on 31 st July and as it is additional
info, it is in shorter and greyer text.
Trailing: - gives the widget which will be shown at the end and this is usually a delete
icon but it can be any widget.

Showing date picker using showDatePicker() method

Just like to show modal sheet there is a method, to show a date picker also there is a
method which is named showDatePicker(). Below is the description from docs with red
coloured text your own explanation.

Shows a dialog containing a Material Design date picker.


The returned [Future] resolves to the date selected by the user when the user confirms the dialog.
If the user cancels the dialog, null is returned [Now future is special kind of class which returns an
object to whom value will be provided in future. So that is why this is the perfect object returned
by showDatePicker because date can be picked anytime by the user in future. When the date is
picked – to know which date is picked we can use the method then() of Future class it describes
the task(s) to do whenever the user choses the date value in future. This is programmatically
done by passing a function refrence (for callback) inside the then() argument. This function
describes the code that will be executed once the user selects a data or in other words when the
value is provided to future object.]
When the date picker is first displayed, it will show the month of [initialDate], with [initialDate]
selected.
The [firstDate] is the earliest allowable date. The [lastDate] is the latest allowable date.
[initialDate] must either fall between these dates, or be equal to one of them. For each of these
[DateTime] parameters, only their dates are considered. Their time fields are ignored. They must
all be non-null. [This describes about the required named parameters for the datepicker]

CircleAvatar() Widget
This creates a circle like container (obviously the features of this are really less when
compared with container but container can’t be circular. You can give container a circular
border but it will still be square in reality just the appearance changes externally. You can
test this by fitting a text inside that container. Now wrap the text with Fitted box and you
will see the text will go outside the border because the container is still square in reality just
the appearance of it changes and thus for the fitted box also the container is square and it
will try to fit the contents in the square box not in the circle seen on screen).

Typically used with a user's profile image, or, in the absence of such an image, the user's
initials. To use this is easy – provide the widget you want to show inside the circle in its
child: parameter. Additionally, you can change the radius by radius: and provide a colour
also.

(DART) Truly Immutable Objects/Compile Time objects (GO IN DEEP BY SEARCHING ON

WEB)–

YOUR TRIED ATTEMPT TO EXPLAIN THE CONCEPT USING COURSE DEFINATION BUT THIS IS
MAY BE WRONG AS COURSE ITSLEF DOSEN’T EXPLAIN IT WELL

Immutable objects are those objects whose instance variables values can’t be changed in
any possible way. Now this is done by making –
a. the constructor of the class as const along with
b. making every property as final and
c. The object itself should be const.
[Note – We have to meet all the given conditions then only we can create a truly immutable
object –
If condition a. is not met then then compiler won’t know that this is an immutable object
eve thought its values are final and thus will call the constructor again
If condition b. is not met then we can change the values using a member method so it is not
truly immutable
If condition c. not met then we can provide the object a new value by calling the constructor
again with different value which will create a new object and the values of the instance
variable will change]

The advantage and use of immutable objects/compile time objects are that flutter can
simply use the object that is first assigned and use it every time build is called. A new
object won’t be created as flutter knows that the object itself is constant and its value
can’t be changed in any possible way with each new build() call.
*
NOTE – The variable should also be const/final or else the variable can itself be provided a
new object and thus flutter will have to create a new object. To understand look at below
example –

Example – Let’s take a class as follows –

Ways to improve code (In Section 6 – Widget and flutter internal deep dive) –

 (readability) To separate each widget code into different files (video 143).
 (readability) using builder methods to separate iOS specific code from Android code
(Video 144).
 (performance) using const objects via const constructors to create immutable
objects (video 138 – 141) To understand more on why do this look here -
https://flutter.dev/docs/resources/inside-flutter
https://www.youtube.com/watch?v=wE7khGHVkYY
https://www.youtube.com/watch?v=AqCMFXEmf3w

Context & Inherited Widget (Video 148) –

Context object holds the data about every widget so that when we need data of some
widget, we get without passing a reference in the constructor.
https://www.youtube.com/watch?v=Zbm3hjPjQMk

Keys (Videos 149 - 151) –


Key is used to link element (element tree) with widgets (widget tree) correctly. This is used
when there is a stateful widget in which a state may change but because element tree
links the widgets according to their type (like a container, column etc is a type of widget) if
there are multiple widgets of same type and one of them gets deleted then the state data
may change (How is given in video 150.). Key helps in correctly linking using a unique ID.
Resources apart from course - https://www.youtube.com/watch?v=kn0EOS-ZiIc

App & Widget Lifecycle (Video 145-148) –


Resources apart
from course – https://medium.com/pharos-production/flutter-app-lifecycle-4b0ab4a4211a
SHOP APP

GridView.builder()

This like list view builder builds a grid view (a view with square brackets) and only loads
those elements which are visible on the screen.

This has following named arguments which are important –


a) gridDelegate: - this controls the layout of the children within the [GridView] and
takes a SliverGridDelegate (mostly SliverGridDelegateWithFixedCrossAxisCount)
object to do so.
b) itemBuilder: this takes a function (called through call-back) which should return the
widget which will be build as a grid element in the grid view. The function takes 2
argument (which will be passed through it by flutter) – the BuildContext and index
representing each grid element (so if index is 2 means it represents the 3r grid
element in the grid)
c) itemCount: - this tells the how many items or grid elements are to be built in the
app which helps the GridView to estimate the maximum scroll extent.
itemBuilder: will be called only with indices greater than or equal to zero and less
than itemCount.
If the item count is not provided then there will be a lot of grid boxes with error
that index is out of range because then itemCount will be infinite maybe and the
itemBuilder will go on producing grids because the index is from 0 to itemCount.

GridTile()

It is a built-in widget which allows us to build a grid element in GridView. This has named
parameters –
a) child: - to provide the widget [mostly an image] which will be covering the whole
GridTile
b) footer: - to provide info at the bottom of the tile about the child widget
c) header: - to provide info at the top of the GirdTile about the child widget.

GridTileBar() [Usually used with footer or hearder named parameter]

This widget builds a bar which is usually accompanied in a Grid element to display info
regarding the child widget contained in the Grid Element. It is similar to ListTile as it also
has leading, trailing, title, and subtitle named paramters.
ClipRRect()

This stands for clip Rounded Rectangle and thus this clips a rectangle in a rounded shape to
give it a rounded effect. Its 2 main named arguments are –
a) child: - defines the child widget whose border will be clipped in circular shape.
b) borderRadius: – defined how much rounded border curves you want for the widget
given above.

GestureDetector()

Creates a widget that detects gestures. The child widget (provided thorugh the
child:named paramters) is actually the widget which will listen for the gestures.
There are different gestrues (like onTap, onLongPress etc) described in for of named
parameters.
Tasks that will be done as soon as a gesture is detected by the child widget – is defined by
the function which is passed as value to the named argument of that gesture.
below is a code snippet to demonstrate the use of this widget. The below code will print 10
in the console as many times the user taps in the image –

GestureDetector(
        onTap: () => print('10')

        child: GridTile(
          child: Image.network(
            imageUrl,
            fit: BoxFit.cover,
          ));

Navigator

A widget that manages a set of child widgets (pages/screens) with a stack discipline.
Mobile apps typically reveal their contents via full-screen elements called "screens" or
"pages". In Flutter these elements are called routes and they're managed by a [Navigator]
widget. The navigator manages a stack of [Route] objects and provides two ways for
managing the stack, the declarative API [Navigator.pages] (not taught thus far in course) or
imperative API [Navigator.push] and [Navigator.pop].

Imperative API -
Imperative means which is given implicitly/automatically and declarative means those for
which has to be defined explicitly.
Now even though we can create declarative API it's most common to use the imperative
navigator created by the [Router] which itself is created and configured by a [WidgetsApp]
or a [MaterialApp] widget. You can refer to that navigator with [Navigator.of].

A [MaterialApp] is the simplest way to set things up. The [MaterialApp]'s home becomes the
route at the bottom of the [Navigator]'s stack. It is what you see when the app is launched.
void main() {
runApp(MaterialApp(home: MyAppHome()));
}

Navigator.push(), Navigator.pop() using MaterialPageRoute() -


To push a new route on the stack you can create an instance of [MaterialPageRoute] with a
builder function, provided in named argument builder: of push(), that creates whatever you
want to appear on the screen. For example:

The route defines its widget with a builder function instead of a child widget because it will
be built and rebuilt in different contexts depending on when it's pushed and popped.
As you can see, the new route can be popped, revealing the app's home page, with the
Navigator's pop method:
Navigator.pop(context);

Routes can return a value


When a route is pushed to ask the user for a value, the value can be returned via the [pop]
method's result parameter.
Methods that push a route (i.e. a MaterialAppRoute()) return a [Future]. The Future resolves
when the route is popped and the [Future]'s value is the [pop] method's result parameter.
If the user presses 'OK' then value will be true. If the user backs out of the route, for
example by pressing the Scaffold's back button, the value will be null.
When a route is used to return a value, the route's type parameter must match the type of
[pop]'s result. That's why we've used MaterialPageRoute<bool> instead of
MaterialPageRoute<void> or just MaterialPageRoute. (If you prefer to not specify the types,
though, that's fine too.)

Using named navigator routes using routes table and pushNamed()

Mobile apps often manage a large number of routes and it's often easiest to refer to them
by name. Route names, by convention, use a path-like structure (for example, '/a/b/c'). The
app's home page route is named '/' by default.
The [MaterialApp] can be created with a [Map<String, WidgetBuilder>] which maps from a
route's name to a builder function that will create it. The [MaterialApp] uses this map to
create a value for its navigator's [onGenerateRoute] callback.

To show a route by name we use the following syntax:

Navigator.pushNamed(context, '/b');
NOTE – Instead of hard quoting the name of the route in a string and use it at different
places, we can create a static variable in the widget which creates the route/screen (main
Class in the <screen_name>.dart file) and use that everywhere so that if we change name
of route we have to do it at just one place. (The example of this can be seen in SHOP app
main.dart, product_items.dart(given below), product_detail_screen.dart(given below)
[video 188 in course]).

Taking arguments through ModalRoute


Now we can pass the arguments which we pass through the constructor, using the
arguments: named parameter. This takes an object so if we have a single argument, we can
pass it as it is to the argument: parameter as value. But if there are multiple values which
we want to pass then we have to pass them all through a map as the argument takes a
single Object value which map is and it also allows to store the arguments which we want
to pass inside it.
We can pass the arguments in the following way using map –

class ProductItem extends StatelessWidget {
  final String name = 'nikhil';
  final String id = 'p1';
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => Navigator.of(context).pushNamed(
          ProductDetailScreen.routeName,
          arguments: {'name': name, 'id': id}),//MAIN LINE - PASSED ARGUMENT
      child: Text('Tap to pass arguments'),
    );
  }
}

Now the screen in which we want to access the arguments we can access it through the
following code line (whole code of the screen is given below the line) –

final map = (ModalRoute.of(context).settings.arguments as Map<String, Object>)

class ProductDetailScreen extends StatelessWidget {
  static const routeName = '/product-detail';

  @override
  Widget build(BuildContext context) {
//MAIN LINE – TO ACCESS THE ARGUMENTS using map variable
    final map =
        (ModalRoute.of(context).settings.arguments as Map<String, Object>);
    final name = map['name'];
    final id = map['id'];
    return Scaffold(
      appBar: AppBar(
        title: Text('$name($id)'),
      ),
    );
  }
}

State Management (Using Provider package) [videos 189 – 191 & 193 - 196]

We need states (data members which effect the UI and which might change over time.
App-state is a state which effects the entire app or large parts of the app [like
authentication status of user. It is needed across entire app] whereas Local/Widget state is
a state which effects that widget itself. We need state management (using provider
package) for app state. Local/widget state can be managed through Stateful widgets using
setState()) in different widget. There are 2 methods to manage app-state -

(NOTE - state in this topic will mean app-state)

1. Through Constructor - first way is to pass the state (app state to be specific) through
the constructors to all the widgets that need the state. But it has the following
disadvantages -
i. Becomes cumbersome in larger apps because it may happen that we are just
passing data which is not required by a widget A but just because widget A is
in turn calling widget B which needs that data, we need to pass it through the
constructor of Widget A.
ii. Impacts performance if our states are in a central file like main.dart. Because
if any change happens to even one of the widgets in main.dart file the whole
widget tree is rebuilt (that many a times means the whole app because the
routes/screens are also present in the main.dart and thus they are also
rebuild even though they have no connection with the change and don’t
need to be rebuild) even though we don’t need it.

2. Using Provider Package - The other and the preferred way is to use the provider
package (It is an external package to be installed through the pubspec.yaml) which
provides a data container (or sometimes called data provider. This data container is
nothing but an object of Provider Class) which can be attached to any widget in our
app (in the below ex it is MyApp but it can be any widget).
Now all the children of MyApp can listen to changes in this container by using a
listener to this container and thus access and react to changes to the data in the
container (this data is our app-state. So, in this section data = app-state).
Now because it is a listener, whenever the data (app-state) in the container
changes the widgets which are listening to it, their build() is called. And now this
does not only solve the problem of passing the data through constructor but also
overcomes the performance problem because only those widgets will be rebuilt
which are listening to the container and not the other children (which are not
listening to the container). Like in the below only SingleProduct will be rebuild and
others – Products, ProductDetail, and Cart will not be rebuilt.

Provider Package - Programmatic Implementation

 You saw the theory about Provider package in previous section. Now the actual
implementational details are provided here –

a. Install provider – Firstly we have to install the provider package which is a 3rd party
package and thus its specifications are to be provided in the pubspec.yamal file.

b. Making a Provider Class (Using ChangeNotifier class & notifyListeners()) – To make


a provider class, we need to create a separate file with a class which will store the
state which we want to store in the container. Like in this example it is the list of
products we display in Overview screen. This class will be our provider and thus we
store this file in the providers folder.

Now the provider package gives us the ability to notify listeners (of the instance of
provider class) about the changes that happen to the data inside the instance. But
how? Notifying listeners about changes is done via the notifyListeners() methods
which is provided in the ChangeNotifier Class.

Now whatever change we do to the data, after the change, we have to call the
notifyListeners() method so that flutter can tell all the listeners of our provider class
instance that the data has changed.
Note – This is like the setState() - so any state (data) changed without calling
notifyListeners() will not result in any change (rebuild) to the listeners as they won’t
be notified by flutter that the state (data) is changed. Flutter notifies listeners only
via notifyListeners() and thus be careful that the state can’t be changed from
outside the provider class. This is done by making the state private and also when
retuning values through getters - return the values by copying it in a new variable
and not directly returning the state member as doing it will return the reference of
the state and thus any change outside will also change the value of state that too
without notifyListeners(). This is why in the app code we are returning […_items] as
this is copying the values of _items into a new list and that list is being returned and
not the reference to _items.

So, we have to use ChangeNotifier as a mixin to the class in which we keep our
state so that we can use notifyListeners() inside our class to change the state. The
example of the code is given below -

c. Setting up the Listeners (using ChangeNotifierProvider and [Provider.of<>() or


Consumer<>()] ) – Now that we have stored our state in provider let’s setup the
listeners. There are 2 steps for setting up a listener as given below -

1) To attach the provider object to a class –

Now the class(es) which can listen to a provider object are children of the class to
which we have attached this provider object (explained in the above section).
So, we attach this provider object to the MyApp Class because we need the list of
products (our state stored in the provider) in the ProductsOverviewScreen and
ProductsDetailScreen both of which are children of the MyApp.
So, to attach provider object to MyAPP, we wrap the MyApp class with the
ChangeNotifierProvider which is provided to us by the provider package so first
import the package in the file. This is shown below -

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (ctx) => Products(),
      child: MaterialApp(
        title: 'MyShop',

Now the above allows us to attach an instance of our provider class, to MyApp, using
create: named argument which takes a function. The Function takes a BuildContext
as argument and returns an instance of the provider class (in our case the Products
class). Changes to this returned instance will be listened by child widgets (NOTE –
Changes to only this instance (returned from inside the ChangeNotifierListener)
will be listened. Changes that happen in any other instance of the provider class
are not listened. This is explained in detail with code here).

Now the child widgets of MyApp can listen and whenever the data in the provider
instance (i.e. our state) changes, the child widgets (which are listening) are rebuild.
Note – when you have to create a new instance of the provider class use the above
approach (i.e. ChangeNotifierProvider with create: named argument) but in some
cases, like when working with lists or girds, you may use an instance of provider
class which is already instantiated and thus not create a new object. In that case,
use the ChangeNotiferProvider.value() constructor and in it provide the already
created instance to value: named parameter. This is shown in the
products_grid.dart file or video 196 in course. A code snippet is shown below –

GridView.builder(
      padding: const EdgeInsets.all(10),
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2, crossAxisSpacing: 10, mainAxisSpacing: 10)
,
      itemCount: products.length,
      itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
        value: products[i], //not a new object
        child: ProductItem(),
      ),

2) To make a child listen –

Method 1 – provider.of<>()
we need to create a communication channel between the provider instance we set
up in the ChangeNotiferProvider and the child widget which needs the data and
wants to become a listener. This we do using the following line of code (given in
product_grid.dart file) in the child widget (which wants to listen) –

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

.of() is used to set up a direct communication channel and can be only used if the
widget in which we are using this .of() is direct or indirect child of a widget which
sets up the Provider Class on which we are calling this .of() method.
Note – The widget that is going to call Provider.of() “needs” to be a direct or indirect
child of a class which is already a child of ChangeNotifyProvider class [it inherits
Provider class]. In the above case also, we use this code line in
ProductsOverviewScreen which is child of MyApp which is a child of
ChangeNotifyProvider (This inherits Provider class and thus we can use
Provider.of()).

As we can have multiple provider objects (Each obj will be from a different provider
class), we need to tell to which provider object we want to set up the connection.
This we do by specifying the provider class name (Products in our case), to whose
object we want to connect, inside the angular brackets as follows –
.of<Products>(context)
By the above line, as we use Products in angular braces, we tell flutter to connect to
the object which is of the Products provider class.

Method 2 – Consumer<> widget [video 197] (More Advantageous)


Instead of calling Provider.of() at start of build method, we can wrap the child
widget, which wants to listen to the changes, into a widget called Consumer<>. In
the angular brackets (‘<>’) we should provide the name of the provider class,
changes to whose object, we want to listen to (in our case it is the Products class).
So, this is similar to Provider.of() in the aspect that inside the angular brackets of of()
method also, we provided the name of the provider class, changes to whose object,
we want to listen to. We wrap child widget by returning it from a function which is
provided to builder: named argument. The code example is given below –

class ProductsGrid extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<Products>(builder: (context, productsOB, child) {
      final products = productsOB.getProducts;
      return GridView.builder(
//Code to display individual grid items
        padding: const EdgeInsets.all(10),
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,crossAxisSpacing: 10,mainAxisSpacing: 10)
,
        itemCount: products.length,
        itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
          value: products[i],
          child: ProductItem(),
        ),
      );
    });
  }
}

As you can see the GridView is wrapped in Consumer<> widget (the function,
provided to builder, returns the GridView widget) so that if any product gets added
in the list of products, they are shown in the GridView also. Also note that the
function takes 3 arguments –
a. ctx - BuildContext (as usual),
b. productsOB - Instance of the provider class to which we will listen,
c. child – this parameter provides that part of the widget which will remain
constant and doesn’t need to be rebuild. The part which will not rebuild is
provided inside the Consumer object through child: named parameter
(Example of this can be seen at 5:37 in video 197).

Advantages of Consumer<> over Provider.of<>() –


In Provider.of() the whole widget will be rebuilt so the build method will be called
again even if some parts of the widget may not be needing the rebuild. Now this can
be overcome by separating the part which will not be rebuild into a separate widget
but instead we can use Consumer<> which will allow us to wrap only that widget
which we want to rebuild and the other code part, which will be outside Consumer
won’t be rebuild (thus won’t need to separate widgets into seperate files).

 Setting up / Attaching multiple provider instances to a single class (using


MultipleProviders()) –
In the above section we attached a Products data container (i.e. a Products object) to
MyApp using ChangeNotiferProvider but what if want to attach another different data
container (let’s say of Carts Class)? i.e. how can we attach multiple provider instances to
MyApp?
We can do the above with MuiltpleProviders() [by using named argument Providers:
and giving it the list provider instance you want attach] in the following way –

 To access the members inside the Provider Container - There are 2 ways to access data
in Provider Class –
a. Using Provider.of() - Now the above line of code using Provider.of() will not only
make the child class a listener but loadedProducts will also become an object of
the Products class which can be used like normal objects to call the non-private
methods or properties inside the Products provider class.
NOTE – we can also use Provider.of() simply to create an object and not to set up a
listener by using listen: named argument which takes a bool value. Default is set to
true (that is why the Provider.of() by default makes the child class a listener) but if
we set it to false then only an object will be created and the widget (in which we
use Provider.of()) won’t be registered as a listener i.e. the widget won’t be rebuild
even if any data changes in the provider container class. The example is shown
below -

final productsOB = Provider.of<Products>(context, listen: false);
Now this will make productOB only an object and not a signal to create the widget a
listener and thus using this line will not make a rebuild even if the data inside
Products changes.
b. Using the Instance of the Provider Class – now as the provider container is a class,
we can create an instance of the class by simply importing the file in which the class
is stored and create its object, as we normally do, to access data members or
methods inside the class. The only downside is that it will not make the widget a
listener for that we will have to use Provider.of().

 Changes to only the instance attached using the ChangeNotifierProvider are listened –

 Providing non-object or constant values though provider container

Typically, when working with the Provider package, you provide objects based on your
own custom classes.

This makes sense because you can implement the  ChangeNotifier  mixin in your classes to
then trigger  notifyListeners()  whenever you want to update all places in your app that
listen to your data.

But you're not limited to providing objects - you can provide ANY kind of value (lists,
numbers, strings, objects without  ChangeNotifier  mixing, ...).

Example:

Provider<String>(builder: (ctx) => 'Hi, I am a text!', child: ...);


Of course, if you're using Provider Package v4 or greater, it would be  create: ...  instead
of  builder: ...

You might wonder, how this text can change though - it's a constant text after all. It
certainly doesn't implement the  ChangeNotifier  mixin (the  String  class, which is built-into
Dart, indeed doesn't - just like numbers, booleans etc.).

It's important to note, that the above snippet uses  Provider , NOT  ChangeNotifierProvider .
The latter indeed only works with objects based on classes that use the ChangeNotifier
mixin. And this is the most common use-case, because you typically want to be your
global data changeable (and have the app UI react to that).

But in case you just want to provide some global (constant) value which you can then
conveniently use like this:

print(Provider.of<String>(context)); // prints 'Hi, I am a text!'; does never


update!

(DART) INHERITENCE VS MIXIN

PopupMenuButton
PopupMenuItem
Defining Enum

Dismissable() – key required. Provides direction for swipe

Using show keyword with imports to import only a particular class

Using as with imports to import a name

You might also like