You are on page 1of 72

CHAPTER 1

DART, WIDGETS AND LAYOUTS

AKSHAYA R
ASSISTANT PROFESSOR
ARTIFICIAL INTELLIGENCE & DATA
SCIENCE
DART OVERVIEW

▪ Dart is a modern, versatile programming language developed by Google,


primarily known for its use in web and app development.

▪ It was designed to address challenges in web and app development, offering a


clean and efficient way to create high-performance applications.

▪ Dart emphasizes readability, scalability, and ease of use, making it an appealing


choice for developers.
DART HISTORY

o Dart, developed by Google, emerged in 2011 as a programming language targeting


web development. It aimed to overcome limitations of JavaScript.

o In 2013, Dart reached milestone releases and introduced Dartium, a browser with a
built-in Dart VM. Shifting focus, Google introduced Flutter in 2015, using Dart as its
core language for cross-platform app development.

o Dart 2.0 arrived in 2018, enhancing syntax and performance, while strong mode
bolstered type checking.

o Today, Dart's popularity thrives mainly through its integration with Flutter, making it a
favored choice for building modern and visually appealing applications across
platforms.
POPULARITY AND USAGE

▪ Dart gained recognition, especially with the advent of the Flutter framework, which
uses Dart as its primary language.

▪ Flutter, powered by Dart, is a UI toolkit for building natively compiled applications for
mobile, web, and desktop from a single codebase.

▪ Dart's popularity surged as Flutter gained traction for its rapid development,
expressive UI, and cross-platform capabilities.

▪ Dart's usage extends beyond Flutter. It can be employed for web development and
even server-side programming, though its prominence mainly lies in mobile and web
app development.
WHY DART

▪ Efficiency: Dart's focus on performance and concurrency makes it suitable for building
high-performance applications, especially those that require real-time updates or
handling multiple tasks simultaneously.

▪ Productivity: Dart offers a clean and readable syntax, which contributes to developer
productivity. Features like hot-reloading (in Flutter) speed up the development
process.

▪ Cross-Platform Development: With Flutter, Dart allows you to build applications that
work across multiple platforms (iOS, Android, web, desktop) from a single codebase
reducing the development time and effort.
WHY DART

▪ Modern Language Features: Dart incorporates modern language features, such


as strong typing, asynchronous programming, and modularization, making it
well-suited for building scalable and maintainable applications.

▪ Flutter Ecosystem: Flutter's rich set of widgets and tools for UI development
have gained popularity in the mobile and web development communities.

▪ Being developed by Google, Dart has a strong backing from a tech giant. This
support helps ensure its continued development, improvements, and adoption.
KEY FEATURES OF DART

✔ Object-Oriented: Dart is rooted in object-oriented principles, making it well-suited


for creating modular and reusable code.

✔ Strong Typing: Dart employs strong typing, which helps catch type-related errors
early in the development process, enhancing code reliability.

✔ Cross-Platform Development: Dart is associated with the Flutter framework, enabling


developers to build applications that work across different platforms, from mobile to
web and even desktop.

✔ Modern Syntax: a clean and readable syntax. Its structure encourages code
organization and maintainability.
KEY FEATURES OF DART – CONTD.

✔ Asynchronous Programming: Dart simplifies asynchronous programming through the


use of async and await keywords, facilitating efficient handling of asynchronous tasks.

✔ Hot Reload (Flutter): In conjunction with Flutter, Dart introduces a unique feature
called hot reload, enabling developers to see instant changes in the application as they
modify the code.

✔ Performance Focus: Dart offers a combination of Just-in-Time (JIT) and


Ahead-of-Time (AOT) compilation, optimizing performance for both development and
production environments.
DART BASICS

main() function - the entry point of a Dart program.

The main() function is a crucial entry point for Dart programs.

It is where the program execution starts and flows to other functions and statements.

import 'dart:io';

void main() {

// Program code

}
VARIABLES

In Dart, variables are used to store and manipulate data. Dart is a statically typed
language, which means that variables have a specific data type associated with them, and
this data type is determined at compile-time.

Declaration and Initialization:

Variables are declared using the var, final, or const keyword, followed by the variable
name.

var is used for variables whose values can change (mutable).

final is used for variables that are initialized once and cannot be changed afterward.

const is used for compile-time constants.


VARIABLES

Type Inference: Dart supports type inference.

