0% found this document useful (0 votes)
101 views106 pages

FLUTTER

Uploaded by

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

FLUTTER

Uploaded by

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

FLUTTER

BCA-III

SOUNotes

RR

SOUNotes
UNIT-01 Introduction
Introduction to Flutter
Flutter is framework created by Google.
It is an Open-Source cross-platform framework used to develop application for
Android, iOS, Web, Desktop
Dart Programming language is used for Flutter

Why Flutter?
Flutter is Open-Source framework Therefore; anyone can use it for any given purpose.
Flutter is so fast that it takes less than 30 sec for first full compilation.
Comes with Hot-Reload & Hot-Restart.
Due to Stateful widget hot-reload feature Flutter is very fast iterative coding style.
Ready to use widget, Flutter has many widgets that you can use to build flutter
application.
Flutter Community is bit small if we compare with other framework like React.
But Flutter is growing very fast then other framework.
Anyone who have basic knowledge of OOPS & UI Designing can easily learn Flutter.

Features of Flutter
Open-Source:
Flutter is a free and open-source framework for developing mobile applications.
Cross-platform:
This feature allows Flutter to write the code once, maintain, and can run on
different platforms.
It saves the time, effort, and money of the developers.
Minimal Coding.
Hot Reload:
Whenever the developer makes changes in the code, then these changes can be
seen instantaneously with Hot Reload.
It means the changes immediately visible in the app itself.
It is a very handy feature, which allows the developer to fix the bugs instantly.
Hot Reload & Hot Restart.
Accessible Native Features and SDKs:
This feature allows the app development process easy and delightful through
Flutter's native code, third-party integration, and platform APIs.
Thus, we can easily access the SDKs on both platforms.
Minimal code:
Flutter app is developed by Dart programming language, which uses JIT and AOT
compilation to improve the overall start-up time, functioning and accelerates the
performance.

3
JIT enhances the development system and refreshes the UI without putting extra
effort into building a new one.
Widgets:
The Flutter framework offers widgets, which are capable of developing
customisable specific designs.
Most importantly, Flutter has two sets of widgets: Material Design and Cupertino
widgets that help to provide a glitch-free experience on all platforms.

Advantages of Flutter
It makes the app development process extremely fast because of the hot-reload
feature. This feature allows us to change or update the code are reflected as soon as
the alterations are made.
It provides the smoother and seamless scrolling experiences of using the app without
much hangs or cuts, which makes running applications faster in comparison to other
mobile app development frameworks.
Flutter reduces the time and efforts of testing. As we know, flutter apps are cross-
platform so that testers do not always need to run the same set of tests on different
platforms for the same app.
It has an excellent user interface because it uses a design-centric widget, high-
development tools, advanced APIs, and many more features.
It is similar to a reactive framework where the developers do not need to update the
UI content manually.
It is suitable for MVP (Minimum Viable Product) apps because of its speedy
development process and cross-platform nature.

Disadvantages of Flutter
The Flutter is a comparatively new language that needs continuous integration
support through the maintenance of scripts.
It provides very limited access to SDK libraries. It means a developer does not have
a lot of functionalities to create a mobile application. Such types of functionalities
need to be developed by the Flutter developer themselves.
It uses Dart programming for coding, so a developer needs to learn new technologies.
However, it is easy to learn for developers.

Flutter Installation in Windows


System Requirements:
Operating System: Windows 7 or Later (You can also use Mac or Linux OS.).
Disk Space: 400 MB (It does not include disk space for IDE/tools).
Windows PowerShell
Git for Windows
SDK: Flutter SDK for Windows
IDE: Android Studio (Official)

4
Install Git:
Step-01: To download Git
Step-02: Run the .exe file to complete the installation. During installation, make
sure that you have selected the recommended option.
Install Flutter SDK:
Step-01: Download the installation bundle of the Flutter Software Development Kit
for windows. To download Flutter SDK, Go to its official website, click on Get
started button
Step-02: Next, to download the latest Flutter SDK, click on the Windows icon.
Here, you will find the download link for SDK.
Step-03: When your download is complete, extract the zip file and place it in the
desired installation folder or location.
Step-04: To run the Flutter command in regular windows console, you need to
update the system path to include the flutter bin directory. The following steps are
required to do this:
Step-04.01: Go to My Computer -> properties -> advanced tab ->
environment variables .
Step-04.02: Now, select path -> click on edit .
Step-04.03: In the above window, click on New > write path of Flutter bin
folder in variable value click ok.
Step-05: Now, run the flutter doctor command. This command checks for all the
requirements of Flutter app development and displays a report of the status of
your Flutter installation.
Step-06: When you run the above command, it will analyse the system and show
its report. Here, you will find the details of all missing tools, which required running
Flutter as well as the development tools that are available but not connected with
the device.
Step-07: Install the Android SDK. If the flutter doctor command does not find the
Android SDK tool in your system, then you need first to install the Android Studio
IDE. Install by doing following steps:
Step-07.01: Download the latest Android Studio executable or zip file from the
official site.
Step-07.02: When the download is complete, open the .exe file and run it.
Step-07.03: Follow the steps of the installation wizard.
Step-07.04: After Installation is complete, click Next -> Finish . Once the
Finish button is clicked, you need to choose the Don't import Settings option
and click OK . It will start the Android Studio.
Step-08: Next, you need to set up an Android emulator. It is responsible for
running and testing the Flutter application.
Step-08.01: To set an Android emulator, go to Android Studio -> Tools ->
Android -> AVD Manager and select Create Virtual Device . Or, go to Help ->
Find Action -> Type Emulator in the search box
Step-08.02: Choose your device definition and click on Next .

5
Step-08.03: Select the system image for the latest Android version and click
on Next.
Step-08.04: Now, verify the all AVD configuration. If it is correct, click on
Finish
Step-08.05: Last, click on the icon pointed into the red colour rectangle.
Step-09: Now, install Flutter and Dart plugin for building Flutter application in
Android Studio. These plugins provide a template to create a Flutter application,
give an option to run and debug Flutter application in the Android Studio itself. Do
the following steps to install these plugins.
Step-09.01: Open the Android Studio and then go to File -> Settings ->
Plugins .
Step-09.02: Now, search the Flutter plugin . If found, select Flutter plugin
and click install . When you click on install, it will ask you to install Dart
plugin, click yes to proceed.
Step-09.03: Restart the Android Studio.

Flutter Installation in macOS


System Requirements:
Operating System: macOS
Disk Space 2.8 gb (It does not include disk space for IDE/tools).
Install Flutter SDK
Step-01: Download the installation bundle of the Flutter Software Development Kit
for macOS.
Step-02: When your download is complete, extract the zip file and place it in the
desired installation folder or location.
Step-03: To run the Flutter command, you need to update the system path to
include the flutter bin directory. $ export PATH="$PATH:pwd/flutter/bin"
Step-04: Next, enable the updated path in the current terminal window using the
below command and then verify it also. source ~/.bashrc source
$HOME/.bash_profile echo $PATH
Step-05: Now, run the flutter doctor command. This command checks for all the
requirements of Flutter app development and displays a report of the status of
your Flutter installation.
Step-06: When you run the above command, it will analyse the system and the
details of all missing tools, which required to run Flutter as well as the
development tools that are available but not connected with the device.
Step-07: Install the latest Xcode tools if reported by the Flutter doctor tool.
Step-08: Install the latest Android Studio and SDK, if reported by the Flutter
doctor tool.
Step-09: Next, you need to set up an iOS simulator or connect an iPhone device to
the system for developing an iOS application.
Step-10: Again, set up an android emulator or connect an android device to the
system for developing an android application.

6
Step-11: Now, install Flutter and Dart plugin for building Flutter application in
Android Studio. These plugins provide a template to create a Flutter application,
give an option to run and debug Flutter application in the Android Studio itself.

Architecture of Flutter Application


The Flutter architecture mainly comprises of 4 components:

1. Flutter Engine:
It is a portable runtime for high-quality mobile apps and primarily based on the
C++ language.
It implements Flutter core libraries that include animation and graphics, file and
network I/O, plugin architecture, accessibility support, and a dart runtime for
developing, compiling, and running Flutter applications.
It takes Google's open-source graphics library, Skia, to render low-level graphics.
2. Foundation Library:
It contains all the required packages for the basic building blocks of writing a
Flutter application. These libraries are written in Dart language.
3. Widgets:
In Flutter, everything is a widget, which is the core concept of this framework.
Widget in the Flutter is basically a UI component that affects and controls the
view and interface of the app.
It represents an immutable description of part of the user interface and includes
graphics, text, shapes, and animations that are created using widgets.
The widgets are similar to the React components.
In Flutter, the application is itself a widget that contains many sub widgets.
It means the app is the top-level widget, and its UI is build using one or more
children widgets, which again includes sub child widgets.
This feature helps you to create a complex user interface very easily.

7
4. Design Specific Widgets:
The Flutter framework has two sets of widgets that conform to specific design
languages. These are Material Design for Android application and Cupertino
Style for IOS application.

Gestures
It is a widget that provides interaction (how to listen for and respond to) in Flutter
using GestureDetector. GestureDetector is an invisible widget, which includes tapping,
dragging, and scaling interaction of its child widget. We can also use other interactive
features into the existing widgets by composing with the GestureDetector widget.

State Management
Flutter widget maintains its state by using a special widget, StatefulWidget. It is
always auto re-rendered whenever its internal state is changed. The re-rendering is
optimised by calculating the distance between old and new widget UI and render only
necessary things that are changes.

Layers
Layers are an important concept of the Flutter framework, which are grouped into
multiple categories in terms of complexity and arranged in the top-down approach.
The topmost layer is the UI of the application, which is specific to the Android and iOS
platforms.

8
The second topmost layer contains all the Flutter native widgets.
The next layer is the rendering layer, which renders everything in the Flutter app.
Then, the layers go down to Gestures, foundation library, engine, and finally, core
platform-specific code. The following diagram specifies the layers in Flutter app
development.

First Flutter Application


Step-01: Open Android Studio
Step-02: Create the Flutter project. To create a project, go to File -> New -> New
Flutter Project .
Step-03: In the next wizard, you need to choose the Flutter Application. For this, select
Flutter Application -> click Next
Step-04: Next, configure the application details as shown in the below and click on the
Next button,
Project Name: Your application name
Flutter SDK Path: <path to flutter SDK>
Project Location: <path to project folder>
Description: Project description
Step-05: In the next wizard, you need to set the company domain name and click the
Finish button. After clicking the Finish button, it will take some time to create a project.
When the project is created, you will get a fully working Flutter application with
minimal functionality.
Step-06: To run application, go to Run->Run main.dart
9
Step-07: Open the main.dart file and replace the code with the following code
snippets.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My Flutter App',
home: MyHomePage(),
);
}
}

class MyHomePage extends StatelessWidget {


@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(“This is my First Flutter App”),
),
body: Center(
child: Text('Namastey World !',),
),
);
}
}

Step-08: Let us understand the above code snippet line by line.


Here, we have imported a Material package. This package allows you to create
user interface according to the Material design guidelines specified by Android.
The second line is an entry point of the Flutter applications similar to the main
method in other programming languages. It calls the runApp function and pass it
an object of MyApp The primary purpose of this function is to attach the given
widget to the screen.
Line 5 to 18 is a widget used for creating UI in the Flutter framework. Here, the
StatelessWidget does not maintain any state of the widget. MyApp extends
StatelessWidget that overrides its build The build method is used for creating a
part of the UI of the application. In this block, the build method uses MaterialApp,
a widget to create the root level UI of the application and contains three
properties - title, theme, and home.
Title: It is the title of the Flutter application.
Theme: It is the theme of the widget. By default, it set the blue as the overall
colour of the application.

10
Home: It is the inner UI of the application, which sets another widget
(MyHomePage) for the application.
MyHomePage is similar to MyApp, except it will return the Scaffold Scaffold
widget is a top-level widget after the MaterialApp widget for creating the user
interface. This widget contains two properties appBar and body. The appBar
shows the header of the app, and body property shows the actual content of
the application.
AppBar render the header of the application, Center widget is used to center
the child widget, and Text is the final widget used to show the text content
and displays in the center of the screen.
Step-09: Now, run the application. To do this, go to Run -> Run main.dart

Flutter Folder Structure

11
.idea :
This folder is at the very top of the project structure, which holds the
configuration for Android Studio.
It doesn't matter because we are not going to work with Android Studio so that
the content of this folder can be ignored.
android :
This folder holds a complete Android project and used when you build the Flutter
application for Android.
When the Flutter code is compiled into the native code, it will get injected into this
Android project, so that the result is a native Android application.
For Example: When you are using the Android emulator, this Android project is
used to build the Android app, which further deployed to the Android Virtual
Device.
ios :
This folder holds a complete Mac project and used when you build the Flutter
application for iOS.
It is similar to the android folder that is used when developing an app for Android.
When the Flutter code is compiled into the native code, it will get injected into this
iOS project, so that the result is a native iOS application.
Building a Flutter application for iOS is only possible when you are working on
macOS.
lib :
It is an essential folder, which stands for the library. It is a folder where we'll do
our 99 % of project work.
Inside the lib folder, we will find the Dart files which contain the code of our
Flutter application.

12
By default, this folder contains the file main.dart , which is the entry file of the
Flutter application.
test :
This folder contains a Dart code, which is written for the Flutter application to
perform the automated test when building the app. It won't be too important for
us here.

We can also have some default files in the Flutter application. These files are:

.gitignore :
It is a text file containing a list of files, file extensions, and folders that tells Git
which files should be ignored in a project. Git is a version-control file for tracking
changes in source code during software development.
.metadata :
It is an auto-generated file by the flutter tools, which is used to track the
properties of the Flutter project.
This file performs the internal tasks, so you do not need to edit the content
manually at any time.
.packages :
It is an auto-generated file by the Flutter SDK, which is used to contain a list of
dependencies for your Flutter project.
my_app.iml :
It is always named according to the Flutter project's name that contains
additional settings of the project.
This file performs the internal tasks, which is managed by the Flutter SDK, so you
do not need to edit the content manually at any time.
pubspec.lock :
It is an auto-generated file based on the .yaml file. It holds more detail setup
about all dependencies.
pubspec.yaml :
It is the project's configuration file that will use a lot during working with the
Flutter project.
It allows you how your application works.
This file contains: Project general settings such as name, description, and version
of the project, Project dependencies, Project assets (e.g., images)
README.md :
It is an auto-generated file that holds information about the project. We can edit
this file if we want to share information with the developers.

Flutter Application Architecture With Layered Approach


Flutter applications can benefit from a layered architecture, which separates concerns and
promotes code maintainability.

13
1. Presentation Layer (UI):
This layer deals with how the app looks and feels.
It utilizes Flutter's widgets to build the user interface (UI) elements.
This layer is built using stateless or stateful widgets depending on data
requirements.
It interacts with the application layer to request data or trigger actions.
2. Application layer (Business Logic):
This layer handles the core business logic of the app. It manages user interactions,
data validation, and application flow.
It interacts with the domain layer to access and manipulate data relevant to the
app's functionality.
This layer might use state management solutions like Provider, BLoC, or Scoped
Model to manage application state.
3. Domain Layer (Models & Rules):
This layer represents the core domain concepts and rules of the app.
It defines data models (classes) that represent entities like users, products, or
tasks.
It encapsulates business logic rules specific to the app's domain
This layer interacts with the data layer to fetch and store data.
4. Data Layer (Persistence):
This layer handles data persistence and retrieval.

14
It interacts with local databases (e.g., Hive, SQlite) or remote APIs (e.g., using
HTTP libraries) to access and store data.
It retrieves and stores data based on requests from the domain layer.
This layer should strive for data abstraction, allowing switching between data
sources without affecting the rest of the app.

Questions
1. What is Flutter? Explain features of Flutter.
2. Write advantages and disadvantages of Flutter.
3. Name applications which are developed using Flutter.
4. Discuss architecture of Flutter application.
5. Explain File and Folder structure of Flutter application

15
UNIT-02 Flutter Basics
Widgets
In Flutter, widgets are the building blocks of a Flutter app. Everything you see on the
screen of a Flutter app is a widget. They are like small pieces of a puzzle that you put
together to make a complete picture.

Lifecycle of a StatefulWidget
In Flutter, a StatefulWidget is a widget that can change its state during its lifetime.
This means it can rebuild itself multiple times, reflecting changes in its internal state or
external data. Understanding the lifecycle of a StatefulWidget is crucial for
managing resources, performing actions at the right time, and ensuring optimal
performance. The lifecycle of a StatefulWidget involves the following stages:

