FLUTTER
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.
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.
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.
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.
import 'package:flutter/material.dart';
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
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.
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();
}
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.
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.
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.
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(),
),
));
}
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'),
),
),
)
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.
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
Features of Dart
Easy to Understand:
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.
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
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
void main() {
dynamic item = "Book";
print(item); // Output: Book
item = 10;
print(item); // Output: 10
}
const pi = 3.14;
const String greeting = "Hello";
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
}
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
}
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
void main() {
int number = -5;
print(number.abs()); // Output: 5
}
void main() {
double number = 3.14;
32
print(number.ceil()); // Output: 4
}
void main() {
int number1 = 10;
int number2 = 20;
print(number1.compareTo(number2)); // Output: -1
}
void main() {
int dividend = 10;
int divisor = 3;
print(dividend.remainder(divisor)); // Output: 1
}
void main() {
double number = 3.6;
print(number.round()); // Output: 4
}
void main() {
int number = 10;
print(number.toDouble()); // Output: 10.0
}
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"
}
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!
}
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]
}
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]
}
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.
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}
}
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
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 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}");
}
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);
}
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();
}
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");
}
import 'dart:io';
void main() {
print("Welcome to Dart!");
}
import 'dart:io';
void main() {
stdout.write("Welcome to Dart!");
}
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
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');
}
}
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');
}
}
void main() {
int number = 10;
String result =
number > 5 ? 'The number is greater than 5' : 'The number is 5 or less';
print(result);
}
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.
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
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.
Operational Parameters
void main() {
printDetails('Alice'); // Output: Name: Alice, Age: 0
printDetails('Bob', 25); // Output: Name: Bob, Age: 25
}
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.
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.
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: 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.
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.
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();
}
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();
}
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;
}
}
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');
}
}
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');
}
}
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
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.
}
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'),
);
Padding(
padding: EdgeInsets.all(16.0),
child: Text('Padded Text'),
);
Align(
alignment: Alignment.center,
child: Text('Centered Text'),
);
Center(
child: Text('Centered Text'),
);
SizedBox(
width: 100,
height: 100,
child: Text('Sized Box'),
);
63
Expanded(
child: Text('Expanded Text'),
);
Column(
children: <Widget>[
Text('First'),
Text('Second'),
Text('Third'),
],
);
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')),
],
);
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.
import 'package:flutter/material.dart';
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);
@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
),
],
),
),
);
}
}
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';
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"),
],
),
);
}
}
import 'package:flutter/material.dart';
@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",
),
],
),
);
}
}
@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());
}
@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,
),
);
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,
),
);
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
74
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Ephemeral State Example'),
),
body: CounterWidget(),
),
);
}
}
@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.
You can use a StatefulWidget to hold the application state (in this case, a counter)
and manage its changes.
import 'package:flutter/material.dart';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App State Management',
theme: ThemeData(primarySwatch: Colors.blue),
home: HomeScreen(
counter: _counter,
incrementCounter: _incrementCounter,
),
);
}
}
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';
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),
),
);
}
}
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.
The SecondScreen will display the counter value and allow the user to increment the
counter.
import 'package:flutter/material.dart';
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.
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.
import 'package:flutter/material.dart';
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()),
);
},
),
),
);
}
}
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';
81
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// Define the named routes
routes: {
'/': (context) => FirstScreen(),
'/second': (context) => SecondScreen(),
},
initialRoute: '/',
);
}
}
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.
import 'package:flutter/material.dart';
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.
Example
Let’s create a simple counter application using ScopedModel
yamlCopy code
dependencies:
flutter:
sdk: flutter
scoped_model: ^1.0.1
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
}
}
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(),
),
);
}
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import 'counter_model.dart';
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.
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';
89
_ImplicitAnimationExampleState();
}
@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());
}
90
class AnimationExample extends StatefulWidget {
@override
_AnimationExampleState createState() => _AnimationExampleState();
}
@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,
),
),
),
);
}
}
_controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Container(
width: _animation.value, // Animation value updates over time
height: _animation.value,
color: Colors.blue,
);
},
);
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();
}
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 2000),
vsync: this,
)..addListener(() {
setState(() {});
});
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
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:flutter/material.dart';
import 'package:http/http.dart' as http; // Importing the HTTP package
import 'dart:convert';
void main() {
runApp(MyApp());
}
95
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'HTTP Package Example',
home: MyHomePage(),
);
}
}
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'),
),
],
),
),
);
}
}
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:
@override
void initState() {
super.initState();
fetchData(); // Call the fetch function when the widget
initializes
}
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');
}
}
Here’s an explanation of how you can retrieve product data from an API in Flutter:
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.
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
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.
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
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
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.
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.
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.
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
}
}
}
104
adb install build/app/outputs/apk/release/app-release.ap k
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