You can omit the data type when declaring a variable, and Dart will infer it from the
assigned value.

var age = 23; // Dart infers age as an integer.

Explicit Type Annotation:

You can explicitly specify the data type of a variable using the Type followed by the
variable name.

String name = "Akshaya";


VARIABLES

Dynamic Typing:

Dart also supports the dynamic type, which allows a variable to hold values of any
type.

Variables are Lexically Scoped: Variables are only accessible within the scope they
are declared in.

Default Values:

Variables in Dart have default values:

If a variable is declared but not initialized, it gets a default value of null.

final and const variables must be initialized when declared.


VARIABLES

Dart has two types of constants: final and const

final variables are initialized once at runtime, while const variables are compile-time constants.

const variables are implicitly final.

final finalVar = 100; // Runtime constant.

const constVar = 20; // Compile-time constant.

var and dynamic variables can be reassigned to values of different types, while final and const
variables cannot be reassigned once initialized.
VARIABLE NAMING CONVENTIONS AND RULES

Naming Conventions:

Variables should have meaningful names that reflect their purpose in the program.
This enhances code readability and understanding.

Use camelCase: Begin with a lowercase letter and capitalize the first letter of
subsequent words (e.g., studentName, totalAmount).
VARIABLE NAMING CONVENTIONS AND RULES –
CONTD.

Naming Rules

1. Variable names can contain letters, digits, and underscores.

2. They must start with a letter (a-z, A-Z) or an underscore (_).

3. Avoid starting variable names with numbers or special characters.

4. Variable names are case-sensitive (myVariable and myvariable are considered different).

5. Don't use reserved words (keywords) as variable names (e.g., var, int, double, if, while).

6. Use names that make the purpose of the variable clear, like age, userName, totalAmount.
DART DATA TYPES

Dart has several built-in data types:

int: Integer numbers; int score = 100;

double: Floating-point numbers; double price = 19.99;

String: Textual data; String name = "Alice";

bool: Boolean values (true or false); bool isStudent = true;

Dart also has dynamic type, allowing variable type changes

dynamic firstName = “Tim”


DART DATA TYPES

List<int> numbers = [1, 2, 3, 4, 5];

Map<String, dynamic> person = {

'name': 'John',

'age': 30,

};

Set<String> uniqueColors = {'red', 'green', 'blue'};