1. `createState()
1. Purpose: This method is the first step in the lifecycle. It is called when the
StatefulWidget is created. It returns an instance of the State class associated
with the StatefulWidget .
2. Key Point: This method is called only once.

@override
_ExampleState createState() => _ExampleState();

2. initState()

17
Purpose: This method is called once when the State object is inserted into the
widget tree. It is used for initializing any data or state the widget needs. For
example, initializing variables, setting up streams, or starting animations.
Key Point: Always call super.initState() to ensure the parent class is also
initialized properly.

@override
void initState() {
super.initState();
// Initialization code here
}

3. didChangeDependencies()
Purpose: This method is called immediately after initState() and whenever the
widget’s dependencies change. Dependencies can change if the widget depends
on an InheritedWidget that itself changes.
Key Point: Useful for actions that depend on the context or inherited widgets.

@override
void didChangeDependencies() {
super.didChangeDependencies();
// Respond to dependency changes
}

4. build()
Purpose: This method is called whenever the widget is to be rendered. It is
responsible for constructing the widget tree, which represents the UI. The build()
method can be called multiple times during the widget's lifecycle, especially after
calling setState() .
Key Point: The UI is built based on the current state, so changes in state will
trigger a rebuild.

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Stateful Widget Lifecycle'),
),
body: Center(
child: Text('Hello, World!'),
),
);
}

5. setState()
Purpose: This method is used to notify the framework that the internal state of
this object has changed. When called, it triggers a rebuild of the widget, ensuring
18
that the UI reflects the updated state.
Key Point: Use this method responsibly to avoid unnecessary rebuilds.

void _incrementCounter() {
setState(() {
_counter++;
});
}

6. dispose()
Purpose: This method is called when the State object is permanently removed
from the widget tree. It is used for final cleanup, such as canceling timers, closing
streams, or disposing of controllers.
Key Point: After dispose() , the widget is no longer active, and its State object is
considered dead.

@override
void dispose() {
// Clean up resources
super.dispose();
}

Types of Design System


Material Design
Material Design is a design language developed by Google. It is used primarily for
Android apps but has become popular across various platforms due to its clean,
modern aesthetics and usability principles. Material Design provides a consistent look
and feel with a set of guidelines and components.

Key Principles of Material Design:

1. Material Metaphor: Material Design is based on the metaphor of physical materials


like paper and ink. It uses shadows, depth, and responsive animations to mimic how
real-world materials behave.
2. Bold, Graphic, Intentional: Emphasis on bold colors, large typography, and
meaningful use of space. It aims for clarity and impact.
3. Motion Provides Meaning: Motion in Material Design is used to provide context and
meaning. For example, transitions and animations help users understand the
relationship between different elements.

Cupertino Design
Cupertino Design is a design language developed by Apple for iOS. It aims to create a
user experience that aligns with iOS conventions and aesthetics. Cupertino widgets
provide a native iOS look and feel.

19
Key Principles of Cupertino Design:

1. Consistency with iOS: Cupertino widgets adhere to the design guidelines set by Apple
for iOS apps, ensuring that apps have a consistent look and feel with native iOS
applications.
2. Flat and Minimalistic: Cupertino design tends to use flat, minimalistic elements
without the elevated effects seen in Material Design.
3. Native Feel: Cupertino widgets are styled to feel native on iOS, using iOS-specific
patterns, colors, and interactions.

Row Widget
The Row widget in Flutter is used to display its children widgets in a horizontal array.
It arranges its children in a single horizontal line, which can be useful for creating
layouts where you want elements to be aligned side-by-side.

Key Properties of the Row Widget

1. children: A list of widgets that are arranged horizontally. These can be any widget,
such as Text , Icon , Container , etc.
2. mainAxisAlignment: Defines how the children are aligned along the horizontal axis
(main axis). Common values include:
1. MainAxisAlignment.start : Aligns children to the start of the row.
2. MainAxisAlignment.end : Aligns children to the end of the row.
3. MainAxisAlignment.center : Centers children within the row.
4. MainAxisAlignment.spaceBetween : Distributes children evenly with space between
them.
5. MainAxisAlignment.spaceAround : Distributes children evenly with space around
them.
6. MainAxisAlignment.spaceEvenly : Distributes children evenly with equal space
between them.
3. CrossAxisAlignment: Defines how the children are aligned along the vertical axis
(cross axis). Common values include:
1. CrossAxisAlignment.start : Aligns children to the start of the row (top for
horizontal rows).
2. CrossAxisAlignment.end : Aligns children to the end of the row (bottom for
horizontal rows).
3. CrossAxisAlignment.center : Centers children within the row vertically.
4. CrossAxisAlignment.stretch : Stretches children to fill the vertical space.
4. mainAxisSize: Defines how much space the row should take along the main axis.
Values include:
1. MainAxisSize.max : The row takes up as much horizontal space as possible.
2. MainAxisSize.min : The row only takes up as much horizontal space as needed by
its children.

20
Column Widget
The Column widget in Flutter is used to display its children widgets in a vertical array.
It arranges its children in a single vertical line, which is useful for creating layouts
where you want elements to be stacked one on top of the other.

Key Properties of the Column Widget

1. children: A list of widgets arranged vertically. These can be any widget, such as Text ,
Icon , Container , etc.
2. mainAxisAlignment: Defines how the children are aligned along the vertical axis
(main axis). Common values include:
1. MainAxisAlignment.start : Aligns children to the top of the column.
2. MainAxisAlignment.end : Aligns children to the bottom of the column.
3. MainAxisAlignment.center : Centers children within the column.
4. MainAxisAlignment.spaceBetween : Distributes children evenly with space between
them.
5. MainAxisAlignment.spaceAround : Distributes children evenly with space around
them.
6. MainAxisAlignment.spaceEvenly : Distributes children evenly with equal space
between them.
3. crossAxisAlignment: Defines how the children are aligned along the horizontal axis
(cross axis). Common values include:
1. CrossAxisAlignment.start : Aligns children to the start of the column (left for
vertical columns).
2. CrossAxisAlignment.end : Aligns children to the end of the column (right for
vertical columns).
3. CrossAxisAlignment.center : Centers children within the column horizontally.
4. CrossAxisAlignment.stretch : Stretches children to fill the horizontal space.
4. mainAxisSize: Defines how much space the column should take along the main axis.
Values include:
1. MainAxisSize.max : The column takes up as much vertical space as possible.
2. MainAxisSize.min : The column only takes up as much vertical space as needed
by its children.

Container Widget
The Container widget in Flutter is a versatile and flexible widget used to create
rectangular visual elements. It can be used for a variety of purposes such as adding
padding, margins, borders, and background colors, or for positioning widgets in a
specific layout.

Key Properties of the Container Widget

1. alignment : Aligns the child widget within the container. Common values include
Alignment.center , Alignment.topLeft , Alignment.bottomRight , etc.

21
2. padding: Adds space inside the container between its border and the child widget.
Defined using EdgeInsets (e.g., EdgeInsets.all(16.0) ).
3. margin: Adds space outside the container, separating it from other widgets. Defined
using EdgeInsets (e.g., EdgeInsets.all(16.0) ).
4. width and height: Defines the size of the container. If not provided, the container will
size itself based on its child and constraints.
5. color: Sets the background color of the container.
6. decoration: Allows more complex styling, such as adding borders, gradients, and
shadows. Defined using BoxDecoration (e.g., BoxDecoration(color: Colors.blue,
borderRadius: BorderRadius.circular(10)) ).
7. child: The widget inside the container. This is where you place the content of the
container.

import 'package:flutter/material.dart';

void main() {
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Container Widget Example')),
body: ContainerExample(),
),
));
}

class ContainerExample extends StatelessWidget {


@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
alignment: Alignment.center,
padding: EdgeInsets.all(16.0),
margin: EdgeInsets.all(16.0),
width: 200,
height: 100,
color: Colors.blue,
child: Text(
'Blue Container',
style: TextStyle(color: Colors.white),
),
),
SizedBox(height: 20),
Container(
alignment: Alignment.center,
padding: EdgeInsets.all(16.0),
margin: EdgeInsets.all(16.0),
decoration: BoxDecoration(

22
color: Colors.green,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
),
child: Text(
'Green Container with Shadow',
style: TextStyle(color: Colors.white),
),
),
SizedBox(height: 20),
Container(
alignment: Alignment.center,
padding: EdgeInsets.all(16.0),
margin: EdgeInsets.all(16.0),
width: 200,
height: 100,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.purple, Colors.orange],
),
borderRadius: BorderRadius.circular(10),
),
child: Text(
'Gradient Container',
style: TextStyle(color: Colors.white),
),
),
],
),
);
}
}

Text Widget

Text('Hello, world!')

Button Widget

ElevatedButton(
onPressed: () {
// Action when button is pressed
},

23
child: Text('Press Me'),
)

Image Widget

Image.network('<https://example.com/myimage.jpg>')

Gestures
In Flutter, gestures are actions that users can perform with their fingers, like tapping,
dragging, or swiping, to interact with the app. Think of gestures as the ways you can
communicate with your app using touch.

Tap Gesture

GestureDetector(
onTap: () {
print('Tapped!');
},
child: Container(
color: Colors.blue,
width: 100,
height: 100,
child: Center(
child: Text('Tap Me'),
),
),
)

Long Press Gesture

GestureDetector(
onLongPress: () {
print('Long Pressed!');
},
child: Container(
color: Colors.green,
width: 100,
height: 100,
child: Center(
child: Text('Long Press Me'),
),
),
)

Swipe Gesture

24
GestureDetector(
onPanUpdate: (details) {
print('Swiped: ${details.delta}');
},
child: Container(
color: Colors.red,
width: 100,
height: 100,
child: Center(
child: Text('Swipe Me'),
),
),
)

Concept of State
In Flutter, the concept of "state" refers to any data or information that can change
over time within your app. The state of a widget is what allows it to remember
information and change its appearance or behavior in response to user actions or
other events.
Think of a simple counter app where you press a button to increase a number
displayed on the screen. The number is the state, and pressing the button changes the
state.
StatelessWidget: A widget that doesn't change over time. It has no state.
StatefulWidget: A widget that can change over time. It has a state that can change.

Layers
In modern software development, maintaining a well-organized and scalable
codebase is crucial for building robust applications.
The layered architecture, often referred to as clean architecture, is a design pattern
that divides an application into distinct layers, each responsible for a specific aspect
of the system.

25
Presentation Layer
Widgets:
In Flutter, widgets are the basic building blocks of the user interface (UI).
They define the visual components and structure of the UI, such as buttons, text
fields, and layouts.
Widgets are used to create the visual representation of the app.
States:
State refers to the dynamic data in your application that can change over time.
In Flutter, state management is crucial for building interactive and responsive UIs.
State holds the current values of the UI components and allows for updating them
when needed.
Controllers:
Controllers manage the logic and interaction between the UI and the data.
They handle user inputs, update the state, and manage the flow of data between
the UI (widgets) and the underlying layers.
Controllers are responsible for ensuring that the correct data is displayed in the UI
and that user actions trigger the appropriate responses.

Application Layer
Services:
Services contain the business logic of the application.
26
They perform operations that are not directly tied to the UI but are essential for
the functioning of the app, such as fetching data from an API, processing user
inputs, or executing business rules.
Services act as intermediaries between the presentation layer and the domain
layer.

Domain Layer
Models:
Models represent the core data structures and business logic of the application.
They define the entities and their relationships, encapsulating the rules and
behaviors associated with the data.
Models are usually independent of the UI and can be reused across different parts
of the application.

Data Layer
Repositories:
Repositories are responsible for handling data operations such as fetching,
storing, and updating data.
They abstract the data sources from the rest of the application, providing a
consistent interface for accessing data.
Repositories ensure that the application does not need to know where the data
comes from (e.g., a database, an API, or a local file).
DTOs (Data Transfer Objects):
DTOs are simple objects used to transfer data between different layers of the
application.
They are often used to encapsulate data that needs to be passed from the data
layer to the domain or presentation layers.
DTOs help in keeping the application layers loosely coupled by providing a
common format for data exchange.
Data Sources:
Data sources refer to the origin of the data, such as databases, web APIs, or local
storage.
They provide the raw data that the repositories access and manage.
The data layer interacts directly with these sources to retrieve or store information
needed by the application.

This architecture promotes separation of concerns, making it easier to manage, test, and
maintain the application. Each layer has a distinct responsibility, ensuring that changes in
one part of the application do not negatively impact other parts.

Dart Programming

27
Dart is a free and open-source programming language developed by Google. It's used
to create both the backend (server side) and frontend (user side) of applications.

Key Components of Dart

Dart SDK: The Software Development Kit (SDK) includes tools to write and run Dart
programs.
Dart VM: The Dart Virtual Machine is a program that runs Dart code directly.
dart2js: A tool that converts Dart code into JavaScript. This is useful because not all
websites support Dart, but they do support JavaScript.

Key Features

Server Side: Dart can be used to write server code.


User Side: Dart can be used to create user interfaces, especially with Flutter for
mobile apps.
JavaScript Compatibility: Dart code can be converted to JavaScript, making it
versatile for web development.

Features of Dart
Easy to Understand:

Dart's syntax is similar to C# and Java, making it familiar to many developers.


It supports code reuse, keeping programs clean and easy to understand.

Object-Oriented Programming (OOP):

Like Java and C++, Dart follows OOP principles, making it powerful for building
complex applications.

Open Source:

Dart is open source, making it popular among individual developers and organizations

Browser Support:

Dart programs can be converted to JavaScript using the dart2js compiler, ensuring
compatibility with all modern web browsers.

Type Safe:

Dart combines static and runtime checks to ensure variables match their defined
types, reducing errors.

Flexible Compilation and Execution:

Supports both Just-in-Time (JIT) and Ahead-of-Time (AOT) compilation, offering


flexibility in how code is compiled and executed.
The dart2js tool adds further value by converting Dart code to JavaScript.
28
Asynchronous Programming:

Dart supports asynchronous programming, allowing it to handle multiple tasks


simultaneously and efficiently.
Example

void main() async {


print('Start');
await Future.delayed(Duration(seconds: 2), () {
print('Hello after 2 seconds');
});
print('End');
}

Variables
A variable is a name assigned to a memory location where data is stored. The type of
variable depends on the type of data it stores.
Declaring Variables

Single Variables
/
/
type variable_name;

// Multiple Variable
type variable1, variable2, variable3;

Types of Variable:

1. Static Variables
2. Dynamic Variables
3. Final or Const Variables

Rules for variable

Cannot be a keyword.
Can contain alphabets and numbers.
No spaces or special characters (except _ and $ ).
Cannot start with a number.

void main() {
int age = 30;
double height = 5.9;
bool isStudent = false;
String name = "Alice", city = "Wonderland";
print(age); // Output: 30
print(height); // Output: 5.9
print(isStudent); // Output: false
print(name); // Output: Alice

29
print(city); // Output: Wonderland
}

Dynamic Variables

Declared with dynamic keyword.


Can store any type of value.

void main() {
dynamic item = "Book";
print(item); // Output: Book
item = 10;
print(item); // Output: 10
}

Final and Const Variable

Final: Value assigned once, can be set at runtime.

final name = "Alice";


final String city = "Wonderland";

Const: Compile-time constant, value known before runtime.

const pi = 3.14;
const String greeting = "Hello";

Null Safety in Dart

Variables can be declared to hold null values by appending ?

void main() {
int? age;
age = null;
print(age); // Output: null
}

Keywords
Keywords in Dart are reserved words with predefined functions.
They cannot be used as variable names or identifiers.
These keywords help define the structure and syntax of the Dart programming
language.

30
Data Types
Data type determines what kind of data the variable can hold. Understanding data
types is crucial because it helps you handle data correctly in your programs.

Number
In Dart, numbers are essential for representing and manipulating numeric values. Dart
provides a few different types for handling numbers, and each type has its own
characteristics and uses.
int: Used to represent whole numbers (integers).

void main() {
// Declare an integer
int num1 = 10;
print(num1); // Output: 10
}

double: Used to represent numbers with decimal points (floating-point numbers).

void main() {
// Declare a double value
double num2 = 10.5;
print(num2); // Output: 10.5
}

num: This is a general type that can hold either int or double . It is useful when you
need a variable that can be either an integer or a floating-point number.
31
void main() {
// Use num to hold different types of numbers
num num1 = 10; // an integer
num num2 = 10.5; // a double
print(num1); // Output: 10
print(num2); // Output: 10.5
}

Parsing Strings to Numbers

Dart allows you to convert strings containing numeric values into actual numbers
using the parse() function.

void main() {
// Parsing strings to numbers
var num1 = num.parse("5");
var num2 = num.parse("3.14");
// Adding parsed numbers
var sum = num1 + num2;
print("Sum = $sum"); // Output: Sum = 8.14
}

Properties of Number

hashCode : Gets the hash code of the number.


isFinite : Checks if the number is finite (not infinite).
isInfinite : Checks if the number is infinite.
isNaN : Checks if the number is 'Not a Number' (NaN).
isNegative : Checks if the number is negative.
sign : Returns -1 for negative numbers, 0 for zero, and 1 for positive numbers.
isEven : Checks if the number is even.
isOdd : Checks if the number is odd.

Methods for Number

abs() : Returns the absolute value.

void main() {
int number = -5;
print(number.abs()); // Output: 5
}

ceil() : Rounds the number up to the nearest integer.

void main() {
double number = 3.14;

32
print(number.ceil()); // Output: 4
}

oor(): Rounds the number down to the nearest integer.


fl
void main() {
double number = 3.14;
print(number.floor()); // Output: 3
}

compareTo() : Compares the number with another number.

void main() {
int number1 = 10;
int number2 = 20;
print(number1.compareTo(number2)); // Output: -1
}

remainder(): Returns the remainder after division.

void main() {
int dividend = 10;
int divisor = 3;
print(dividend.remainder(divisor)); // Output: 1
}

round(): Rounds the number to the nearest integer.

void main() {
double number = 3.6;
print(number.round()); // Output: 4
}

toDouble(): Converts the number to a double.

void main() {
int number = 10;
print(number.toDouble()); // Output: 10.0
}

toInt(): Converts the number to an integer.

void main() {
double number = 10.5;
print(number.toInt()); // Output: 10
}

33
toString(): Converts the number to a string.

void main() {
int number = 10;
print(number.toString()); // Output: "10"
}

truncate(): Removes the fractional part of the number.

void main() {
double number = 10.9;
print(number.truncate()); // Output: 10
}

String
Strings in Dart are used to represent sequences of characters. Dart provides various
features and methods to handle and manipulate strings efficiently.
In Dart, you can declare strings using either single quotes ( ' ) or double quotes ( " ).
Both types of quotes work the same way.

void main() {
String greeting = 'Hello, Dart!';
print(greeting); // Output: Hello, Dart!
}

You can combine strings using the + operator.

void main() {
String firstName = 'John';
String lastName = 'Doe';
String fullName = firstName + ' ' + lastName;
print(fullName); // Output: John Doe
}

Dart allows you to embed variables inside strings using ${} within double quoted
strings.

void main() {
String name = 'Alice';
int age = 30;
String introduction = 'Hello, my name is $name and I am $age years old.';
print(introduction); // Output: Hello, my name is Alice and I am 30 years old.
}

For simple variables, you can directly use the variable name without curly braces:

34
void main() {
String name = 'Bob';
String greeting = 'Hi, $name!';
print(greeting); // Output: Hi, Bob!
}

You can create multi-line strings using triple quotes ( ''' or """ ).

void main() {
String multiLine = '''This is a string
that spans across multiple
lines.''';
print(multiLine);
// Output:
// This is a string
// that spans across multiple
// lines.
}

String Methods

Creating Strings
String.fromCharCodes(Iterable charCodes) : Creates a string from a list of
character codes.
String.fromCharCode(int charCode) : Creates a string from a single character
code.
String.fromEnvironment(String name, {String defaultValue}) : Gets a string
from environment variables.
Accessing Characters
codeUnitAt(int index) : Returns the UTF-16 code unit at the specified index.
runes : Returns an iterable of the Unicode code points of the string.
characters : Returns an iterable of the string's characters (requires the characters
package).
Querying Strings
contains(Pattern pattern, [int start = 0]) : Checks if the string contains the
specified pattern.
startsWith(Pattern pattern, [int start = 0]) : Checks if the string starts with
the specified pattern.
endsWith(String other) : Checks if the string ends with the specified string.
isEmpty : Checks if the string is empty.
isNotEmpty : Checks if the string is not empty.
length : Returns the number of characters in the string.
Manipulating Strings
toLowerCase() : Converts all characters in the string to lowercase.
toUpperCase( ) : Converts all characters in the string to uppercase.

35
trim() : Removes leading and trailing whitespace from the string.
trimLeft() : Removes leading whitespace from the string.
trimRight() : Removes trailing whitespace from the string.
replaceAll(Pattern from, String replace) : Replaces all occurrences of a
pattern with a new string.
replaceFirst(Pattern from, String to, [int startIndex = 0]) : Replaces the
first occurrence of a pattern with a new string.
replaceRange(int start, int end, String replacement) : Replaces the substring
from start to end with a new string.
padLeft(int width, [String padding = ' ']) : Pads the string on the left to the
specified width.
padRight(int width, [String padding = ' ']) : Pads the string on the right to
the specified width.
substring(int start, [int end]) : Returns the substring from start to end.
split(Pattern pattern) : Splits the string at each occurrence of the pattern and
returns a list of substrings.
join(Iterable strings ) : Joins a list of strings into a single string.

Comparing Strings
compareTo(String other) : Compares this string to another string.
(equality operator): Checks if two strings are equal.
=
=
hashCode : Returns the hash code for the string.
Interpolating and Formatting Strings
String interpolation: String name = 'World'; print('Hello, $name!');
replaceAllMapped(Pattern from, String replace(Match match)) : Replaces all
occurrences of a pattern with a new string computed from a function.

void main() {
String str = 'Hello, World!';
// Accessing Characters
print(str.codeUnitAt(0)); // Output: 72
print(str.runes
.toList()); // Output: [72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108,
100, 33]
// Querying Strings
print(str.contains('World')); // Output: true
print(str.startsWith('Hello')); // Output: true
print(str.endsWith('!')); // Output: true
print(str.isEmpty); // Output: false
print(str.isNotEmpty); // Output: true
print(str.length); // Output: 13
// Manipulating Strings
print(str.toLowerCase()); // Output: hello, world!
print(str.toUpperCase()); // Output: HELLO, WORLD!
print(str.trim()); // Output: Hello, World!
print(str.replaceAll('World', 'Dart')); // Output: Hello, Dart!
print(str.substring(0, 5)); // Output: Hello

36
print(str.split(', ')); // Output: [Hello, World!]
print(str.padLeft(15, '*')); // Output: **Hello, World!
print(str.padRight(15, '*')); // Output: Hello, World!**
// Interpolating and Formatting Strings
String name = 'Dart';
print('Hello, $name!'); // Output: Hello, Dart!
// Advanced Replacement
String text = 'The quick brown fox';
String newText = text.replaceAllMapped(
RegExp(r'\\b\\w'), (match) => match.group(0)!.toUpperCase());
print(newText); // Output: The Quick Brown Fox
}

Booleans
Booleans represent truth values: either true or false .

void main() {
// Declare boolean variables
bool isDay = true;
bool isNight = false;
// Print the values
print("Is it day? $isDay");
print("Is it night? $isNight");
}

List in Dart
In Dart, a List is a collection of items that are ordered and can be accessed by their
index.
It is similar to arrays in other programming languages.
Lists are versatile and can hold various types of data, such as numbers, strings, or
even other lists.
You can declare an empty list and then add elements to it.

void main() {
List<int> numbers = []; // Empty list of integers
numbers.add(10); // Add elements to the list
numbers.add(20);
numbers.add(30);
print(numbers); // Output: [10, 20, 30]
}

You can declare a list with some initial values.

void main() {
List<String> fruits = ['Apple', 'Banana', 'Cherry'];

37
print(fruits); // Output: [Apple, Banana, Cherry]
}

You can create a list with a fixed size, where the size is defined at the time of creation.
You can then modify the elements, but the size remains fixed.

void main() {
List<String> fixedList = List<String>.filled(3, 'default');
fixedList[0] = 'Hello';
fixedList[1] = 'World';
fixedList[2] = 'Dart';
print(fixedList); // Output: [Hello, World, Dart]
}

You can also create a list by generating values using a function.

void main() {
List<int> generatedList = List<int>.generate(5, (index) => index * 2);
print(generatedList); // Output: [0, 2, 4, 6, 8]
}

You can access elements in a list using their index, which starts at 0.

void main() {
List<String> colors = ['Red', 'Green', 'Blue'];
print(colors[0]); // Output: Red
print(colors[1]); // Output: Green
print(colors[2]); // Output: Blue
}

You can change the value of a specific element by accessing it via its index.

void main() {
List<String> animals = ['Cat', 'Dog', 'Bird'];
animals[1] = 'Fish'; // Change 'Dog' to 'Fish'
print(animals); // Output: [Cat, Fish, Bird]
}

List Methods

Adding Elements
add(E value) : Adds a single element to the end of the list.
addAll(Iterable iterable) : Adds all elements of the given iterable to the end of
the list.
Removing Elements
remove(Object value) : Removes the first occurrence of the value from the list.

38
removeAt(int index) : Removes the element at the specified index.
removeLast() : Removes the last element of the list.
removeRange(int start, int end) : Removes elements from the specified range.

Accessing Elements
elementAt(int index) : Returns the element at the specified index.
first : Gets the first element of the list.
last : Gets the last element of the list.
single : Gets the single element of the list, assuming the list has only one element.
indexOf(E element) : Returns the first index of the element, or -1 if not found.
lastIndexOf(E element) : Returns the last index of the element, or -1 if not found.
Updating Elements
[] (index operator): Access or update the element at the specified index.
insert(int index, E element) : Inserts an element at the specified index.
insertAll(int index, Iterable iterable) : Inserts all elements of the given
iterable at the specified index.
Querying Lists
contains(Object element) : Checks if the list contains the specified element.
isEmpty : Checks if the list is empty.
isNotEmpty : Checks if the list is not empty. length : Returns the number of
elements in the list.
Sorting and Reversing
sort([int compare(E a, E b)]) : Sorts the list in place according to the provided
comparison function.
reversed : Returns an iterable with the elements of the list in reverse order.

Sublist and Ranges


sublist(int start, [int end]) : Returns a new list containing the elements in the
specified range.
fillRange(int start, int end, [E fillValue]) : Replaces the elements in the
specified range with the given value.
replaceRange(int start, int end, Iterable newContents) : Replaces the
elements in the specified range with the given iterable.
Transformation
map(T f(E e)) : Returns a new iterable with the results of applying the function to
each element.
where(bool test(E element)) : Returns a new iterable with all elements that
satisfy the test.
forEach(void f(E element)) : Applies the function to each element of the list.
reduce(T combine(T value, E element)) : Reduces the list to a single value by
repeatedly applying the combine function.
fold(T initialValue, T combine(T previousValue, E element)) : Similar to
reduce , but allows specifying an initial value.

39
join([String separator = ""]) : Returns a string representation of the list, joined
by the separator.
Copy and Clear
toList({bool growable = true}) : Returns a new list containing the elements of
the iterable.
clear() : Removes all elements from the list.

void main() {
List<int> numbers = [1, 2, 3, 4, 5];
// Add elements
numbers.add(6);
numbers.addAll([7, 8, 9]);
// Remove elements
numbers.remove(2);
numbers.removeAt(0);
numbers.removeLast();
// Access elements
print(numbers[0]); // Output: 3
print(numbers.first); // Output: 3
print(numbers.last); // Output: 8
// Update elements
numbers[0] = 10;
numbers.insert(1, 15);
// Query list
print(numbers.contains(15)); // Output: true
print(numbers.isEmpty); // Output: false
print(numbers.length); // Output: 6
// Sort and reverse
numbers.sort();
print(numbers); // Output: [3, 4, 5, 6, 7, 10, 15]
print(numbers.reversed.toList()); // Output: [15, 10, 7,6, 5, 4, 3]
// Sublist and ranges
print(numbers.sublist(1, 3)); // Output: [4, 5]
numbers.fillRange(0, 2, 99);
print(numbers); // Output: [99, 99, 5, 6, 7, 10, 15]
// Transformation
var mapped = numbers.map((e) => e * 2).toList();
print(mapped); // Output: [198, 198, 10, 12, 14, 20, 30]
// Copy and clear
var copy = numbers.toList();
numbers.clear();
print(numbers); // Output: []
print(copy); // Output: [99, 99, 5, 6, 7, 10, 15]
}

Maps
A Map in Dart is a collection of key-value pairs, where each key is associated with a
value.

40
It is similar to dictionaries in Python or hash maps in other programming languages.
Maps are useful when you need to store and retrieve data based on a unique key.
You can declare an empty map and then add key-value pairs to it.

void main() {
// Declaring an empty map
Map<String, String> emptyMap = {};
// Adding key-value pairs
emptyMap['name'] = 'John';
emptyMap['city'] = 'New York';
print(emptyMap); // Output: {name: John, city: New York}
}

You can declare a map with some initial key-value pairs.

void main() {
// Declaring a map with initial values
Map<String, int> ages = {'Alice': 30, 'Bob': 25, 'Charlie': 35};
print(ages); // Output: {Alice: 30, Bob: 25, Charlie: 35}
}

You can access values in a map using their keys and modify them as needed.

void main() {
Map<String, String> capitals = {
'USA': 'Washington, D.C.',
'France': 'Paris',
'Japan': 'Tokyo'
};
// Accessing a value
print(capitals['France']); // Output: Paris
// Modifying a value
capitals['Japan'] = 'Kyoto'; // Change Tokyo to Kyoto
print(
capitals); // Output: {USA: Washington, D.C., France: Paris, Japan: Kyoto}
}

Map Methods

Adding and Updating Elements


putIfAbsent(K key, V ifAbsent()) : Adds a key-value pair to the map if the key is
not already present.
addAll(Map other) : Adds all key-value pairs from another map to the current
map.
update(K key, V update(V value), {V ifAbsent()}) : Updates the value for the
provided key, or adds it if it does not exist.
updateAll(V update(K key, V value)) : Updates all values in the map by applying
the provided function.
41
Removing Elements
remove(Object key) : Removes the value for the specified key from the map.
clear() : Removes all key-value pairs from the map.
Accessing Elements
[] (index operator): Accesses the value associated with the specified key.
containsKey(Object key) : Checks if the map contains the specified key.
containsValue(Object value) : Checks if the map contains the specified value.
entries : Returns an iterable of the key-value pairs in the map.
keys : Returns an iterable of the keys in the map.
values : Returns an iterable of the values in the map.

Querying Maps
isEmpty : Checks if the map is empty.
isNotEmpty : Checks if the map is not empty.
length : Returns the number of key-value pairs in the map.
Transforming Maps
map(MapEntry transform(K key, V value)) : Returns a new map with the results of
applying the transform function to each key-value pair.
forEach(void action(K key, V value)) : Applies the function to each key-value
pair in the map.
Getting Default Values
putIfAbsent(K key, V ifAbsent()) : Returns the value for the specified key, or
adds the key with a value computed by ifAbsent() and returns that value.

void main() {
Map<String, int> map = {'a': 1, 'b': 2, 'c': 3};
// Adding and updating elements
map['d'] = 4; // Add new key-value pair
map.putIfAbsent('e', () => 5); // Add if key is absent
map.update('a', (value) => value + 1); // Update existing value
map.updateAll((key, value) => value * 2); // Update allvalues
// Removing elements
map.remove('b'); // Remove key-value pair by key
map.clear(); // Clear all key-value pairs
// Accessing elements
map = {'a': 1, 'b': 2, 'c': 3};
print(map['a']); // Access value by key, Output: 1
print(map.containsKey('a')); // Check if key exists, Output: true
print(map.containsValue(2)); // Check if value exists, Output: true
// Querying map
print(map.isEmpty); // Check if map is empty, Output: false
print(map.isNotEmpty); // Check if map is not empty, Output: true
print(map.length); // Get the number of key-value pairs,Output: 3
// Transforming maps
map.forEach((key, value) {
print('Key: $key, Value: $value');
});
// Output:
42
// Key: a, Value: 1
// Key: b, Value: 2
// Key: c, Value: 3
var newMap = map.map((key, value) => MapEntry(key, value * 10));
print(newMap); // Output: {a: 10, b: 20, c: 30}
// Getting default values
var value = map.putIfAbsent('d', () => 4);
print(value); // Output: 4
print(map); // Output: {a: 1, b: 2, c: 3, d: 4}
// Accessing keys and values
print(map.keys); // Output: (a, b, c, d)
print(map.values); // Output: (1, 2, 3, 4)
// Entries
print(map
.entries); // Output: (MapEntry(a: 1), MapEntry(b: 2), MapEntry(c: 3),
MapEntry(d: 4))
}

Comments
Comments help explain code for better understanding. They are not executed by the
compiler and serve as documentation.

This sis Documentation Comment


/
/
/
/// This function prints a greeting message

// This is a single line comment

/* This is
a multi line
comment */

print("Hello World")

Operators
Operators are special symbols used to perform operations on values or variables.

Arithmetic Operators
Arithmetic operators are used for basic math operations. They work with numbers to
perform calculations
+ (Addition): Adds two numbers.
- (Subtraction): Subtracts one number from another.
* (Multiplication): Multiplies two numbers.
/ (Division): Divides one number by another and gives a decimal result.
~/ (Integer Division): Divides and gives the whole number part of the result.
% (Modulus): Gives the remainder of a division.

43
void main() {
int a = 2;
int b = 3;
print("Sum (a + b) = ${a + b}");
print("Difference (a - b) = ${a - b}");
print("Negation (-(a - b)) = ${-(a - b)}");
print("Product (a * b) = ${a * b}");
print("Division (b / a) = ${b / a}");
print("Quotient (b ~/ a) = ${b ~/ a}");
print("Remainder (b % a) = ${b % a}");
}

Relational Operators
Relational operators compare values and give a result that tells whether a condition is
true or false.
> (Greater than): Checks if one value is larger than another.
< (Less than): Checks if one value is smaller than another
(Greater than or equal to): Checks if one value is larger or equal to another.
>
=
(Less than or equal to): Checks if one value is smaller or equal to another.
<
=
(Equal to): Checks if two values are the same.
=
=
(Not equal to): Checks if two values are different.
!
=
void main() {
int a = 2;
int b = 3;
print("a > b: ${a > b}");
print("a < b: ${a < b}");
print("a >= b: ${a >= b}");
print("a <= b: ${a <= b}");
print("a == b: ${a == b}");
print("a != b: ${a != b}");
}

Type Test Operators


These operators are used to check the type of a value.
is : Checks if a value is of a specific type.
is! : Checks if a value is not of a specific type.

void main() {
String a = 'Hello';
double b = 3.3;
print(a is String); // true
print(b is! int); // true
}

44
Assignment Operators
Assignment operators are used to set or update the value of a variable.
= (Equal to): Assigns a value to a variable.
= (Null-aware assignment): Assigns a value only if the variable is null .
?
?
void main() {
int a = 5;
int b = 7;
var c = a * b;
print("c = $c");
var d;
d ??= a + b;
print("d = $d");
d ??= a - b;
print("d = $d"); // d remains 12
}

Logical Operators
Logical operators are used to combine multiple conditions.
(AND): Returns true if both conditions are true .
&
&
(OR): Returns true if at least one condition is true .
|
|
! (NOT): Reverses the result of a condition.

void main() {
bool a = true;
bool b = false;
print("a && b: ${a && b}"); // false
print("a || b: ${a || b}"); // true
print("!a: ${!a}"); // false
}

Conditional Operators
Conditional operators choose between two values based on a condition.
condition ? expression1 : expression2 : Executes expression1 if condition is true ,
otherwise expression2 .
expression1 ?? expression2 : Returns expression1 if it's not null , otherwise
expression2 .

void main() {
int a = 5;
var result = (a < 10) ? "Correct" : "Wrong";
print(result);
int? n;
var value = n ?? "n is null";
print(value);

45
n = 10;
value = n;
print(value);
}

Cascade Notation Operators


Cascade notation allows performing multiple operations on the same object.
(Cascade): Allows you to call multiple methods on the same object
.
.
class Calc {
int a = 0;
int b = 0;
void set(x, y) {
this.a = x;
this.b = y;
}

void add() {
var z = this.a + this.b;
print(z);
}
}

void main() {
Calc calculator = new Calc();
Calc calculator2 = new Calc();
calculator.set(1, 2);
calculator.add();
calculator2
..set(3, 4)
..add();
}

Standard Input and Output


To read input from the user, you need to use the dart:io library. This library allows
you to access the standard input (keyboard) and output (console).
The stdin.readLineSync() function is commonly used to read user input as a string.
Here’s how to take a string input from the user

import 'dart:io';

void main() {
print("Enter your name:");
// Reading the user's name
String? name = stdin.readLineSync();
// Printing a greeting message

46
print("Hello, $name! \\nWelcome to Dart!!");
}

To take an integer input, you first read the input as a string and then convert it to an
integer using int.parse()

import 'dart:io';

void main() {
print("Enter your favourite number:");
// Reading and converting input to integer
int? number = int.parse(stdin.readLineSync()!);
// Printing the number
print("Your favourite number is $number");
}

Dart provides two ways to display output


The print() function adds a newline after the output

import 'dart:io';

void main() {
print("Welcome to Dart!");
}

The stdout.write() function prints text without adding a newline

import 'dart:io';

void main() {
stdout.write("Welcome to Dart!");
}

Using print() : Moves to the next line after printing.


Using stdout.write : Stays on the same line.

Conditional Statements
Conditional statements in Dart are used to make decisions in your code.
They allow you to execute different blocks of code based on certain conditions.

if Statement

The if statement is used to execute a block of code if a specified condition is true.

void main() {
int number = 10;
if (number > 5) {

47
print('The number is greater than 5');
}
}

if-else Statement

The if-else statement allows you to execute one block of code if a condition is true
and another block if the condition is false

void main() {
int number = 3;
if (number > 5) {
print('The number is greater than 5');
} else {
print('The number is 5 or less');
}
}

if-else if-else Statement

This structure allows you to check multiple conditions sequentially

void main() {
int number = 7;
if (number > 10) {
print('The number is greater than 10');
} else if (number > 5) {
print('The number is greater than 5 but 10 or less');
} else {
print('The number is 5 or less');
}
}

switch Statement

The switch statement is used to select one of many code blocks to execute.
It compares the value of a variable to a series of cases and executes the code block
associated with the matching case.

void main() {
String day = 'Monday';
switch (day) {
case 'Monday':
print('Start of the work week');
break;
case 'Friday':
print('End of the work week');
break;
case 'Saturday':

48
case 'Sunday':
print('Weekend');
break;
default:
print('Invalid day');
}
}

Conditional Expressions (Ternary Operator)


The ternary operator is a shorthand for if-else statements. It allows you to write a
simple conditional statement in a single line.

void main() {
int number = 10;
String result =
number > 5 ? 'The number is greater than 5' : 'The number is 5 or less';
print(result);
}

Looping and Control Flow


Looping and control flow statements are essential for executing repetitive tasks and
controlling the flow of a program based on various conditions.

for Loop

The for loop is used to execute a block of code a specific number of times.
It is useful when you know beforehand how many times you want to iterate.

void main() {
for (int i = 0; i < 5; i++) {
print(i);
}
}

while Loop

The while loop repeatedly executes a block of code as long as a specified condition is
true.
It is used when you don't know in advance how many times the loop will run.

void main() {
int i = 0;
while (i < 5) {
print(i);
i++;

49
}
}

do-while Loop

The do-while loop is similar to the while loop but ensures that the code block is
executed at least once before checking the condition.

void main() {
int i = 0;
do {
print(i);
i++;
} while (i < 5);
}

for-in Loop

The for-in loop is used to iterate over elements in a collection such as a list or a map.
It is useful for looping through items in a collection without needing an index.

void main() {
List<String> fruits = ['Apple', 'Banana', 'Cherry'];
for (var fruit in fruits) {
print(fruit);
}
}

break Statement

The break statement is used to exit a loop before its condition becomes false. It
immediately terminates the innermost loop.

void main() {
for (int i = 0; i < 10; i++) {
if (i == 5) {
break;
}
print(i);
}
}

continue Statement

The continue statement is used to skip the rest of the code inside the current iteration
of a loop and proceed with the next iteration.

50
void main() {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue;
}
print(i);
}
}

Labels
Labels can be used with break and continue statements to specify which loop to
break out of or continue. This is useful in nested loops.

outerLoop: for (int i = 0; i < 5; i++) {


for (int j = 0; j < 5; j++) {
if (j == 3) {
break outerLoop;
}
print('$i, $j');
}
}

Functions
Functions are blocks of code designed to perform a specific task.
They help in organizing and reusing code, making programs more modular and easier
to manage

Built-in Function
Built-in functions are predefined functions provided by the programming language.
They are readily available and do not require you to define them
In dart, print() , int.parse() , List.add() , sqrt() etc are built-in functions

User Defined Functions


User-defined functions are those that you create yourself to perform specific tasks
that are not covered by built-in functions.
They allow you to encapsulate reusable logic in your code
In Dart, you define a function using the returnType followed by the functionName , a
list of parameters enclosed in parentheses, and a block of code enclosed in curly
braces.

int add(int a, int b) {


return a + b;
}

51
To use a function, you call it by its name and provide the necessary arguments

void main() {
int result = add(5, 3);
print(result); // Output: 8
}

If a function does not return a value, you use void as the return type.

void greet(String name) {


print('Hello, $name!');
}

Operational Parameters

Dart functions can have optional parameters.


Positional Optional Parameters: Enclosed in square brackets.

void printDetails(String name, [int age = 0]) {


print('Name: $name, Age: $age');
}

void main() {
printDetails('Alice'); // Output: Name: Alice, Age: 0
printDetails('Bob', 25); // Output: Name: Bob, Age: 25
}

Named Optional Parameters: Enclosed in curly braces

void printDetails({required String name, int age = 0}) {


print('Name: $name, Age: $age');
}

void main() {
printDetails(name: 'Alice'); // Output: Name: Alice, Age: 0
printDetails(name: 'Bob', age: 25); // Output: Name: Bob, Age: 25
}

Arrow Functions

For simple functions with a single expression, you can use the arrow syntax for
conciseness.

int add(int a, int b) => a + b;

Anonymous Functions

52
Anonymous functions (or lambda functions) are functions without a name. They are
often used as arguments to other functions.

void main() {
var numbers = [1, 2, 3, 4];
// Anonymous Function
var doubled = numbers.map((number) => number * 2);
print(doubled); // Output: (2, 4, 6, 8)
}

Higher-Order Functions

Functions that take other functions as parameters or return functions are called
higher-order functions.

void performOperation(int a, int b, int Function(int, int) operation) {


print(operation(a, b));
}

int add(int x, int y) => x + y;


int multiply(int x, int y) => x * y;
void main() {
performOperation(5, 3, add); // Output: 8
performOperation(5, 3, multiply); // Output: 15
}

Difference Between Built-in and User-Defined Functions


Origin:

Built-in functions are provided by the programming language.


User-defined functions are created by the programmer.

Availability:

Built-in functions are always available and can be used without any prior definition.
User-defined functions must be defined before they can be used.

Flexibility:

Built-in functions have fixed functionality as defined by the language.


User-defined functions can be customized to perform any task you need.

When to Use Each:

Built-in Functions: Use these whenever possible, as they are optimized and tested for
performance.
User-Defined Functions: Use these when you need custom logic that is not provided
by built-in functions or when you want to encapsulate repeated code for better

53
reusability and readability.

Object-Oriented Programming (OOP)


Object-Oriented Programming (OOP) is a programming paradigm that uses "objects"
to design and structure software.
Dart, being an object-oriented language, allows you to use OOP principles to create
reusable, modular, and maintainable code
Class: A blueprint for creating objects. It defines the properties (attributes) and
methods (functions) that the objects created from the class will have.
Object: An instance of a class. It is created based on the class blueprint and can use
the properties and methods defined in the class.

Define a class
/
/
class Person {
// Properties
String name;
int age;
// Constructor
Person(this.name, this.age);
// Method
void greet() {
print('Hello, my name is $name and I am $age years old.');
}
}

void main() {
// Create an object of the class
Person person = Person('Alice', 30);
person.greet(); // Output: Hello, my name is Alice and I am 30 years old.
}

this Keyword

In Dart, the this keyword refers to the current instance of the class in which it is used.
It is primarily used to distinguish between class properties and parameters with the
same name, and to access the current object’s members
Key Uses of this Keyword
Accessing Instance Variables: Use this to access instance variables and
methods of the current object.
Differentiating Between Instance Variables and Parameters: Use this to
differentiate between instance variables and parameters or local variables when
they have the same name.
Passing the Current Object: Use this to pass the current object as a parameter
to other methods or constructors.

Accessing Instance Variables

54
When you want to refer to instance variables within a class method, you can use this
to clarify that you are referring to the current object's variables.

class Person {
String name;
int age;
Person(this.name,
this.age); // Constructor with parameter names same as instance variables
void printInfo() {
print('Name: ${this.name}');
print('Age: ${this.age}');
}
}

void main() {
Person person = Person('Alice', 30);
person.printInfo();
}

Differentiating Between Instance Variables and Parameters

When constructor parameters or local variables have the same names as instance
variables, this helps to distinguish them

class Rectangle {
int width;
int height;
Rectangle(int width, int height) {
this.width = width; // `this.width` refers to the instance variable
this.height = height; // `this.height` refers to the instance variable
}
void display() {
print('Width: $width, Height: $height');
}
}

void main() {
Rectangle rect = Rectangle(10, 20);
rect.display();
}

Passing the Current Object

You can use this to pass the current object as a parameter to other methods or
constructors.

class Box {
int length;
Box(this.length);

55
void describe(Box other) {
print('Comparing this box with another box of length ${other.length}');
}

void compare() {
describe(this); // Passing the current object to the method
}
}

void main() {
Box box1 = Box(15);
Box box2 = Box(10);
box1.compare();
// Output: Comparing this box with another box of length 15
}

Encapsulation
Encapsulation is the concept of hiding the internal state of an object and requiring all
interaction to be performed through an object's methods.
This is done to protect the object's integrity by preventing outside interference and
misuse.

class BankAccount {
// Private property
double _balance;
// Constructor
BankAccount(this._balance);
// Method to deposit money
void deposit(double amount) {
if (amount > 0) {
_balance += amount;
}
}

// Method to withdraw money


void withdraw(double amount) {
if (amount > 0 && amount <= _balance) {
_balance -= amount;
}
}

// Method to get the balance


double getBalance() {
return _balance;
}
}

void main() {
BankAccount account = BankAccount(1000);
account.deposit(500);

56
account.withdraw(200);
print('Balance: ${account.getBalance()}'); // Output: Balance: 1300.0
}

Inheritance
Inheritance allows a class to inherit properties and methods from another class.
The class that inherits is called the "subclass" or "derived class," and the class being
inherited from is called the "superclass" or "base class."
Dart supports Single Inheritance, Multi-level Inheritance, Hierarchical Inheritance.
We will use extends keyword to inherit a class

Single Inheritance
Single inheritance occurs when a class inherits from only one superclass. In Dart, all
classes inherit from the Object class by default.

Parent class
/
/
class Animal {
void eat() {
print('Animal is eating');
}
}

// Child class inheriting from Animal


class Dog extends Animal {
void bark() {
print('Dog is barking');
}
}

void main() {
var dog = Dog();
dog.eat(); // Inherited method
dog.bark(); // Method of the subclass
}

Multi-Level Inheritance
Multi-level inheritance occurs when a derived class inherits from another derived class.
This forms a chain of inheritance.

// Grandparent class
class Animal {
void eat() {
print('Animal is eating');
}
}

57
Parent class
/
/
class Dog extends Animal {
void bark() {
print('Dog is barking');
}
}

// Child class inheriting from Dog


class Labrador extends Dog {
void play() {
print('Labrador is playing fetch');
}
}

void main() {
var labrador = Labrador();
labrador.eat(); // Inherited from Animal
labrador.bark(); // Inherited from Dog
labrador.play(); // Method of the subclass
}

Hierarchical Inheritance
Hierarchical inheritance involves multiple derived classes inheriting from the same
base or superclass.

// Parent class
class Animal {
void eat() {
print('Animal is eating');
}
}

// Child class 1
class Dog extends Animal {
void bark() {
print('Dog is barking');
}
}

// Child class 2
class Cat extends Animal {
void meow() {
print('Cat is meowing');
}
}

void main() {
var dog = Dog();
var cat = Cat();

58
dog.eat(); // Inherited method
dog.bark(); // Method of Dog

cat.eat(); // Inherited method


cat.meow(); // Method of Cat
}

Polymorphism
Polymorphism allows methods to do different things based on the object it is acting
upon.
It enables a single interface to represent different underlying forms (data types).

Base class
/
/
class Shape {
void draw() {
print('Drawing a shape.');
}
}

// Subclass
class Circle extends Shape {
@override
void draw() {
print('Drawing a circle.');
}
}

// Subclass
class Square extends Shape {
@override
void draw() {
print('Drawing a square.');
}
}

void main() {
Shape shape1 = Circle();
Shape shape2 = Square();
shape1.draw(); // Output: Drawing a circle.
shape2.draw(); // Output: Drawing a square.
}

Abstraction
Abstraction involves hiding complex implementation details and showing only the
necessary features of an object.
This is typically achieved using abstract classes.

59
Abstract class
/
/
abstract class Shape {
void draw(); // Abstract method
}

// Concrete class
class Triangle extends Shape {
@override
void draw() {
print('Drawing a triangle.');
}
}

void main() {
Shape shape = Triangle();
shape.draw(); // Output: Drawing a triangle.
}

Constructors and Initialization


Constructors are special methods used to initialize objects. Dart allows you to define
named and default constructors.

class Car {
String make;
String model;
// Default constructor
Car(this.make, this.model);
// Named constructor
Car.withDefaults()
: make = 'Unknown',
model = 'Unknown';
}

void main() {
Car car1 = Car('Toyota', 'Corolla');
print('Car 1: ${car1.make} ${car1.model}');
// Output: Car 1: Toyota Corolla
Car car2 = Car.withDefaults();
print('Car 2: ${car2.make} ${car2.model}');
// Output: Car 2: Unknown Unknown
}

Questions
1. What is Dart Programming? Describe Characteristics of Dart.
2. Discuss Data Types in Dart
3. Discuss Dart Conditional statement with example.
4. Explain Dart looping with example.

60
5. Discuss function in Dart.
6. Explain object oriented concepts in Dart.
7. What is widget?
8. Explain stateful widgets life cycle.
9. What is state in flutter?
10. Explain the layers of flutter

61
UNIT-03 Introduction
to Layouts
Type of Layout Widgets
Single Child Widgets
Single Child Widgets are those that can contain only one child. These widgets are used to
modify or control the behavior of a single widget

1. Container: A versatile widget that can be used for layout, styling, and positioning. Can
have padding, margin, borders, and background colors.

Container(
padding: EdgeInsets.all(16.0),
margin: EdgeInsets.all(8.0),
color: Colors.blue,
child: Text('Single Child Widget'),
);

2. Padding : Adds padding around a child widget

Padding(
padding: EdgeInsets.all(16.0),
child: Text('Padded Text'),
);

3. Align : Aligns a child widget within its parent.

Align(
alignment: Alignment.center,
child: Text('Centered Text'),
);

4. Center : Centers its child widget within the parent.

Center(
child: Text('Centered Text'),
);

5. SizedBox : Provides a box with a specific size.

SizedBox(
width: 100,
height: 100,
child: Text('Sized Box'),
);

6. Expanded : Expands a child widget to fill available space in the parent

63
Expanded(
child: Text('Expanded Text'),
);

Multiple Child Widgets


Multiple Child Widgets can contain more than one child. These widgets are often used to
create complex layouts by arranging multiple widgets in a specific order or alignment.

1. Column : Arranges its children in a vertical direction.

Column(
children: <Widget>[
Text('First'),
Text('Second'),
Text('Third'),
],
);

2. Row : Arranges its children in a horizontal direction.

Row(
children: <Widget>[
Icon(Icons.star),
Icon(Icons.star),
Icon(Icons.star),
],
);

3. Stack : Places its children on top of each other, allowing for overlap.

Stack(
children: <Widget>[
Container(
width: 100,
height: 100,
color: Colors.red,
),
Positioned(
top: 20,
left: 20,
child: Container(
width: 50,
height: 50,
color: Colors.blue,
),
),
],
);

64
4. ListView : A scrollable list of widgets arranged in a linear fashion (either vertical or
horizontal).

ListView(
children: <Widget>[
ListTile(title: Text('Item 1')),
ListTile(title: Text('Item 2')),
ListTile(title: Text('Item 3')),
],
);

5. GridView : A scrollable, 2D array of widgets arranged in rows and columns.

GridView.count(
crossAxisCount: 2,
children: <Widget>[
Container(color: Colors.red, height: 100),
Container(color: Colors.blue, height: 100),
Container(color: Colors.green, height: 100),
Container(color: Colors.yellow, height: 100),
],
);

6. Wrap : Wraps its children into a vertical or horizontal flow, breaking lines as necessary.

Wrap(
spacing: 8.0,
runSpacing: 4.0,
children: <Widget>[
Chip(label: Text('Chip 1')),
Chip(label: Text('Chip 2')),
Chip(label: Text('Chip 3')),
],
);

7. Flex : A widget that arranges its children in a one-dimensional array (similar to Row
and Column but more customizable).

Flex(
direction: Axis.horizontal,
children: <Widget>[
Expanded(child: Container(color: Colors.red, height:
50)),
Expanded(child: Container(color: Colors.blue, heigh
t: 50)),
],
);

Conclusion
65
Single Child Widgets are used when you need to modify or layout a single widget.
Multiple Child Widgets are used for more complex layouts that involve arranging
multiple widgets in different patterns or directions.

Advanced Layout Application


In this section, let us learn how to create a complex user interface of product listing with
custom design using both single and multiple child layout widgets.

For this purpose, follow the sequence given below:

Create a new Flutter application in VS Code, store_app.


Replace the main.dart code with following code:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Product layout demo home page'),
);
}
}

Here, We have created MyHomePage widget by extending StatelessWidget instead of


default StatefulWidget and then removed the relevant code.
Now, create a new widget, ProductBox according to the specified design as shown
below

import 'package:flutter/material.dart';

66
class ProductBox extends StatelessWidget {
const ProductBox({
Key? key,
this.name = '',
this.description = '',
this.price = 0,
this.image = '',
}) : super(key: key);

final String name;


final String description;
final int price;
final String image;

@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(2),
height: 120,
child: Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Image.asset(
'assets/appimages/' + image,
width: 100, // Adjust width as needed
height: 100, // Adjust height as needed
fit: BoxFit.cover, // Adjust BoxFit as needed
),
Expanded(
child: Container(
padding: const EdgeInsets.all(5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
name,
style: const TextStyle(fontWeight: FontWeight.bold),
),
Text(
description,
maxLines: 2, // Adjust as needed
overflow: TextOverflow.ellipsis,
),
Text(
'Price: \$${price.toString()}',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),

67
),
],
),
),
);
}
}

Please observe the following in the code:


ProductBox has used four arguments as specified below:
name - Product name
description - Product description
price - Price of the product
image - Image of the product
ProductBox uses seven build-in widgets as specified below:
Container Expanded Row Column Card Text
ProductBox is designed using the above mentioned widget. The arrangement or
hierarchy of the widget is specified in the diagram shown below

Now, place some dummy image (see below) for product information in the assets
folder of the application and configure the assets folder in the pubspec.yaml file as
shown below
Definition images in pubspec.yam
Finally, Use the ProductBox widget in the MyHomePage widget as specified below:

import 'package:flutter/material.dart';

class MyHomePage extends StatelessWidget {


const MyHomePage({super.key, this.title = ''});
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Product Listing")),
body: ListView(

68
shrinkWrap: true,
padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0),
children: <Widget>[
ProductBox(
name: "iPhone",
description: "iPhone is the stylist phone ever",
price: 1000,
image: "iPhone.jpg"),
ProductBox(
name: "Pixel",
description: "Pixel is the most featureful phone ever",
price: 800,
image: "pixel.png"),
ProductBox(
name: "Laptop",
description: "Laptop is most productive development tool",
price: 2000,
image: "laptop.jpg"),
ProductBox(
name: "Tablet",
description: "Tablet is the most useful device ever for meeting",
price: 1500,
image: "tablet.jpg"),
ProductBox(
name: "Floppy Drive",
description: "Floppy drive is useful rescue storage medium",
price: 20,
image: "floppy.jpg"),
],
),
);
}
}

Here,we have used ProductBox as children of ListView widget.


The complete code (main.dart) of the product layout application (store_app) is as
follows:

import 'package:flutter/material.dart';

class MyHomePage extends StatelessWidget {


const MyHomePage({super.key, this.title = ''});
final String title;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Product Listing")),
body: ListView(
shrinkWrap: true,
padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0),

69
children: const <Widget>[
ProductBox(
name: "iPhone",
description: "iPhone is the most stylish phone ever.",
price: 1000,
image: "iPhone.jpg",
),
ProductBox(
name: "Pixel",
description: "Pixel is the most featureful phone.",
price: 800,
image: "pixel.png",
),
ProductBox(
name: "Laptop",
description: "Laptop is the most productive development tool.",
price: 2000,
image: "laptop.jpg",
),
ProductBox(
name: "Tablet",
description: "Tablet is the most useful device for meetings.",
price: 1500,
image: "tablet.jpg",
),
ProductBox(
name: "Floppy Drive",
description: "Floppy drive is useful for rescue storage.",
price: 20,
image: "floppy.jpg",
),
],
),
);
}
}

class ProductBox extends StatelessWidget {


const ProductBox({
super.key,
this.name = '',
this.description = '',
this.price = 0,
this.image = '',
});

final String name;


final String description;
final int price;
final String image;

@override

70
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(2),
height: 120,
child: Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Image.asset(
"assets/appimages/$image",
width: 100,
height: 100,
fit: BoxFit.cover,
),
Expanded(
child: Container(
padding: const EdgeInsets.all(5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
name,
style: const TextStyle(fontWeight: FontWeight.bold),
),
Text(
description,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
Text(
"Price: \$${price.toString()}",
style: const TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
),
],
),
),
);
}
}

Gestures
In Flutter, gestures are a way to detect and respond to user interactions like taps, swipes,
and drags. Flutter provides a robust system to handle gestures, making it easy to create
interactive and responsive user interfaces. The primary widget used for handling gestures
in Flutter is the GestureDetector .

71
Key Concepts of Gestures
GestureDetector : The GestureDetector widget is used to capture a wide range of
gestures. It wraps around another widget and intercepts gestures like taps, double
taps, long presses, swipes, and more.
Common Gestures:
Tap: A single quick press.
Double Tap: Two quick presses in succession.
Long Press: A press that lasts for more than a second.
Swipe: A quick drag motion in a particular direction.
Drag: A press followed by movement.
Using GestureDetector : The GestureDetector widget has properties like onTap ,
onDoubleTap , onLongPress , onPanUpdate , and others, each corresponding to a
specific gesture.

Example
Let's create a simple example where a container changes its color when the user taps on it

import 'package:flutter/material.dart';

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

class GestureExample extends StatefulWidget {


@override
_GestureExampleState createState() => _GestureExampleState();
}

class _GestureExampleState extends State<GestureExample> {


Color _color = Colors.blue;
void _changeColor() {
setState(() {
_color = _color == Colors.blue ? Colors.red : Colors.blue;
});
}

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Gesture Example'),
),
body: Center(
child: GestureDetector(
onTap: _changeColor,
child: Container(

72
width: 200,
height: 200,
color: _color,
child: Center(
child: Text(
'Tap me',
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
),
),
),
),
);
}
}

Here The GestureDetector wraps a Container widget. The onTap callback is used to
change the color of the container when the user taps on it.
The _changeColor method is called when the container is tapped, toggling its color
between blue and red.

Advanced Gestures
1. Drag Gesture: You can detect dragging using the onPanUpdate callback. This is useful
for interactive elements like sliders or custom scroll views.

GestureDetector(
onPanUpdate: (details) {
// Handle drag
},
child: Container(
color: Colors.green,
width: 200,
height: 200,
),
);

2. Swipe Gesture: Detecting swipes involves using onHorizontalDragEnd or


onVerticalDragEnd callbacks. These are triggered when the user swipes in a particular
direction.

dartCopy code
GestureDetector(
onHorizontalDragEnd: (details) {
// Handle horizontal swipe
},
child: Container(
color: Colors.orange,
width: 200,
73
height: 200,
),
);

3. Pinch and Zoom: For more advanced interactions like pinch-to-zoom, you would
typically use the onScaleUpdate callback.

dartCopy code
GestureDetector(
onScaleUpdate: (details) {
// Handle pinch-to-zoom
},
child: Container(
color: Colors.purple,
width: 200,
height: 200,
),
);

State Management in Flutter


State management is a critical concept in Flutter that involves managing the state, or
the data that your UI depends on.
In Flutter, state refers to any data that can change over time, such as user input, the
result of an asynchronous operation, or even the appearance of a widget.
Flutter provides various ways to manage state, depending on the complexity and
scope of the application.
These methods can generally be categorised into Ephemeral State Management and
Application State Management.

Ephemeral State Management


Ephemeral state (also known as UI state or local state) is state that is local to a single
widget.
This type of state is usually simple and short-lived, like whether a checkbox is checked
or the current page of a PageView .
Ephemeral state can be managed directly within the widget itself using the State
class
Example:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {


@override

74
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Ephemeral State Example'),
),
body: CounterWidget(),
),
);
}
}

class CounterWidget extends StatefulWidget {


@override
_CounterWidgetState createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {


int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}

@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text('$_counter', style: Theme.of(context).textTheme.headline4),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('Increment'),
),
],
),
);
}
}

In this example, the _counter variable is managed within the CounterWidget 's state
class ( _CounterWidgetState ).
This state is ephemeral because it only affects the CounterWidget and is not needed
elsewhere in the app.
When the button is pressed, setState is called, which tells Flutter to rebuild the
widget with the updated state.
75
Application State Management
Application state (also known as shared state or global state) refers to state that
needs to be shared across multiple parts of the application.
This kind of state can include user preferences, authentication status, or data fetched
from an API that needs to be accessible throughout the app.
Managing application state can be more complex because it often involves sharing
data between different parts of the app.
Flutter provides several approaches to managing application state, including
InheritedWidget , Provider , Bloc , Riverpod , and more.

Step 1: Define the App's State in a StatefulWidget

You can use a StatefulWidget to hold the application state (in this case, a counter)
and manage its changes.

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {


@override
_MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {


int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App State Management',
theme: ThemeData(primarySwatch: Colors.blue),
home: HomeScreen(
counter: _counter,
incrementCounter: _incrementCounter,
),
);
}
}

Here, _counter is our application-wide state. We have a function


_incrementCounter() that updates the state. setState() is used to rebuild the widget
tree when the state changes.

76
Step 2: Create the HomeScreen and Pass the State

The HomeScreen widget will display the counter value and allow navigation to a
second screen where the counter can be updated.

import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {


final int counter;
final VoidCallback incrementCounter;

HomeScreen({
required this.counter,
required this.incrementCounter,
});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home Screen'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Counter value:',
style: TextStyle(fontSize: 20),
),
Text(
'$counter',
style: Theme.of(context).textTheme.headline4,
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(
counter: counter,
incrementCounter: incrementCounter,
),
),
);
},
child: const Text('Go to Second Screen'),
),
],
),

77
),
floatingActionButton: FloatingActionButton(
onPressed: incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}

class SecondScreen extends StatelessWidget {


final int counter;
final VoidCallback incrementCounter;

SecondScreen({
required this.counter,
required this.incrementCounter,
});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Second Screen'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Counter value on Second Screen:',
style: TextStyle(fontSize: 20),
),
Text(
'$counter',
style: Theme.of(context).textTheme.headline4,
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Back to Home Screen'),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),

78
);
}
}

The HomeScreen takes the current counter value and the incrementCounter function as
parameters.
The button navigates to the SecondScreen while passing the same state and callback
function to allow state management across screens.

Step 3: Create the SecondScreen and Access the Shared State

The SecondScreen will display the counter value and allow the user to increment the
counter.

import 'package:flutter/material.dart';

class SecondScreen extends StatelessWidget {


final int counter;
final VoidCallback incrementCounter;

SecondScreen({
required this.counter,
required this.incrementCounter,
});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Second Screen'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Counter value:',
style: TextStyle(fontSize: 20),
),
Text(
'$counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
79
);
}
}

Here CounterModel is a ChangeNotifier that holds the application state (in this case,
the counter value).
The ChangeNotifierProvider makes CounterModel available to the entire widget tree.
CounterWidget listens to changes in CounterModel using context.watch() and
updates the UI accordingly.
The FloatingActionButton increments the counter, demonstrating how application
state can be modified and shared across different parts of the app.

Navigation and Routing in Flutter


Navigation and routing in Flutter allow you to move between different screens (or
pages) in your app, creating a smooth user experience.
Flutter provides a robust set of APIs to handle navigation and routing, making it easy
to build complex applications with multiple screens.

1. Route: A Route in Flutter is an abstraction for a screen or page. When you navigate to
a new screen, you are pushing a new Route onto the navigation stack.
2. Navigator: The Navigator is a widget that manages a stack of Route objects. It
provides methods to navigate between routes, such as push , pop , and others.
3. Routing: Routing refers to the process of defining and handling the routes in your app.
This includes defining named routes and handling dynamic routing for more complex
scenarios.

Pushing a New Screen


To navigate to a new screen, you use the Navigator.push method. This method takes
a Route and adds it to the navigation stack, displaying the new screen.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
home: FirstScreen(),
);
}
}

class FirstScreen extends StatelessWidget {


@override
Widget build(BuildContext context) {

80
return Scaffold(
appBar: AppBar(title: Text('First Screen')),
body: Center(
child: ElevatedButton(
child: Text('Go to Second Screen'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
},
),
),
);
}
}

class SecondScreen extends StatelessWidget {


@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Second Screen')),
body: Center(
child: ElevatedButton(
child: Text('Go Back'),
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
}

Navigator.push : This method is used to navigate to a new screen by adding a new


route to the stack.
MaterialPageRoute : This creates a route that uses a platform-specific animation to
transition between screens.
Navigator.pop : This removes the top route from the stack, returning to the previous
screen.

Named Routes
Named routes allow you to define routes centrally and refer to them by name when
navigating.
This is useful for larger apps where route management becomes complex.

import 'package:flutter/material.dart';

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

81
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// Define the named routes
routes: {
'/': (context) => FirstScreen(),
'/second': (context) => SecondScreen(),
},
initialRoute: '/',
);
}
}

class FirstScreen extends StatelessWidget {


@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('First Screen')),
body: Center(
child: ElevatedButton(
child: Text('Go to Second Screen'),
onPressed: () {
Navigator.pushNamed(context, '/second');
},
),
),
);
}
}

class SecondScreen extends StatelessWidget {


@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Second Screen')),
body: Center(
child: ElevatedButton(
child: Text('Go Back'),
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
}

Named Routes: Routes are defined in a map within the MaterialApp widget using the
routes property.

82
Navigator.pushNamed : This method is used to navigate to a route using its name,
which simplifies the navigation process, especially in larger apps.

Passing Data Between Screens


You can pass data between screens when navigating by using the constructor of the
screen you're navigating to or by using arguments in named routes.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
home: FirstScreen(),
);
}
}

class FirstScreen extends StatelessWidget {


@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('First Screen')),
body: Center(
child: ElevatedButton(
child: Text('Go to Second Screen with Data'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
SecondScreen(data: 'Hello from First Screen!'),
),
);
},
),
),
);
}
}

class SecondScreen extends StatelessWidget {


final String data;
SecondScreen({required this.data});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Second Screen')),

83
body: Center(
child: Text(data),
),
);
}
}

The data is passed to the SecondScreen through its constructor. This allows the
SecondScreen to access and display the data.

Scoped Model in Flutter


ScopedModel is a state management solution in Flutter that allows you to share state
across your application in a way that is simple, efficient, and effective.
It was popular before more modern solutions like Provider and Riverpod became
mainstream, but it’s still useful for understanding how state can be managed in a
structured manner.

Key Concept of Scoped Model


1. Model: A Model is a class that holds the application state. It extends the Model class
from the scoped_model package. Any changes to the model can trigger a rebuild of
widgets that depend on it.
2. ScopedModel: The ScopedModel widget is used to provide the Model to the widget
tree. It acts as a provider of the state and can be accessed by any widget in the
subtree.
3. ScopedModelDescendant: ScopedModelDescendant is a widget that listens to changes
in the Model . When the state in the Model changes, only those
ScopedModelDescendant widgets that depend on the model will rebuild.

Example
Let’s create a simple counter application using ScopedModel

Step 1: Add the scoped_model Dependency

First, add the scoped_model package to your pubspec.yaml file:

yamlCopy code
dependencies:
flutter:
sdk: flutter
scoped_model: ^1.0.1

Then, run flutter pub get to install the package.

Step 2: Define the Model

84
Create a CounterModel class that will hold the counter value and provide methods to
modify it.

import 'package:scoped_model/scoped_model.dart';
class CounterModel extends Model {
int _counter = 0;
int get counter => _counter;
void increment() {
_counter++;
notifyListeners(); // Notifies all listening widgets to rebuild
}
}

Step 3: Provide the Model Using ScopedModel

Wrap your MaterialApp or Scaffold with a ScopedModel to provide the model to the
widget tree.

import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import 'counter_model.dart'; // Import the model

void main() {
runApp(
ScopedModel<CounterModel>(
model: CounterModel(),
child: MyApp(),
),
);
}

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
home: CounterScreen(),
);
}
}

Step 4: Consume the Model Using ScopedModelDescendant

Use ScopedModelDescendant to access and react to changes in the model.

import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import 'counter_model.dart';

class CounterScreen extends StatelessWidget {


@override

85
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Scoped Model Example'),
),
body: Center(
child: ScopedModelDescendant<CounterModel>(
builder: (context, child, model) {
return Text(
'Counter: ${model.counter}',
style: Theme.of(context).textTheme.headline4,
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
ScopedModel.of<CounterModel>(context).increment();
},
child: Icon(Icons.add),
),
);
}
}

Explanation of Code

CounterModel: The CounterModel class holds the counter value and a method to
increment it. It extends Model and calls notifyListeners() whenever the counter
value changes. This ensures that all widgets listening to this model get rebuilt with the
updated state.
ScopedModel: The ScopedModel widget provides the CounterModel to the entire
widget tree. Any widget within this tree can access the model and react to changes.
ScopedModelDescendant: This widget is used to listen to changes in CounterModel .
When the CounterModel updates, the ScopedModelDescendant will rebuild, displaying
the new counter value.
FloatingActionButton: The button triggers the increment() method in the
CounterModel to increase the counter value and notify listeners to update the UI.

Advantages of Scoped Model

Simplicity: It’s easy to understand and use, making it suitable for small to medium-
sized applications.
Separation of Concerns: Helps to separate the UI from the business logic and state
management.
Efficient Rebuilds: Only the widgets that depend on the model are rebuilt when the
state changes.

Limitations
86
Scalability: While good for small projects, ScopedModel can become difficult to
manage in larger applications with more complex state requirements.
Less Community Support: Over time, Provider has become the preferred state
management solution, so ScopedModel has less community support and fewer
resources.

Questions
1. What is state in flutter?
2. How state management is used in Flutter?
3. Discuss Layout in Flutter.
4. What is widget?
5. What is the difference between Single Child Widgets- Multiple Child Widgets?
6. Explain Flutter Navigation and Routing.
7. What is the difference between stateless widgets and stateful widgets?
8. How does the 'scoped model' help in state management in Flutter?

87
UNIT-04 Animation in
Flutter
Introduction
What is Animation? Animation is a sequence of images or frames that create the
illusion of movement.
Define: Animation in Flutter refers to the dynamic transitions or movements applied to
widgets over a period of time, often making the UI more interactive and visually
appealing.
Types of Animations in Flutter:
Explicit Animation: More control over the animation with specific start/stop
points and time control.
Implicit Animation: Easier to implement, animates based on changes in property
values.

AnimationController() Tween()
An AnimationController is responsible for A Tween defines the range of values
controlling the animation's behavior. It lets you that an animation should interpolate
define the duration, start, stop, reverse, and between. It doesn't perform
repeat the animation. animation itself but specifies the start
( begin ) and end ( end ) values for
the controller to animate.
Key Properties Key Properties
duration: Specifies how long the animation begin: The starting value of the
should last. animation.
vsync : Reduces unnecessary CPU/GPU work end: The final value after the
by syncing animation with the screen's refresh animation completes.
rate. It's mandatory and typically provided by
using SingleTickerProviderStateMixin .
How to use AnimationController() ? How to use Tween() ?
- Create an AnimationController in the - Pair a Tween with an
initState() method. AnimationController .
- Control the animation with methods like - Use .animate() to bind the Tween
forward() , reverse() , or repeat() . to the controller.

Implicit Animation
Implicit animations automatically handle the changes in a widget's property, such as
size, color, or position, over a period of time.
Implicit Animations are simple and easy to use for basic property changes.

import 'package:flutter/material.dart';

class ImplicitAnimationExample extends StatefulWidget {


@override
_ImplicitAnimationExampleState createState() =>

89
_ImplicitAnimationExampleState();
}

class _ImplicitAnimationExampleState extends State<ImplicitAnimationExample> {


double _size = 100;

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
onTap: () {
setState(() {
_size = _size == 100 ? 200 : 100; // Change size on tap
});
},
child: AnimatedContainer(
width: _size,
height: _size,
color: Colors.blue,
duration: Duration(seconds: 1), // Animation duration
),
),
),
);
}
}

Explicit Animation:
Explicit animations require manual control over the animation lifecycle, such as
starting, stopping, or reversing the animation.
Explicit Animations offer greater control and flexibility for more complex animations.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Explicit Animation Example',
home: AnimationExample(),
);
}
}

90
class AnimationExample extends StatefulWidget {
@override
_AnimationExampleState createState() => _AnimationExampleState();
}

class _AnimationExampleState extends State<AnimationExample> with


SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
bool _isAnimating = false;

@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = Tween<double>(begin: 0, end: 300).animate(_controller)
..addListener(() {
setState(() {});
});
}

void _startAnimation() {
if (!_isAnimating) {
_isAnimating = true;
_controller.forward(from: 0).then((_) {
_isAnimating = false; // Resetting the flag after animation completes
});
}
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Explicit Animation'),
),
body: GestureDetector(
onTap: _startAnimation,
child: Center(
child: Container(
margin: EdgeInsets.only(left: _animation.value),
width: 100,
height: 100,

91
color: Colors.blue,
),
),
),
);
}
}

Workflow of Animation in Flutter


Set Up the Animation Controller:
The AnimationController manages the timing of the animation (when it starts,
how long it lasts, and its progress).
You must specify duration and vsync to sync with the screen's refresh rate.

_controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);

Define a Tween (Optional):


A Tween defines the range of values to interpolate between, such as size, color, or
opacity. It doesn't animate itself but works with the AnimationController .

_animation = Tween<double>(begin: 100, end: 300).animate(_controller);

Build the UI with Animated Widgets:


Use AnimatedBuilder or similar widgets to rebuild the UI based on the current
value of the animation. The widget changes dynamically as the animation
progresses.

AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Container(
width: _animation.value, // Animation value updates over time
height: _animation.value,
color: Colors.blue,
);
},
);

Start/Control the Animation:


Trigger the animation with methods like forward() , reverse() , or repeat() .

_controller.forward(); // Starts the animation

92
Dispose the Controller:
Always dispose of the AnimationController when it's no longer needed to free up
resources

@override
void dispose() {
_controller.dispose();
super.dispose();
}

Working Example of Flutter Animation:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Spring Animation Example',
home: AnimationExample(),
);
}
}

class AnimationExample extends StatefulWidget {


@override
_AnimationExampleState createState() => _AnimationExampleState();
}

class _AnimationExampleState extends State<AnimationExample> with


SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;

@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 2000),
vsync: this,
)..addListener(() {
setState(() {});
});

_animation = Tween<double>(begin: 0, end: 300).animate(

93
CurvedAnimation(
parent: _controller,
curve: Curves.elasticOut,
),
);
}

void _startAnimation() {
_controller.forward(from: 0).then((_) {
// Animation completed
});
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Spring Animation'),
),
body: GestureDetector(
onTap: _startAnimation,
child: Center(
child: Container(
margin: EdgeInsets.only(left: _animation.value),
width: 100,
height: 100,
color: Colors.blue,
),
),
),
);
}
}

Introduction to Package
Package is a collection of reusable code, assets, and functionality that you can
integrate into your project to extend its features.
Types of Packages:
Dart Packages: Contain pure Dart code.
Flutter Packages: Contain both Dart and Flutter-specific code (including widgets
and plugins)
To use a package, add it to your pubspec.yaml file

94
dependencies:
http: ^0.13.3 # Example of the HTTP package for network
requests

Then, import and use the package in your Flutter code

import 'package:http/http.dart' as http;

var response = await http.get(Uri.parse('https://packagelin


k.com'));

Using Dart Packages


Find a Package:
Visit Pub.dev website: which is the official package repository for Dart and Flutter.
Search for Packages: Use the search bar to find the package you need
Add the Package to Your Project:
Open pubspec.yaml : This file is located in the root directory of your Flutter
project.
Add Dependency: Under the dependencies section, add the package name and
version. You can specify a version or use a caret (^) to allow updates.

dependencies:
flutter:
sdk: flutter
http: ^0.13.3 # Example: Adding the HTTP package

Save the File: After adding the package, save the pubspec.yaml file. This action will
trigger Flutter to download the package.
Import the Package:
In your Dart file, import the package using the import statement.

import 'package:http/http.dart' as http; // Importing the HTTP package

Use the Package:

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; // Importing the HTTP package
import 'dart:convert';

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

class MyApp extends StatelessWidget {


@override

95
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'HTTP Package Example',
home: MyHomePage(),
);
}
}

class MyHomePage extends StatefulWidget {


@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {


String _data = "Press the button to fetch quote";

Future<void> fetchData() async {


final response = await
http.get(Uri.parse('https://dummyjson.com/quotes/random'));

if (response.statusCode == 200) {
final Map<String, dynamic> data = json.decode(response.body);
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Quote'),
content: Text("${data['quote']} \n\n-${data['author']}"),
actions: [
TextButton(
child: Text('Close'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
} else {
throw Exception('Failed to load data');
}
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('HTTP Package Example'),
),
body: Center(

96
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_data),
SizedBox(height: 20),
ElevatedButton(
onPressed: fetchData,
child: Text('Fetch Quote'),
),
],
),
),
);
}
}

Develop a Flutter Plugin Package


Create the Plugin:
Use the Flutter CLI command flutter create --template=plugin
my_flutter_plugin to scaffold a new plugin project
Understand the Structure:
The plugin will contain:
lib/my_flutter_plugin.dart : The main Dart file for the plugin API.
android/src/main/kotlin/... : Android-specific implementation.
ios/Classes/MyFlutterPlugin.swift : iOS-specific implementation.

Write the Dart API:


Define the public interface of the plugin in lib/my_flutter_plugin.dart ,
specifying methods that will be callable from Flutter.
Implement Platform-Specific Code:
In the Android Kotlin file, implement the methods defined in Dart to handle
platform-specific logic.
In the iOS Swift file, do the same for iOS functionality.
Test the Plugin:
Create a Flutter app and add your plugin as a dependency in the pubspec.yaml .
Use the methods from your plugin within the app to verify that everything works
as expected.

Accessing Rest API


Accessing a REST API in Flutter involves sending HTTP requests to a server and handling
the responses. Here's a step-by-step guide on how to do it, along with explanations of
each part.

Step-01: Add Dependencies


First, you need to add the http package to your project. This package simplifies
making HTTP requests.

97
Open your pubspec.yaml file.
Add the following dependency under dependencies :

dependencies:
flutter:
sdk: flutter
http: ^0.13.3 # Check for the latest version on pub.dev

Save the file and run flutter pub get to install the package.
Step-02: Import the Package:

import 'package:http/http.dart' as http;

Step-03: Create a Function to Fetch Data:


You can create a function that fetches data from the API. This function will make
an HTTP GET request and process the response.

Future<void> fetchData() async {


final response = await
http.get(Uri.parse('<https://jsonplaceholder.typicode.com/posts>'));
if (response.statusCode == 200) {
// If the server returns a 200 OK response, parse the data
print(response.body); // Handle the response data
} else {
// If the server does not return a 200 OK response, throw an error
throw Exception('Failed to load data');
}
}

Step-04: Call the Function:


You can call this fetchData function within your Flutter app, for example, in the
initState method of a stateful widget.

@override
void initState() {
super.initState();
fetchData(); // Call the fetch function when the widget
initializes
}

Step-05: Parsing JSON Data:


If the API returns JSON data, you can use the dart:convert package to decode
it.

import 'dart:convert'; // Import for JSON parsing


import 'package:http/http.dart' as http; // Import for HTTP requests

98
Future<void> fetchData() async {
final response = await
http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));

if (response.statusCode == 200) {
// Decode the JSON response
List<dynamic> data = json.decode(response.body);
print(data); // Do something with the data
} else {
throw Exception('Failed to load data');
}
}

Accessing Product service API


Example Use Case: Fetching Product Data

Here’s an explanation of how you can retrieve product data from an API in Flutter:

1. Create a Function to Fetch Products:


This function sends a GET request to the API to retrieve product data.
2. Handle the Response:
If the request is successful, you will receive a JSON response, typically containing
product details like the product ID, name, description, price, etc.
3. Parse the JSON:
Use dart:convert to parse the JSON response into Dart objects. You can map the
JSON data to a Dart model class for easier access
4. Display the Data:
Once you’ve fetched and parsed the product data, you can display it in a list or
grid using Flutter’s UI widgets like ListView or GridView

SQLite
SQLite is a lightweight, embedded database that is commonly used for local data
storage in mobile apps, including Flutter applications.
In Flutter, the sqflite package provides a way to interact with SQLite databases. It
allows you to perform operations like creating tables, inserting, updating, querying,
and deleting data in a local database stored on the device.
Key Features of SQLite in Flutter
Local Data Storage: Data is stored locally on the user's device.
SQL Queries: You can use SQL queries to interact with the database.
Offline Mode: It enables offline capabilities in your app.
Persistent Storage: Data remains even if the app is closed.

Steps to Use SQLite in Flutter


1. Add the sqflite and path Packages:

99
First, add the required dependencies in the pubspec.yaml file.

dependencies:
sqflite: ^2.0.0+4 # Ensure you're using the latest version
path: ^1.8.0 # Required to define database path

2. Import the Required Packages:


Import sqflite to interact with the database and path to help find the correct
location for storing the database.
3. Create a Database:
Use the openDatabase() function to open or create a database. You define the
database schema by creating tables using SQL.
4. Perform CRUD Operations:
SQLite allows you to perform the following operations:
Create: Insert new rows of data.
Read: Query existing data.
Update: Modify existing data.
Delete: Remove data
5. Close the Database:
After finishing operations, it’s a good practice to close the database connection
using the close() function.

SQLite Workflow in Flutter


1. Database Path: Define the path where the database will be stored on the device.
2. Open/Initialize the Database: Open or create the database and define the schema
(tables and columns).
3. Insert/Query Data: Use SQL commands to insert, query, update, or delete data from
the database.
4. Close the Database: Close the database connection when it’s no longer needed.

CRUD Operations in SQLite:


Create Table: Define your database schema by creating tables for storing data.
Insert Data: Insert records into the database.
Read Data: Query data to retrieve it, usually using SQL’s SELECT statement.
Update Data: Modify existing data in the database.
Delete Data: Remove records from the database.

Example Use Case:


User Authentication: Store user credentials locally for offline login.
Product Catalog: Store a product catalog that can be accessed offline.
Notes App: Save user-created notes that persist across app sessions.

100
Cloud Fire Store:
Cloud Firestore is a NoSQL database from Google’s Firebase platform that allows you
to store, sync, and query data in real-time for your Flutter apps.
It provides seamless synchronization between your app and the cloud, enabling both
online and offline functionality.
Unlike SQLite (which is local), Firestore stores data in the cloud, making it suitable for
apps that require real-time collaboration or data sharing across devices.

Key Features of Cloud Firestore:


Real-time Sync: Automatically syncs data across devices and the cloud in real-time.
NoSQL Structure: Uses collections and documents to store data, not rows and
columns like SQL databases.
Scalable: Designed to scale as your app grows without managing infrastructure.
Offline Support: Data can be accessed offline, and changes are synced when the user
comes online.
Security Rules: You can define rules to control who can access and modify data.

Structure
1. Collections: Firestore organizes data into collections, which are similar to tables in
SQL databases. Each collection can hold multiple documents
2. Documents: Collections contain documents, and each document holds data in key-
value pairs (fields). Documents can also have sub-collections.
3. Fields: Fields in a document store individual data, such as strings, numbers, arrays, or
other data types

Steps to Use Cloud Firestore in Flutter:


1. Add Firebase Dependencies:
Add the Firebase and Firestore dependencies in pubspec.yaml
2. Initialize Firebase:
Before using Firestore, initialize Firebase in your app, usually done in the main()
function.
3. Access Firestore:
Use the Firestore instance to perform operations like adding, reading, updating,
and deleting data.
4. CRUD Operations:
Create: Add a new document to a collection.
Read: Retrieve documents from a collection.
Update: Modify existing documents.
Delete: Remove documents.

Example Use Case:


101
User Profiles: Store and sync user profiles in real-time across devices.
Chat Applications: Messages are instantly synced between users with real-time
updates.
Product Inventory: Keep track of inventory, syncing data between multiple devices
and the cloud.

Internalization in Flutter
Internationalization (i18n) in Flutter enables apps to support multiple languages,
regional formats, and cultural preferences, making them accessible to users
worldwide.

Key Concepts:
Localization (l10n): The process of translating content into different languages and
adapting to regional formats.
Locale: Represents the language and region (e.g., en_US for English US).
intl Package: Helps manage localized text, dates, numbers, and formatting

Steps for Implementing i18n in Flutter


1. Add Dependencies: Include flutter_localizations and intl in your pubspec.yaml
2. Define Supported Locales: Set supported locales in the MaterialApp widget.
3. Create .arb Files: Create separate .arb (Application Resource Bundle) files (JSON-
like) for each language with key-value pairs for translations.
4. Use LocalizationDelegates : Set up delegates to load the appropriate localized
resources.
5. Access Localized Strings: Use the generated code to display text based on the current
locale.
6. Automatic Locale Detection: The app can automatically detect and adapt to the
user's device language settings.

Benefits:
Global Reach: Supports multiple languages for a wider audience
Cultural Adaptation: Adjusts dates, numbers, and currencies based on locale.

Testing in Flutter
Testing in Flutter is an essential practice that ensures the quality, reliability, and
performance of your applications.
Flutter provides a comprehensive testing framework that allows developers to write
unit tests, widget tests, and integration tests.

Types of Testing in Flutter


1. Unit Testing:

102
Purpose: Tests individual functions, methods, or classes in isolation to ensure they
behave as expected.
Tools: Uses the test package, which provides functionalities for writing and
running tests.
Example: Testing a function that calculates the sum of two numbers.
2. Widget Testing:
Purpose: Tests a single widget’s behavior and appearance, including its
interaction with the user interface.
Tools: Uses the flutter_test package, allowing you to build a widget in a test
environment and interact with it.
Example: Testing a button click that changes the text displayed on the screen.
3. Integration Testing:
Purpose: Tests the complete app or a large portion of it, ensuring that various
parts work together as expected.
Tools: Uses the integration_test package, which runs tests on a real device or
emulator.
Example: Testing a user flow, such as logging in, navigating to a different screen,
and performing actions.

Benefits of Testing in Flutter


Early Bug Detection: Identify and fix issues early in the development process,
reducing costs and time spent on debugging later.
Improved Code Quality: Encourages better code design and architecture, making the
codebase easier to maintain and refactor.
Confidence in Changes: Ensures that new features and bug fixes don’t break existing
functionality, providing confidence when modifying code.
Documentation: Tests can serve as documentation for how code is expected to
behave.

Testing Framework in Flutter


Test Runner: Automatically discovers and runs tests.
Matchers: Provide a way to specify expectations and compare actual values in tests.
Widgets: Use WidgetTester to interact with widgets in a test environment.

Deployment of Flutter App on Android


Deploying a Flutter app on Android involves several key steps, from preparing your
app for release to uploading it to the Google Play Store.
Here’s a concise guide on how to do it.

1. Set Up Your App for Release:


Update pubspec.yaml : Ensure all dependencies are up to date and set your app
version and build number.

103
Remove Debug Code: Make sure to comment out or remove any debug prints or
testing-related code.
2. Generate a Release Build:
Run the following command in your terminal to build the APK (Android Package)
for release:
This command generates an APK file in the build/app/outputs/apk/release/
directory.

flutter build apk --release

3. Signing the APK:


To publish your app, you need to sign it with a release key.
Create a keystore file if you don't have one

keytool -genkey -v -keystore your_keystore_name.jks - keyalg RSA -keysize 2048 -


validity 10000 -alias your_ alias_name

Update the android/app/build.gradle file with the signing configuration:

android {
...
signingConfigs {
release {
keyAlias 'your_alias_name'
keyPassword 'your_key_password'
storeFile file('path_to_your_keystore.jks')
storePassword 'your_store_password'
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}

4. Build the Signed APK:


Use the following command to build the signed APK:
This will create a signed APK file that can be distributed

flutter build apk --release

5. Test the Release APK:


Before uploading, install the APK on a physical device to ensure it runs as
expected.

104
adb install build/app/outputs/apk/release/app-release.ap k

6. Prepare for Play Store Submission:


Create a developer account on the Google Play Console.
Prepare app details (description, screenshots, icons, etc.) for your app listing.
7. Upload to Google Play Store:
Go to the Google Play Console, select "Create Application," and fill in the required
details.
Upload your signed APK and complete the app submission process.
8. Review and Publish:
After submitting, your app will undergo a review process by Google. Once
approved, it will be available on the Play Store

Questions
1. Explain the methods of REST API.
2. What is animation in Flutter, and why is it important for mobile app development?
3. Describe the types of testing in Flutter and the steps involved in widget testing with an
example.
4. What is SQLite and how can it be used in a Flutter application?
5. What is the purpose of the internationalization package in Flutter? Provide an
example of how it can be used for localization.

105
SOUNotes

SOUNotes

SOUNotes

You might also like