Runes t = Runes('\u2665’);

Object obj = 'example'


INPUT OF INFORMATION TO DART PROGRAM

Dart uses stdin for input handling from the console.

To read input:

String input = stdin.readLineSync();

Use int.parse() or double.parse() to convert input to numeric types.

The method readLineSync() is one of the methods used to take input from
the user.

int.parse() method in Dart - The method takes a string as an argument and


converts it into an integer.
DART CONDITIONAL OPERATORS

if: Executes code based on a condition.

else if: Executes one block of code if the condition is true, and another if it's false.

else: Code to execute if no conditions match.

Example:

if (condition1) {

// Code to execute if condition1 is true

} else if (condition2) {

// Code to execute if condition2 is true

} else {

// Code to execute if neither condition1 nor condition2 is true }


IF ELSE AND LOGICAL OPERATORS

Logical operators (&&, ||, !) help combine conditions.

Example:

if (condition1 && condition2) {

// Code to execute

} else if (condition3 || condition4) {

// Code to execute

}
LOOPS

for Loop

The for loop allows you to iterate over a collection of items a specified number
of times or to perform an action based on a specific condition.

for (var i = 0; i < 5; i++) {

print('Value of i: $i');

}
LOOPS

for-in Loop

The for-in loop is used to iterate over elements of an iterable, such as a list, set,
or map.

List<int> numbers = [1, 2, 3, 4, 5];

for (var number in numbers) {

print('Number: $number');

}
LOOPS

while Loop

The while loop repeatedly executes a block of code as long as a specified condition is
true.

var i = 0;

while (i < 5) {

print('Value of i: $i');

i++;

}
LOOPS

do-while Loop

The do-while loop is similar to a while loop, but it guarantees that the block of code is
executed at least once before checking the condition.

var i = 0;

do {

print('Value of i: $i');

i++;

} while (i < 5);


LOOPS

forEach Loop

Dart provides the `forEach` method to iterate over elements in an iterable like
a list.

List<String> fruits = ['Apple', 'Banana', 'Cherry'];

fruits.forEach((fruit) {

print('Fruit: $fruit');

});
LOOPS

Iterable Loop

Dart offers several methods for iterating over collections, such as `map`,
`where`, `reduce`, and others. These methods allow you to perform operations
on the elements of a collection.

List<int> numbers = [1, 2, 3, 4, 5];

var squaredNumbers = numbers.map((number) => number * number);

print(squaredNumbers.toList());
STRINGS

In Dart, a string is a sequence of characters enclosed in single (') or double (")


quotes. Strings are fundamental for working with textual data, such as messages,
names, and more. Dart provides various methods and features to manipulate and
work with strings effectively.

String Declaration:

You can declare strings using single or double quotes.

String message1 = 'Hello, Dart!’; String message2 = "Welcome to programming!";


STRINGS – CONTD.

String Concatenation:

Strings can be concatenated using the + operator:

String firstName = "John";

String lastName = "Doe";

String fullName = firstName + " " + lastName;


STRINGS – CONTD.

Multiline Strings:

Dart supports multiline strings using triple quotes (''' or """). Multiline strings can span
across multiple lines, preserving line breaks and whitespace.

String multilineText = '''

This is a multiline string.

It spans across multiple lines.

Line breaks and whitespace are preserved.

''';
STRINGS – CONTD.

Escape Characters:

Dart also supports escape characters within strings, allowing you to include
special characters like newline (\n), tab (\t), and more.

String escapedString = "This is a string with\na newline.";

String Methods:

Dart provides various methods to manipulate and analyze strings. Some


commonly used string methods include toUpperCase(), toLowerCase(), trim(),
substring(), startsWith(), endsWith(), and more.
STRING INTERPOLATION

String interpolation in Dart is a powerful feature that allows you to embed expressions
and variables directly within a string. This feature helps create dynamic strings by
evaluating the expressions and replacing them with their values. String interpolation is
achieved using the ${expression} syntax within a string.

String name = "Alice";

int age = 30;

String greeting = "Hello, my name is $name and I am $age years old.";

print(greeting);
You can use string interpolation not only with variables but also with expressions, function calls,
and even complex expressions. Here are some more examples:

double price = 19.99;

int quantity = 5;

String summary = "Total cost: \$${price * quantity}";

print(summary); // Outputs: Total cost: $99.95

bool isAdult = age >= 18;

String status = "User is ${isAdult ? 'an adult' : 'a minor'}.";

print(status); // Outputs: User is an adult.

String interpolation enhances code readability and reduces the need for manual string
concatenation, making your code cleaner and easier to understand
FUNCTIONS IN DART

Writing functions is a fundamental concept that allows you to encapsulate a block of


code that performs a specific task or computation.

Dart functions are declared using the returnType functionName(parameters) { ... }


syntax.

functionName(parameters) => expression;

Example:

int add(int a, int b) {

return a + b;

}
FUNCTIONS IN DART

Parameters:

Functions can take zero or more parameters, which are the values passed to the
function when it's called. Parameters have a name and a type.

void printDetails(String name, int age) {

print("Name: $name, Age: $age");

}
FUNCTIONS IN DART

Optional Parameters:

Dart supports optional parameters using curly braces {} for named parameters and square
brackets [] for positional parameters.

void optionalParams({String? name, int age = 30}) {

//

void optionalPositionalParams([String? name, int age = 30]) {

//

}
FUNCTIONS IN DART

Default Parameter Values:

You can specify default values for parameters, which are used if a value is not provided
when the function is called.

void greetWithDefault({String name = “Akshara"}) {

print("Hello, $name!");

Recursive Functions:

Dart allows you to define recursive functions, where a function can call itself.
USING FUNCTIONS AS VARIABLES WITH CLOSURES IN
DART

Functions are treated just like any other data type, such as integers or strings.
This means you can:

▪ Assign functions to variables.

▪ Pass functions as arguments to other functions.

▪ Return functions from other functions.

▪ Store functions in data structures like arrays or objects.

This flexibility enables you to write more modular and reusable code.
FUNCTIONS AS VARIABLES

Just like you can store numbers, strings, or other data in variables, you can also
store functions in variables in Dart. This means you can assign a function to a
variable, and that variable can then be used to call the function.

var myFunction = () {

print('Hello from myFunction!');

};

Here, myFunction is a variable that holds a function. You can call myFunction to
execute the code inside it.
CLOSURES

✔ Closures are functions that "remember" the environment in which they were
created, including the variables and values that were in scope at that time. This
means that even if you call a closure outside of its original scope, it can still
access and use those remembered variables.

✔ Closures allow you to capture variables from their enclosing scope.

✔ A closure in Dart is a function object that has access to variables in its


lexical scope, even when the function is called outside that scope.
This means a closure "closes over" its surrounding variables, capturing their state at the time of its creation.
Example - using functions as variables with closures:

void main() {

int x = 10;

// Create a closure

Function addX = (int y) {

return x + y;

};

// Use the closure

int result = addX(5);

print(result); // Output: 15 }
USING FUNCTIONS AS VARIABLES WITH CLOSURES IN DART -
BENEFITS

Data Encapsulation: Closures allow you to encapsulate data within a function,


creating a private scope for that data.

Code Reusability: You can reuse closures across different parts of your code

Functional Programming: Closures enable functional programming techniques


like mapping and filtering collections of data.

Callback Handling: Closures are commonly used to handle asynchronous


operations and callbacks.
CREATING CLASSES AND USING THE CLASS CONSTRUCTOR
SHORTHAND IN DART

▪ In Dart, creating classes and using the class constructor shorthand is a


fundamental concept in object-oriented programming (OOP).

▪ Classes are used to define blueprints for objects, encapsulate data and behavior,
and promote code organization.

▪ The class constructor shorthand simplifies the process of defining class


properties and their initialization.
CREATING CLASSES

A basic example of creating a class in Dart:

class Person {

String name;

int age;

// Constructor

Person(this.name, this.age);

void greet() {

print('Hello, my name is $name, and I am $age years old.');

}}
CONSTRUCTOR SHORTHAND IN DART

In Dart, the Class Constructor Shorthand allows you to declare class properties directly
in the constructor parameter list using the this keyword. You do not need to explicitly
declare the class properties and assign them in the constructor's body.

It is a simplified way to declare and initialize class properties within the


constructor itself.

It lets you save time and make your code more concise by eliminating the need
for additional property declarations and assignments in the constructor body.
CONSTRUCTOR SHORTHAND IN DART

class Person {

String name;

int age;

// Constructor with Shorthand

Person(this.name, this.age);

In this example, instead of separately declaring name and age as properties and then assigning
them in the constructor, the shorthand allows you to declare and initialize them directly in the
constructor's parameter list using this keyword. This makes your code cleaner and more
efficient.
CREATING CLASSES AND USING THE CLASS
CONSTRUCTOR SHORTHAND IN DART

Dart provides a simple way to create classes using the class keyword. Here's an example
of a class with a constructor shorthand:

class Person {

String name;

int age;

Person(this.name, this.age); // Constructor shorthand

var person = Person('Alice', 30);


GROUPING AND MANIPULATING DATA WITH
COLLECTIONS IN DART

Collections in Dart:

List: An ordered collection of elements. Lists can contain elements of the same or
different types.

Set: An unordered collection of unique elements. Sets are useful for ensuring
uniqueness and checking membership.

Map: A collection of key-value pairs, where each key maps to a value. Keys are
unique within a map.
GROUPING AND MANIPULATING DATA WITH
COLLECTIONS IN DART – CONTD.

Grouping Data:

Filtering: Selecting elements from a collection based on a specific condition. For


example, filtering out even numbers from a list.

Sorting: Arranging elements in a specific order, such as ascending or descending. For


example, sorting a list of names alphabetically. sort()

Grouping: Categorizing elements based on certain criteria. For instance, grouping a list
of people by age to create age-based categories.

Partitioning: Splitting a collection into multiple subsets based on a condition. For


example, partitioning a list into two lists of even and odd numbers.
GROUPING AND MANIPULATING DATA WITH
COLLECTIONS IN DART – CONTD.

Manipulating Data:

Mapping/Transformation: Applying a function to each element in the collection to


produce a new collection with modified values. For example, doubling all numbers in a
list.

Reducing/Folding/Accumulating: Combining elements in the collection to produce a


single result. This can be used for summing values, finding the maximum/minimum, or
aggregating data.

Joining: Combining elements from multiple collections into a single collection. For
example, merging two lists of names.
void main() {

List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Filtering: Get even numbers

List<int> evenNumbers = numbers.where((number) => number % 2 == 0).toList();

// Mapping/Transformation: Square each number

List<int> squaredNumbers = numbers.map((number) => number * number).toList();

// Reducing/Folding: Calculate the sum

int total = numbers.reduce((sum, number) => sum + number);

print("Original numbers: $numbers");

print("Even numbers: $evenNumbers");

print("Squared numbers: $squaredNumbers");

print("Total sum: $total");

}
In this example,

We start with a list of numbers from 1 to 10.

where method - to filter out the even numbers and store them in the evenNumbers list.

map method - to square each number and store the results in the squaredNumbers list.

reduce method - to calculate the sum of all the numbers and store it in the total variable.
LESS CODE WITH HIGHER ORDER-FUNCTIONS IN DART

In Dart, using higher-order functions can lead to less code while maintaining or
even enhancing the functionality of your programs.

Higher-order functions are functions that can accept other functions as


arguments, return functions as results, or both.

They enable you to abstract and modularize common patterns of code,


resulting in more concise and maintainable codebases
LESS CODE WITH HIGHER ORDER-FUNCTIONS IN DART

1. Reusable Functions: Higher-order functions allow you to create reusable functions


that can be customized by passing other functions as parameters. This reduces the need
for writing similar code multiple times.

2. Code Abstraction: You can abstract away complex or repetitive code into
higher-order functions. This makes your code more focused on what needs to be done
rather than how it should be done.

3. Declarative Code: Higher-order functions encourage a more declarative style of


coding. Instead of specifying step-by-step instructions you can express the desired
outcome in a more concise and readable way.
LESS CODE WITH HIGHER ORDER-FUNCTIONS IN DART

Concepts of less code with higher-order functions in Dart:

Filtering with where:

Dart's where method is a higher-order function that filters a list based on a given
condition. It abstracts away the need for writing a loop to filter elements.

List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

List<int> evenNumbers = numbers.where((number) => number % 2 == 0).toList();


LESS CODE WITH HIGHER ORDER-FUNCTIONS IN DART

Mapping with map:

The map method transforms each element of a list according to a provided function.
It abstracts away the need for writing a loop to iterate and modify elements.

List<int> numbers = [1, 2, 3, 4, 5];

List<int> squaredNumbers = numbers.map((number) => number *


number).toList();
LESS CODE WITH HIGHER ORDER-FUNCTIONS IN DART

reduce:

The reduce method combines elements in a list to produce a single result. It


abstracts away the need for writing a loop to perform the accumulation.

List<int> numbers = [1, 2, 3, 4, 5];

int total = numbers.reduce((sum, number) => sum + number);


LESS CODE WITH HIGHER ORDER-FUNCTIONS IN DART

Custom Higher-Order Functions:

You can create your own higher-order functions to abstract away complex tasks.

These custom higher-order functions can be used to abstract and generalize


common patterns in the code, making it more modular and reusable.

For example, a filterAndTransform function that combines filtering and mapping.


LESS CODE WITH HIGHER ORDER-FUNCTIONS IN DART

Consider, a custom higher-order function called repeatFunction as an example. This function will take
another function and an integer n as arguments. It will return a new function that executes the provided
function n times.

Function repeatFunction(Function fn, int n) {

return () {

for (var i = 0; i < n; i++) {

fn(); // Call the provided function

};

}
LESS CODE WITH HIGHER ORDER-FUNCTIONS IN DART

In this repeatFunction,

fn - function argument that represents the function we want to repeat.

n - integer argument that specifies the number of times we want to repeat the
function.

The repeatFunction returns a new function that repeats the provided function n
times.

Custom Higher-Order Functions allows to encapsulate patterns of code into


reusable functions, making the code more concise and maintainable.
CASCADE OPERATOR (..)

The cascade operator in Dart allows to perform a sequence of operations on the same
object without having to repeat the object's name. It's especially useful when you need
to make multiple method calls or property assignments on an object.

An example on how the cascade operator works:

class Person {

String name = '';

int age = 0;

}
CASCADE OPERATOR (..) – CONTD.

void main() {

var person = Person()

..name = 'Alice'

..age = 30;

print(person.name); // Output: Alice

print(person.age); // Output: 30

In this example, Person object is created and its name and age properties are set using the
cascade operator ...
DART NULL SAFETY

Dart Null Safety is a feature introduced in the Dart programming language to help developers
write safer and more reliable code by minimizing null reference errors, which are a common
source of bugs in many programming languages.

Null safety is designed to make it easier to work with null values and to prevent unexpected null
pointer exceptions at runtime.

Nullable and Non-Nullable Types:

In Dart Null Safety, all types are categorized as either nullable or non-nullable.

✔ Nullable types are indicated by adding a ? suffix to the type, such as String?, int?, or Object?.

✔ Non-nullable types do not have a ? suffix and are considered to always have a non-null value.
DART NULL SAFETY

Non-nullable by Default:

Dart Null Safety introduces a default non-nullable behavior for variables and fields. This
means that unless you explicitly specify a variable as nullable using the ? suffix, it is
assumed to be non-nullable.

Late Initialization:

To handle situations where a non-nullable variable cannot be initialized immediately upon


declaration, Dart introduces the late keyword. A variable can be declared as late to
indicate that it will be assigned a value before it is used, allowing you to delay initialization

late String name;


DART NULL SAFETY

Null-aware Operators:

Dart provides operators that allow you to safely work with nullable values without
causing null pointer exceptions.

The null-aware operators include the null conditional operator (?.), the
null-aware cascade operator (..?), and the null coalescing operator (??).
WIDGETS

Widgets are the core elements used to build the UI in Flutter applications.

✔ In Flutter, everything is a widget, including structural elements like buttons,


text, images, as well as layout elements like rows, columns, and entire screens.

✔ They are used to describe how the UI should look and behave based on the
application's current state.

✔ Widgets can be as simple as displaying text or as complex as an entire screen


or interactive element.
WIDGETS

Widgets Are Composable:

▪ Flutter encourages widget composition, where you build complex user


interfaces by combining and nesting simpler widgets within each other.

▪ This approach allows you to create reusable and modular UI components.


Flutter applications are built by composing widgets together.

▪ You can nest widgets inside other widgets to create complex UI hierarchies.

▪ This composability allows you to design your UI by combining and arranging


widgets in a tree-like structure.
WIDGETS

Immutable and Reusable

Widgets Define Structure and Appearance:

Widgets determine both the structure and appearance of the UI.

Structural widgets define how elements are laid out, like Container, Row, Column,
and Stack.

Appearance widgets define the visual characteristics of elements, like Text, Icon,
and Image.
WIDGETS

Stateless and Stateful Widgets:

Flutter distinguishes between two main types of widgets: stateless and stateful.

Stateless Widgets: Stateless widgets are immutable, meaning they do not change their
internal state once they are created. They are used for UI components that remain static
and do not respond to user interactions. For example, displaying a label with static text.

Stateful Widgets: Stateful widgets, on the other hand, can change and update their
state over time. They are used for UI components that need to react to user input or
changes in the application's data. For example, a counter that increments when a button is
pressed..
WIDGETS

Widget Tree:

Widgets are organized in a hierarchical structure called the "widget tree" or "widget
hierarchy." At the top of the tree is typically a root widget like MaterialApp or
CupertinoApp, which sets up the basic structure of your app. Child widgets are then
nested within this root widget, defining the layout and behavior of the UI.

build Method:

All widgets, whether stateless or stateful, have a required build method that must be
implemented. This method defines how the widget should render its content based on
the current state and properties. The build method returns a widget representing the UI.
WIDGETS

Reactive UI:

Widgets in Flutter are reactive, meaning they can rebuild themselves in response
to changes in their properties or the app's state. Stateful widgets, in particular, can
trigger a rebuild of their UI when their internal state changes.

Event Handling:

Widgets can respond to user interactions by attaching event handlers or callbacks.


For example, you can attach an onPressed callback to a button widget to define
what should happen when the button is pressed.
WIDGETS

Custom Widgets:

You can create custom widgets by extending either StatelessWidget or StatefulWidget. Custom
widgets encapsulate parts of your UI and functionality, promoting code modularity and reusability.

State Management:

For managing application state, Flutter provides various options and packages like Provider, Bloc,
and GetX. These help in managing and sharing data across widgets and updating the UI
accordingly.

Layout and Styling:

Flutter provides layout widgets like Container, Row, Column, and styling widgets like TextStyle
and Theme to control the visual appearance and positioning of widgets within the UI.
THANK YOU

You might also like