0% found this document useful (0 votes)
41 views292 pages

Class Work

The document outlines the history and evolution of app development from the 1990s to the present, highlighting key milestones such as the introduction of feature phones, the smartphone revolution with the iPhone and Android, and the rise of app ecosystems. It compares popular frameworks Flutter and React Native in terms of performance, popularity, development speed, UI/UX, and other factors, while also discussing the Dart programming language, its features, and its significance in modern app development. Additionally, it provides examples of successful apps built using Flutter and a brief history of Dart's development and improvements.

Uploaded by

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

Class Work

The document outlines the history and evolution of app development from the 1990s to the present, highlighting key milestones such as the introduction of feature phones, the smartphone revolution with the iPhone and Android, and the rise of app ecosystems. It compares popular frameworks Flutter and React Native in terms of performance, popularity, development speed, UI/UX, and other factors, while also discussing the Dart programming language, its features, and its significance in modern app development. Additionally, it provides examples of successful apps built using Flutter and a brief history of Dart's development and improvements.

Uploaded by

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

App development History

App development Histor​


Early Days (1990s - Early 2000s):

●​ Feature Phones: Mobile apps started with basic software on feature phones, like
calendars, calculators, and simple games like Snake on Nokia devices.
●​ Java and Symbian: In the early 2000s, more advanced apps emerged on
platforms like Java ME (used by many manufacturers) and Symbian OS (led by
Nokia).

2. Smartphone Revolution (2007):

●​ iPhone and App Store (2007-2008): Apple revolutionized mobile apps with the
iPhone and its iOS operating system. 2008 the App Store launched, providing
developers a platform to distribute apps directly to consumers.
●​ Android and Google Play (2008): Google followed suit with Android and its
marketplace, Google Play (originally called Android Market). Due to its
open-source nature, Android quickly became a major competitor.

3. Rise of App Ecosystems (2009-2014):

●​ Apps as Core Features: As smartphones became more powerful, apps became


core functions of the devices, from social media (Facebook, Instagram) to
productivity (Evernote, Google Maps).
●​ Cross-Platform Development: Technologies like PhoneGap and later
frameworks like Xamarin enabled developers to build apps that worked on both
iOS and Android.

4. Shift to Native & Hybrid Apps (2015-Present):

●​ Native App Dominance: Developers began to focus more on native apps (iOS
using Swift, Android using Kotlin) for performance and user experience.
●​ Hybrid Frameworks: Modern frameworks like React Native and Flutter allow for
near-native performance while simplifying development by offering a single
codebase for multiple platforms.

5. Current Trends:

●​ AI and AR Integration: Apps are now more interactive with AI features (personal
assistants, image recognition) and augmented reality (AR) for enhanced user
experiences.
●​ Super Apps: In markets like China, apps like WeChat have grown into all-in-one
platforms, combining messaging, shopping, payments, and services in a single
app.

Flutter and React Native are two popular frameworks for building cross-platform mobile
applications. They allow developers to write a single codebase and deploy it to both iOS
and Android, saving time and resources. Here's a comparison of Flutter and React
Native, breaking down key aspects with percentage-based insights:

1. Performance

●​ Flutter (90%): Flutter delivers near-native performance because it compiles to


native ARM code for both Android and iOS. Its rendering engine, Skia, provides a
high-performance UI experience.
●​ React Native (80%): React Native bridges JavaScript code to native
components, which can introduce some performance overhead. However, it is
still efficient and can handle most app requirements.

2. Popularity and Community Support

●​ React Native (85%): Launched in 2015 by Facebook, React Native has a large
and mature developer community. Many big apps (e.g., Facebook, Instagram)
have been built using this framework, and it remains highly popular.
●​ Flutter (75%): Flutter, launched in 2017 by Google, is rapidly growing in
popularity but is newer compared to React Native. Its community is still growing,
but it’s already used by companies like Alibaba and Google.

3. Development Speed

●​ React Native (85%): React Native’s hot-reloading feature allows developers to


see code changes almost instantly. With JavaScript's flexibility and familiarity,
developers can quickly create and iterate on apps.
●​ Flutter (80%): Flutter also offers hot-reloading, but its development speed can be
slightly slower because Dart (the programming language used) may not be as
familiar to developers as JavaScript.

4. UI/UX

●​ Flutter (95%): Flutter excels in UI design as it comes with its own set of
customizable widgets that offer a consistent look across platforms. It allows
developers more control over the entire screen and UI elements.
●​ React Native (80%): React Native relies on native components, so the UI/UX
can sometimes differ between iOS and Android. However, with additional
libraries, developers can create visually appealing interfaces.

5. Learning Curve

●​ React Native (85%): React Native uses JavaScript, a well-known and widely
used language, making it easier for web developers to transition to mobile app
development.
●​ Flutter (70%): Flutter uses Dart, which is less common than JavaScript, meaning
developers need to learn a new language. However, Dart is relatively easy to
pick up, especially for developers with experience in object-oriented
programming.

6. Ecosystem and Libraries

●​ React Native (90%): Since it has been around longer, React Native has a larger
ecosystem of third-party libraries and tools that can extend its functionality.
●​ Flutter (80%): Flutter's ecosystem is growing, but there are fewer third-party
libraries compared to React Native. However, Google is actively supporting and
expanding Flutter's ecosystem.

7. Stability and Maturity

●​ React Native (80%): React Native has been around longer, making it more
stable and mature, though there are still challenges with native module
compatibility and upgrades.
●​ Flutter (85%): While Flutter is newer, its stability is excellent due to Google's
active development and support. Many developers praise its robust architecture
and smooth updates.

8. Code Reusability

●​ React Native (85%): React Native allows for significant code reuse across
platforms but may require platform-specific tweaks for complex UIs or
functionality.
●​ Flutter (90%): With Flutter, almost 100% of the code can be reused for both
platforms, reducing the need for platform-specific adjustments.

9. Development Cost

●​ React Native (85%): Since JavaScript is more widely known and React Native
has a large community, hiring React Native developers can be cost-effective.
●​ Flutter (80%): The cost is similar to React Native, though finding experienced
Flutter developers may be slightly harder because Dart is not as popular as
JavaScript.

Summary

●​ Flutter: Strong in performance, UI/UX customization, and code reuse, making it


ideal for apps that require high-performance graphics and consistent UI across
platforms. However, it has a steeper learning curve.
●​ React Native: More popular and widely used with excellent support for
third-party libraries and a simpler learning curve for web developers due to
JavaScript. However, it may have performance trade-offs for highly demanding
apps.

Use case example:

●​ Flutter (90%) might be chosen for an app that demands high-performance


graphics and a custom UI.
●​ React Native (85%) would be a great choice if rapid development, leveraging
existing web developers, and using third-party libraries are priorities.
Dart is an open-source, general-purpose programming language developed by Google.
It is designed to be easy to learn and efficient, with a focus on modern app
development, particularly for mobile, web, and desktop applications.

Key Features of Dart:

1.​ Object-Oriented Language:


○​ Dart is object-oriented, meaning it uses classes and objects, similar to
languages like Java or C#. This makes it easier for developers who are
familiar with these languages to learn Dart.
2.​ Statically Typed (with Flexibility):
○​ Dart is statically typed, meaning you define the types of variables (e.g.,
int, String). However, it has a type inference system, so you don’t
always have to explicitly specify types. It also supports dynamic typing
using the dynamic keyword for more flexibility.
3.​ Null Safety:
○​ Dart offers null safety, a feature that helps prevent null reference errors at
compile time. By default, variables cannot be null unless explicitly
declared as nullable with ?, which improves code reliability.
4.​ Compiled Language:
○​ Dart can be compiled both to native machine code (for mobile apps) and
to JavaScript (for web apps). For Flutter apps, Dart compiles directly to
native ARM code, ensuring smooth performance.
5.​ Asynchronous Programming:
○​ Dart has robust support for asynchronous programming using async and
await keywords. This allows developers to write non-blocking code,
which is essential for handling tasks like API calls or file I/O without
freezing the app’s UI.
6.​ Built for Flutter:
○​ Dart is the primary language for building mobile applications using the
Flutter framework. It’s optimized to work with Flutter’s widget system,
offering fast and high-performance mobile apps.
7.​ Garbage Collection:
○​ Dart has automatic garbage collection, meaning it automatically manages
memory allocation and deallocation, helping developers avoid memory
leaks and making coding more efficient.
8.​ Wide Platform Support:
○​ Dart supports multiple platforms, including mobile (iOS, Android), web,
and desktop (Windows, macOS, Linux). The same Dart code can often
be reused across these platforms, making development more efficient.
9.​ DartPad:
○​ DartPad is an online tool where developers can write and run Dart code
directly in the browser without setting up a development environment. It’s
useful for learning and experimenting with the language.

Top app which is built in Flutter

Google Ads

●​ Purpose: This app allows users to manage Google Ads campaigns, track
performance, and get real-time notifications.
●​ Reason for Using Flutter: Flutter’s fast performance and ability to create
complex UIs across platforms made it a great choice for Google.

2. Alibaba

●​ Purpose: Alibaba, the giant e-commerce platform, uses Flutter for parts of its
app to ensure a seamless shopping experience across iOS and Android.
●​ Reason for Using Flutter: The company needed a high-performance framework
that would allow them to ship features faster while maintaining a consistent UI.
3. Reflectly

●​ Purpose: Reflectly is a personal journal and mental health app that uses AI to
help users reflect on their daily lives.
●​ Reason for Using Flutter: Flutter’s smooth animations and ability to create
visually engaging UIs helped Reflectly offer an appealing user experience across
both platforms.

4. BMW

●​ Purpose: The BMW app allows users to interact with their cars
remotely—checking vehicle status, locking/unlocking, and planning trips.
●​ Reason for Using Flutter: BMW selected Flutter to streamline development for
both iOS and Android, ensuring a consistent experience across devices.

5. eBay Motors

●​ Purpose: eBay Motors helps users buy, sell, and explore cars, motorcycles, and
parts directly from their mobile phones.
●​ Reason for Using Flutter: Flutter allowed eBay to develop an intuitive interface
with interactive features for image uploading, browsing, and vehicle details.

Dart basic Day




Introduction to Dart Language

Dart is an open-source, general-purpose programming language developed by Google,


primarily designed for building fast, scalable apps for web, mobile, desktop, and even
server-side applications. Dart was introduced in 2011 to provide a better alternative to
JavaScript for web development, but its role expanded over time, especially with the
rise of the Flutter framework.

Dart is known for its:

●​ Fast execution: Compiles to both native code and JavaScript.


●​ Strong support for async programming: Uses async and await keywords for
non-blocking operations.
●​ Null safety: Helps prevent null errors at compile time.
●​ Cross-platform capabilities: One codebase for multiple platforms (iOS,
Android, web, etc.).

Brief History of Dart

1.​ 2011 - Initial Release:


○​ Dart was unveiled by Google at the GOTO Conference in 2011. It was
intended to replace JavaScript for complex web applications due to
performance issues that JavaScript faced at the time. Dart's main focus
was on simplicity, scalability, and performance.
2.​ 2013 - Dart 1.0:
○​ Dart hit version 1.0 in 2013, marking its readiness for production use.
Google provided tools like Dart2js to compile Dart code into JavaScript,
allowing it to run in modern web browsers.
3.​ 2015 - AngularDart:
○​ Google introduced AngularDart, a variant of the popular Angular
framework, allowing developers to write web apps in Dart. However, Dart
was still struggling to gain wide adoption in the web development
community compared to JavaScript.
4.​ 2017 - Flutter Framework:
○​ The real breakthrough for Dart came with the rise of Flutter in 2017, a UI
toolkit for building natively compiled applications for mobile, web, and
desktop from a single codebase. Flutter uses Dart as its primary language,
making Dart's speed and ease of use a perfect fit for cross-platform app
development.
5.​ 2021 - Dart 2.12 (Null Safety):
○​ With the release of Dart 2.12, the language introduced null safety, which
helps developers avoid common runtime errors by identifying potential null
values at compile time. This improved reliability and productivity, making
Dart even more appealing to developers.
6.​ 2024 - Present:
○​ Dart continues to evolve, primarily as the backbone of Flutter, with
ongoing improvements in performance, tooling, and platform compatibility.
It is increasingly used for mobile apps and web, desktop, and backend
services.

Why Dart?

Dart’s simplicity, performance, and versatility—especially when used with Flutter—make


it a modern, productive language for cross-platform development. It is designed for both
quick prototyping and production-quality apps, with a rich set of libraries and tools to
support various platforms.
In Dart, a variable is a way to store data values. You can declare a variable with
different types like int, double, String, bool, and more. Dart supports both explicit
typing and type inference (where the type is automatically determined).

Declaring Variables in Dart


You can declare variables using:

●​ var: Dart infers the type automatically.


●​ final: Used for variables whose value cannot be changed once set
(immutable).
●​ const: Declares compile-time constants (values known at compile-time).
●​ Explicit types: You can specify types like int, double, String, bool, etc.

Variable Declaration Examples

1. Using var (Type Inference):

dart

Copy code

void main() {

var name = 'Dart'; // Dart infers the type as String

var age = 10; // Dart infers the type as int

var price = 9.99; // Dart infers the type as double

var isActive = true; // Dart infers the type as bool

print(name); // Output: Dart

print(age); // Output: 10

print(price); // Output: 9.99

print(isActive); // Output: true

2. Using Explicit Types:

dart
Copy code

void main() {

String language = 'Dart';

int year = 2011;

double version = 2.10;

bool isReleased = true;

print(language); // Output: Dart

print(year); // Output: 2011

print(version); // Output: 2.10

print(isReleased); // Output: true

3. Using final (Immutable Variable):

void main() {

final String country = 'USA'; // Value cannot be changed after


initialization
final int population = 331; // Immutable value

// country = 'Canada'; // Error: final variable cannot be


reassigned

print(country); // Output: USA

print(population); // Output: 331

4. Using const (Compile-time Constant):

void main() {

const double pi = 3.14159; // Value is determined at


compile-time

const String appName = 'MyApp';

// pi = 3.14; // Error: Cannot change a const variable

print(pi); // Output: 3.14159

print(appName); // Output: MyApp

5. Using dynamic (Type Can Change):

dart

Copy code

void main() {
dynamic x = 'Hello'; // Initially, a String

print(x); // Output: Hello

x = 123; // Now, an int

print(x); // Output: 123

dynamic allows you to change the type of a variable after it has been initialized. This is
not recommended unless necessary, as it can make the code harder to understand and
debug.
Class work​

main() {

var name = "UNDP";


print(name);
name = "123";
print(name);

// rule 1 not use the same name in single file


// bool name = true;
// String name = "123";
///rule 2 we can,t assing int value to string data type;
// String secondName = 123;

///rule 3 not allow space in var name


// String second Name = "New class";

var age = 10;

var active = true;

var weight = 49.0;

String secondName = "New class";

String newString = "it\'s easy \$ $active";

int number = 10;

double number2 = 888.7;

bool weather = "false";

int newUserAge = number;

print("my name is $name ");


print("here is my int value is $newUserAge");

print(newString);

print("adding number ${number + number2}");


// print($number);
}
Dart second Day

Lists and Map

In Dart, a List is an ordered collection of elements, where each element can be


accessed by its index. Lists are one of the most commonly used collections in Dart and
are similar to arrays in other programming languages.

Key Characteristics of Dart List:

●​ Index-based: Elements can be accessed using an index, starting from 0.


●​ Resizable: Lists can grow or shrink dynamically, depending on the operations
performed.
●​ Supports different types: You can create a list of any data type (e.g.,
List<int>, List<String>, etc.), or a list that holds multiple types of values
using List<dynamic>.



oid main() {
// Creating a list

List<int> numbers = [10, 20, 30, 40, 50];

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

print("Original List of numbers: $numbers");

print("Original List of fruits: $fruits\n");

// 1. .length - Returns the number of elements in the list

print("Length of numbers list: ${numbers.length}");

// 2. .isEmpty - Checks if the list is empty

print("Is numbers list empty? ${numbers.isEmpty}");

// 3. .isNotEmpty - Checks if the list is not empty

print("Is numbers list not empty? ${numbers.isNotEmpty}\n");

// 4. .first - Gets the first element


print("First fruit: ${fruits.first}");

// 5. .last - Gets the last element

print("Last fruit: ${fruits.last}");

// 6. .reversed - Reverses the list

print("Reversed numbers list: ${numbers.reversed}\n");

// 7. .add() - Adds a single element to the list

fruits.add('Orange');

print("After adding 'Orange': $fruits");

// 8. .addAll() - Adds multiple elements to the list

fruits.addAll(['Pineapple', 'Grapes']);

print("After adding multiple fruits: $fruits");

// 9. .insert() - Inserts an element at a specific index

numbers.insert(2, 25); // Insert at index 2

print("After inserting 25 at index 2: $numbers");


// 10. .insertAll() - Inserts multiple elements at a
specific index

numbers.insertAll(3, [27, 28]);

print("After inserting [27, 28] at index 3: $numbers\n");

// 11. .remove() - Removes the first occurrence of an


element

numbers.remove(20);

print("After removing first occurrence of 20: $numbers");

// 12. .removeAt() - Removes an element at a specific index

numbers.removeAt(1); // Removes the element at index 1

print("After removing the element at index 1: $numbers");

// 13. .clear() - Removes all elements from the list

List<int> emptyList = [1, 2, 3];

emptyList.clear();

print("After clearing emptyList: $emptyList");


// 14. .contains() - Checks if the list contains an element

print("Does numbers list contain 40?


${numbers.contains(40)}");

// 15. .indexOf() - Finds the index of an element

print("Index of 30 in numbers list:


${numbers.indexOf(30)}\n");

// 16. .sort() - Sorts the list in ascending order

numbers.sort();

print("Sorted numbers list: $numbers");

// 17. .sublist() - Creates a sublist from the original list

print("Sublist of numbers (from index 1 to 3):


${numbers.sublist(1, 4)}");

// 18. .join() - Converts the list to a string, with


elements separated by the given separator

print("Fruits joined by commas: ${fruits.join(',')}\n");


// 19. .map() - Applies a function to all elements and
returns a new iterable

List<int> doubledNumbers = numbers.map((e) => e *


2).toList();

print("Doubled numbers: $doubledNumbers");

// 20. .where() - Filters elements based on a condition

List<int> evenNumbers = numbers.where((element) =>


element.isEven).toList();

print("Even numbers from the list: $evenNumbers");

// 21. .any() - Checks if any element matches a condition

bool hasGreaterThan30 = numbers.any((element) => element >


30);

print("Does any number > 30 exist? $hasGreaterThan30");

// 22. .every() - Checks if all elements match a condition

bool allGreaterThan10 = numbers.every((element) => element >


10);

print("Are all numbers > 10? $allGreaterThan10");


// 23. .forEach() - Iterates through each element

print("\nIterating through numbers using forEach:");

numbers.forEach((element) => print(element));

// 24. .expand() - Expands each element into zero or more


elements

List<int> expandedList = [[1, 2], [3, 4]].expand((i) =>


i).toList();

print("\nExpanded List: $expandedList");

.length: Shows the length of the list.

.isEmpty and .isNotEmpty: Check if the list is empty or not.

.first and .last: Get the first and last elements.

.reversed: Returns the elements in reverse order.

.add() and .addAll(): Add elements to the list.

.insert() and .insertAll(): Insert elements at specific positions.

.remove() and .removeAt(): Remove elements from the list.

.clear(): Clear all elements.


.contains(): Check if an element is present in the list.

.indexOf(): Get the index of a specific element.

.sort(): Sort the list.

.sublist(): Extract a portion of the list.

.join(): Convert the list into a string.

//we will discuss later

.map(): Apply a function to each element and return a new list.

.where(): Filter elements based on a condition.

.any(): Check if any element matches a condition.

.every(): Check if all elements match a condition.

.forEach(): Iterate through each element in the list.

.expand(): Expand elements of a list into a flat list.

Class work

main() {

List number = [1, 20, 40, 30, 400, 50];

var secondList = [1, "new", true, 30.0];

List<String> subjects = [
"maths",

"english",

"physics",

"Bio",

];

print("here is my list $number");

//

// here is my list [1, 20, 40, 30, 400,


50]

print("here is length
${number.length}");

// here is length 6
number.add(2000);

print("after adding new value $number");

//after adding new value [1, 20, 40, 30,


400, 50, true]

//after adding new value [1, 20, 40, 30,


400, 50, 2000]

print("is numbere is empty


${number.isEmpty}");

//is numbere is empty /false

print("is numbere isnotempty


${number.isNotEmpty}");

//is numbere isnotempty true


print("i wnat print first property of
this list ${subjects.first}");

//i wnat print first property of this


list maths

print("i wnat print last property of


this list ${subjects.last}");

// i wnat print last property of this


list Bio

// subject

print(subjects.reversed);

// number list

print(number.reversed);

// (Bio, physics, english, maths)


// (true, 50, 400, 30, 40, 20, 1)

//add more then one value

print("subejct print before adding all


$subjects");

subjects.addAll(["ITC", "ISoftware"]);

print("here is list after add all value


$subjects");

// subejct print before adding all


[maths, english, physics, Bio]

// here is list after add all value


[maths, english, physics, Bio, ITC,
ISoftware]
number.insert(3, 1000);

print("here is value after insert the


1000 on index 3 $number");

// here is value after insert the 1000 on


index 3 [1, 20, 40, 1000, 30, 400, 50,
2000]

// number.insert(4, 2000);

number.insertAll(3, [1000, 2000]);

print("here is value after insert the


more vlaue on index 3 $number");
// here is value after insert the more
vlaue on index 3 [1, 20, 40, 1000, 2000,
1000, 30, 400, 50, 2000]

number.remove(1000);

subjects.remove("ISoftware");

print("here is list remove 1000 after


shown $number $subjects");

//[maths, english, physics, Bio, ITC]

//here is list remove 1000 after shown


[1, 20, 40, 2000, 1000, 30, 400, 50,
2000]

number.removeAt(8);

print("here is list after remove 8 index


$number");
//here is list after remove 8 index [1,
20, 40, 2000, 1000, 30, 400, 50]

List emptyList = [2, 3, 4, "remove",


true];

print("hree is list before removeal


$emptyList");

emptyList.clear();

print("hree is list after removeal


$emptyList");

//output
//hree is list before removeal [2, 3, 4,
remove, true]

// hree is list after removeal []

print("here is contain show value in


bool // ${number.contains(20)}");

//here is contain show value in bool //


false

// here is contain show value in bool //


true

print("here is number before sort


$number");
number.sort();

print("here is number list after sort


$number");

// here is number before sort [1, 20, 40,


2000, 1000, 30, 400, 50]

// here is nuber list after sort [1, 20,


30, 40, 50, 400, 1000, 2000]

bool hasGreaterThan30 =
number.any((element) => element > 30);

print("Does any number > 30 exist?


$hasGreaterThan30");
//Does any number > 30 exist? true

In Dart, a Map is an unordered collection of key-value pairs. Each key is unique, but
multiple keys can have the same value. Maps are useful when you want to associate
keys with values, similar to objects in JavaScript.

Key Characteristics of Dart Map:

●​ Key-value pairs: Every entry in a Map has a key and a corresponding value.
●​ Keys are unique: A key can only appear once in a Map.
●​ Dynamic size: Maps are growable; you can add or remove entries.

// Creating a map with key-value pairs

Map<String, String> capitals = {

'USA': 'Washington D.C.',

'France': 'Paris',

'Japan': 'Tokyo'

};
print("Original Map: $capitals\n");

// 1. .length - Returns the number of key-value pairs

print("Length of the map: ${capitals.length}");

// 2. .isEmpty - Checks if the map is empty

print("Is the map empty? ${capitals.isEmpty}");

// 3. .isNotEmpty - Checks if the map is not empty

print("Is the map not empty? ${capitals.isNotEmpty}\n");

// 4. .keys - Returns all the keys in the map

print("Keys in the map: ${capitals.keys}");

// 5. .values - Returns all the values in the map

print("Values in the map: ${capitals.values}\n");


// 6. .containsKey() - Checks if the map contains a specific
key

print("Does the map contain the key 'France'?


${capitals.containsKey('France')}");

// 7. .containsValue() - Checks if the map contains a


specific value

print("Does the map contain the value 'Paris'?


${capitals.containsValue('Paris')}\n");

// 8. .addAll() - Adds all key-value pairs from another map

Map<String, String> moreCapitals = {

'India': 'New Delhi',

'Germany': 'Berlin'

};

capitals.addAll(moreCapitals);

print("After adding more capitals: $capitals\n");

// 9. .remove() - Removes a key-value pair by key


capitals.remove('Japan');

print("After removing 'Japan': $capitals\n");

// 10. .clear() - Removes all entries from the map

Map<String, String> emptyMap = {'Test': 'Value'};

emptyMap.clear();

print("After clearing the map: $emptyMap\n");

// 11. .update() - Updates the value for an existing key

capitals.update('USA', (value) => 'New York');

print("After updating USA's capital: $capitals\n");

// 12. .putIfAbsent() - Adds a key-value pair only if the


key is not already present

capitals.putIfAbsent('Japan', () => 'Tokyo');

print("After using putIfAbsent for 'Japan': $capitals\n");

// 13. .forEach() - Iterates over the map's entries

print("Iterating over the map:");


capitals.forEach((key, value) {

print("$key: $value");

});

// 14. .map() - Creates a new map with the modified


key-value pairs

Map<String, String> modifiedMap = capitals.map((key, value)


{

return MapEntry(key, value.toUpperCase());

});

print("\nModified map with uppercase values: $modifiedMap");

// 15. .entries - Returns an iterable of key-value pairs

print("\nMap entries:");

capitals.entries.forEach((entry) {

print("${entry.key}: ${entry.value}");

});

// 16. .cast() - Casts the map to a different type


Map<Object, Object> objectMap = capitals.cast<Object,
Object>();

print("\nMap cast to Object: $objectMap");

// 17. .removeWhere() - Removes key-value pairs based on a


condition

capitals.removeWhere((key, value) => key.startsWith('I'));

print("\nAfter removing entries where key starts with 'I':


$capitals");

// 18. .containsKey() - Checking a key again for


demonstration

print("\nDoes the map contain the key 'India'?


${capitals.containsKey('India')}");

.length: Shows the number of key-value pairs in the map.

.isEmpty and .isNotEmpty: Check if the map is empty or not.

.keys: Get all the keys in the map.

.values: Get all the values in the map.

.containsKey() and .containsValue(): Check if the map contains a specific key


or value.

.addAll(): Adds all key-value pairs from another map.


.remove(): Removes an entry by key.

.clear(): Removes all entries from the map.

.update(): Updates the value for a key.

.putIfAbsent(): Adds a key-value pair only if the key doesn't already exist.

//later we will discuss inshaaAllah

.forEach(): Iterates over each entry in the map.

.map(): Creates a new map by applying a function to each key-value pair.

.entries: Returns all key-value pairs as MapEntry objects.

.cast(): Casts the map to a different type.

.removeWhere(): Removes entries based on a condition.

Class work​

main() {

Map firstMap = {

"first": "Name",

"second": 20,

"thrid": 12.30,

"fourth": ["s1", "s2", "s3"],


// "fifth": {

// "inside first value": " XYZ",

// "inside second vlaue":" UNDP"

// }

};

print("here is my first map $firstMap");

//

// here is my first map {first: Name,


second: 20, thrid: 12.3, fourth: [s1, s2,
s]}

// here is my first map {first: Name,


second: 20, thrid: 12.3, fourth: [s1, s2,
s3], fifth: {inside first value: XYZ,
inside second vlaue: UNDP}}
print("here is map length
${firstMap.length}");

// here is map length 4

print("i want to just print keys


${firstMap.keys}");

// i want to just print keys (first,


second, thrid, fourth)

print("i wnt print values


${firstMap.values}");

// i wnt print values (Name, 20, 12.3,


[s1, s2, s3])

print("i want print time


${firstMap["thrid"]}");
//i want print time 12.3

print("${firstMap["fourth"][1]}");

// s2

print("here before update index


$firstMap");

firstMap.update("thrid", (update) =>


[10.30, 12.30]);

print("here is after updata $firstMap");

//here before update index {first: Name,


second: 20, thrid: 12.3, fourth: [s1, s2,
s3]}
// here is after updata {first: Name,
second: 20, thrid: [10.3, 12.3], fourth:
[s1, s2, s3]}

// Map secondMap = {

// "": "",

// "": "",

// "": ""

// };

// Map<String, String> thridMap =

// {

// "Name": "",

// "class": "",
// "Time": "",

// "Student": [],

// };

In Dart, arithmetic operations are used to perform mathematical calculations. The


common arithmetic operators are:

1.​ Addition (+)


2.​ Subtraction (-)
3.​ Multiplication (*)
4.​ Division (/)
5.​ Increment (++)
6.​ Decrement (--)

void main() {

int a = 10;

int b = 3;
// Addition (+)

int sum = a + b;

print("Addition (a + b): $sum");

// Subtraction (-)

int difference = a - b;

print("Subtraction (a - b): $difference");

// Multiplication (*)

int product = a * b;

print("Multiplication (a * b): $product");

// Division (/)

double quotient = a / b;

print("Division (a / b): $quotient");

// Increment (++)

a++; // Increment a by 1

print("Incremented a (++a): $a");

// Decrement (--)

b--; // Decrement b by 1

print("Decremented b (--b): $b");


}

Dart basic Day 3/4

If-else / nested if-else / loops


In Dart, the if-else statement is used to execute a block of code based on a
condition. If the condition is true, the code inside the if block runs. If it's false,
the code inside the else block runs.

Basic if-else Syntax:

if (condition) {

// Code to execute if the condition is true

} else {

// Code to execute if the condition is false

Example of if-else in Dart:

void main() {

int age = 18;


if (age >= 18) {

print("You are an adult.");

} else {

print("You are a minor.");

●​ Explanation:
○​ If age is greater than or equal to 18, the first block (You are an
adult.) is executed.
○​ Otherwise, the second block (You are a minor.) runs.

Nested if-else in Dart:

You can also place one if-else statement inside another if-else. This is called
nested if-else, and it allows for checking multiple conditions.

Syntax for Nested if-else:

if (condition1) {

// Code to execute if condition1 is true

if (condition2) {

// Code to execute if condition2 is true

} else {

// Code to execute if condition2 is false

}
} else {

// Code to execute if condition1 is false

void main() {

int score = 85;

if (score >= 90) {

print("You got an A grade.");

} else if (score >= 80) {

print("You got a B grade.");

} else if (score >= 70) {

print("You got a C grade.");

} else {

print("You need to improve.");

Explanation:

●​ The code first checks if the score is 90 or above. If true, it prints "You got
an A grade."
●​ If not, it checks if the score is 80 or above. If true, it prints "You got a B
grade."
●​ If that is also false, it checks if the score is 70 or above and prints "You got
a C grade."
●​ If none of the conditions are met, the code falls to the last else block and
prints "You need to improve."

Nested if-else Example in Dart:

void main() {

int temperature = 35;

if (temperature > 30) {

print("It's a hot day.");

if (temperature > 40) {

print("Make sure to stay hydrated!");

} else {

print("Wear light clothes.");

} else {

print("It's a nice day.");

if (temperature < 20) {

print("You might want a jacket.");

} else {

print("Perfect weather for a walk!");


}

●​ Explanation:
○​ If the temperature is greater than 30, it first prints "It's a hot day."
○​ Inside that block, if the temperature is also greater than 40, it prints
"Make sure to stay hydrated!"
○​ Otherwise, it prints "Wear light clothes."
○​ If the temperature is not greater than 30, it prints "It's a nice day."
Then it checks if the temperature is below 20 and prints "You might
want a jacket." If it's not below 20, it prints "Perfect weather for a
walk!"

Key Points:

●​ Use if-else for simple decision making.


●​ Use nested if-else for multiple conditions.
●​ You can nest if-else statements as deep as necessary, but try to avoid
over-nesting to keep your code clean and readable.

Class work

main() {
// equal to ==
// int a = 11;
// if (a == 10) {
// print("yes the A value is 10");
// } else {
// print("the A vlaue is not 10");
// }

///yes the A value is 10


/// the A vlaue is not 10

// greater then Or equal to >=

// int a = 9;
// if (a >= 10) {
// print("the a value is 10 or greater
then 10 ");
// } else {
// print("the a value is less then 10");
// }
// the a value is 10 or greater then 10
// the a value is less then 10

// less then Or equal to <=


// int a = 25;
// if (a <= 20) {
// print("here is run if body and the
value of a is $a");

// // here is run if body and the value


of a is 15
// } else {
// print("here is else body vlaue is
greater then 20");
// // here is else body vlaue is greater
then 20
// }

// not equal to !=
// String userStatus = "unauthrized";
// String userStatus = "authrized";
// if (userStatus != "unauthrized") {

// print("wellcome to payment screen");

// // wellcome to payment screen

// } else {

// print("please complete your


verfication");

// }

// greater than >


// List number = [9, 10, 30, 45];

// if (number[1] > 20) {


// print("list thrid element is greater
than 20");

// //thrid element is greater than 20


// } else {
// print("please verify it");
// // please verify it
// }

// List secondNumber = [9, 10, 30, 45];

// if (secondNumber[1] < 20) {


// print("list second element is less
than 20");

// // list second element is less than 20


// } else {
// print("please verify it");
// // please verify it
// }

// &&

// int a = 30;
// String b = "Passed";
// if (a >= 80 && b == "Passed") {

// print("this student marks is great");

// // this student marks is great

// }
// else{
// print("this student marks is not
great");
// //this student marks is not great
// }

// || or

// int b = 40;
// String c = "NotPass";

// if(b >= 80 || c == "Passed"){


// print("this student marks is not well
but passed");

// }
// else{
// print("this student not passed");
// //this student not passed
// }

//
///////////////////////////////////////////
////if else in side first ( if )

// int a = 11;
// String b = "userlogin";
// List c = ["s1", "s2", "s3", "s4",
"s5"];

// if (a == 10) {
// if (b == "userlogin") {
// if (c.length == 5) {

// print("all conditions are true");


// //all conditions are true
// } else {
// print("c condition is false");
// }
// } else {
// print("b condition is false");
// }
// } else {
// print("a condition is false");
// }

// nested if else

// int temperature = 19;

// if (temperature > 30) {


// print("It's a hot day.");
// if (temperature > 40) {
// print("Make sure to stay
hydrated!");
// } else {
// print("Wear light clothes.");

// // It's a hot day.


// // Wear light clothes.
// }
// } else {
// print("It's a nice day.");
// if (temperature < 20) {
// print("You might want a jacket.");

// // It's a nice day.


// // You might want a jacket.
// } else {
// print("Perfect weather for a
walk!");

// // It's a nice day.


// // Perfect weather for a walk!
// }
// }

// nested if else
// int score = 70;

// if (score >= 90) {


// print("You got an A grade.");

// if (score >= 80) {}


// else{

// }
// } else if (score >= 80) {
// print("You got a B grade.");
// } else if (score >= 70) {
// print("You got a C grade.");
// // You got a C grade.
// } else {
// print("You need to improve.");
// //You need to improve.
// }
}

LOOPS in dart

In Dart, loops allow you to execute a block of code multiple times. There are three
commonly used loop structures: for, for-in, and forEach. Let's explore each
one.

1. for Loop
A for loop is used when you know how many times you want to run a block of
code. It consists of three parts:

●​ Initialization: Set the starting value for the loop counter.


●​ Condition: The loop runs as long as this condition is true.
●​ Increment/Decrement: Modify the counter after each iteration.

Syntax for for Loop:

for (initialization; condition; increment/decrement) {


// Code to execute in each iteration
}

void main() {
// Loop from 1 to 5
for (int i = 1; i <= 5; i++) {
print("Iteration $i");
}
}

Explanation:

○​ The loop starts with i = 1 and runs until i is greater than 5.


○​ The value of i increases by 1 after each iteration.
○​ It prints "Iteration 1", "Iteration 2", etc., up to 5.
●​

if (number % 2 == 0)

{ print("$number is even"); }
else { print("$number is odd"); }

2. for-in Loop

The for-in loop is used to iterate over elements in a collection like a list, set, or map.
It's easier to use when you just want to access each element directly without worrying
about the index or length of the collection.

Syntax for for-in Loop:

for (var element in collection) {


// Code to execute for each element in the collection
}

Example of for-in Loop:


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

// Loop through each fruit in the list


for (var fruit in fruits) {
print(fruit);
}
}

Explanation:

●​ The for-in loop automatically goes through each element in the fruits list
and assigns it to fruit.
●​ It prints "Apple", "Banana", and "Cherry" one by one.

3. forEach Loop

The forEach method is another way to iterate over collections, and it's typically used
with functions. It executes a function for each element in the collection.

Syntax for forEach Loop:

collection.forEach((element) {
// Code to execute for each element
});

Example of forEach Loop:

void main() {
List<int> numbers = [10, 20, 30];

// Loop through each number using forEach


numbers.forEach((number) {
print(number);
});
}

Explanation:

●​ The forEach method goes through each element in the numbers list and
executes the function (which prints each number).
●​ It prints 10, 20, and 30.
Summary:

●​ for loop: Best when you need full control over the loop index and condition.
●​ for-in loop: Ideal for iterating over collections without needing the index.
●​ forEach loop: A clean way to apply a function to every element in a collection.

These loops are all useful in different situations, and Dart gives you the flexibility to choose the
best one for your task.

Class work

import 'dart:developer';

main() {
////////////////////// 1
// List num = [];

// print("here is list lenght ${num.length}


here is complete list $num");

// for (int xyz = 0; xyz <= 100; xyz++) {


// num.add(xyz);
// print("here is list after adding data
${num.length} , here is list $num");
// }

///////////////////// 2

// List even = [];


// List odd = [];

// print("before data Even list $even");


// print("before data odd list $odd");

// for (int a = 0; a <= 100; a++) {


// if (a % 2 == 0) {
// even.add(a);
// } else {
// odd.add(a);
// }

// for(int b = 0; b <= even.length ; b++ ){

// }

// print("here is even list after adding


data $even");
// print("here is odd list after adding data
$odd");
// }

//////////////// 3

List<String> fruits = ['Apple', 'Banana',


'Cherry'];

// Loop through each fruit in the list

// forIn loop
for (var a in fruits) {
print("Fruit: $a");
print(a);
}

// for

for (int a = 0; a < fruits.length; a++) {


print("Fruit: ${fruits[a]}");
}
fruits.forEach((f) {
print("here is all fruit $f");
});
}

Dart basics day 5

Functions:

A function in Dart is a block of reusable code that performs a specific task.


Functions allow you to organize code into smaller, manageable parts and can be
called multiple times from different parts of a program. They can accept input
(parameters) and optionally return a result (return value).

returnType functionName(parameters) {

// Function body

return value; // Optional

Return Type: Specifies the data type the function will return. If no value is
returned, use void.

Function Name: The name of the function.

Parameters: Variables that are passed into the function.


Return Value: A value that the function returns after execution (optional).
// Main function to run all examples

void main() {

// 1. Simple Function

print("Simple Function:");

sayHello();
// 2. Function with return type

print("\nFunction with Return Type:");

int result = add(10, 5);

print("Sum: $result");

// 3. Function with parameters/arguments

print("\nFunction with Parameters:");

greet("Alice", 25);

// 4. Function with optional positional parameters

print("\nFunction with Optional Positional Parameters:");

describe("John");

describe("John", 30);

// 5. Function with optional named parameters

print("\nFunction with Optional Named Parameters:");

introduce(name: "David");

introduce(name: "David", age: 40);


// 6. Function with default values

print("\nFunction with Default Values:");

orderPizza("Cheese");

orderPizza("Pepperoni", 3);

// 7. Anonymous Function

print("\nAnonymous Function:");

var numbers = [1, 2, 3, 4];

numbers.forEach((number) {

print("Number: $number");

});

// 8. Using a Class

print("\nUsing a Class:");

Person person = Person("John", 30);

person.sayDetails();

// 9. Class with a method that returns a value


print("\nClass with Return Method:");

Calculator calc = Calculator();

int sum = calc.add(10, 20);

print("Sum from Calculator: $sum");

// 10. Class with named constructor

print("\nClass with Named Constructor:");

Car car = Car.electric("Tesla", "Model S");

car.displayInfo();

////////////////////////////////////////////////////////////
/

// Function Examples

// 1. Simple Function

void sayHello() {

print("Hello, Dart!");

}
// 2. Function with Return Type

int add(int a, int b) {

return a + b;

// 3. Function with Parameters

void greet(String name, int age) {

print("Hello $name, you are $age years old.");

// 4. Function with Optional Positional Parameters

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

if (age > 0) {

print("$name is $age years old.");

} else {

print("$name's age is unknown.");

}
// 5. Function with Optional Named Parameters

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

if (age > 0) {

print("$name is $age years old.");

} else {

print("This is $name.");

// 6. Function with Default Values

void orderPizza(String type, [int quantity = 1]) {

print("Ordered $quantity $type pizza(s).");

1.​ Simple Function: sayHello() prints a message.


2.​ Return Type Function: add(int a, int b) takes two integers, adds them,
and returns the result.
3.​ Function with Parameters: greet(String name, int age) takes two
arguments and prints a greeting.
4.​ Optional Positional Parameters: describe(String name, [int age])
allows an optional age parameter.
5.​ Optional Named Parameters: introduce({String name, int age})
allows named parameters and gives default values.
6.​ Function with Default Values: orderPizza(String type, [int
quantity = 1]) sets a default value for quantity if not provided.
7.​ Anonymous Function: Used inside forEach to print each number from the
list.

Function class work


main() {
//calling function
// findArea();

//2
// nameFun("Atiq", 25);
// my name is Atiq and my age is 25

// nameFun("xyz", 19);
///3
// firstFunction(30, 50);
// here is lenght area 160

//4

// secondFunction(name: "XYZ", age: 24);


// here is my name XYZ and my age is 24 24

//5

// thridFunction(name: "XYZ", Class: "UNDP");

//6
forthFunction(name: "XYZ", age: 23);
// here is value of name XYZ and age 23

// my name is i learn in class

// my name is XYZ i learn in class UNDP


}
/////////////////////1
findArea() {
int length = 20;
int width = 10;

int area = 2 * (length + width);

print("here is lenght area $area");


}

///////////2
nameFun(String name, int age) {
print("my name is $name and my age is $age");
}

////////////3

firstFunction(int length, int width) {


int area = 2 * (length + width);
print("here is lenght area $area");
}
/////4
secondFunction({String? name, int? age}) {
// assign age int ot another var

print("here is my name $name and my age is


$age ");
print(age);
}

//5

thridFunction({String name = "", String Class =


"undp"}) {
print("my name is $name i learn in class
$Class");
}

////6

forthFunction({required String name, required


int age}) {
print("here is value of name $name and age
$age");
}

int fifthFuntion(int a , int b) {

return a+b;
}

24.oct

main() {
//calling
// addNumber(a: 300, b: 20);
// // the a value is 300
// // and the b vlaue is 20

// int result = addNumber(a: 40, b: 100);

// print("the result value is $result");

List<int> evenList = evenNumber();


print("here is even list till 50 $evenList");

// here is even list till 50 [0, 2, 4, 6, 8,


10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32,
34, 36, 38, 40, 42, 44, 46, 48]

// the a value is 300


// and the b vlaue is 20
// the a value is 40
// and the b vlaue is 100
// the result value is 140
}

///

int addNumber({required int a, required int b})


{
print("the a value is $a \n and the b vlaue is
$b");
return a + b;
}
List<int> evenNumber() {
List<int> num = [];

for (int a = 0; a < 50; a++) {


if (a % 2 == 0) {
num.add(a);
}
}
return num;
}

Dart Last day

Object-Oriented Programming (OOP) in Dart follows the same principles as in


other object-oriented languages like Java, C++, or Python. The core concepts of
OOP are:

1.​ Classes and Objects


2.​ Encapsulation
3.​ Inheritance
4.​ Polymorphism
5.​ Abstraction

Let's go through each of these concepts one by one with examples in Dart:

1. Classes and Objects


A class is a blueprint for creating objects (instances). An object is an instance of
a class.

Example:

dart

// Class definition

class Car {

String brand = '';

String model = '';

int year = 0;

// Method to display car information

void displayInfo() {

print("Car: $brand $model, Year: $year");

void main() {

// Creating objects from the class

Car car1 = Car();

car1.brand = "Toyota";

car1.model = "Corolla";
car1.year = 2021;

car1.displayInfo(); // Output: Car: Toyota Corolla, Year:


2021

Car car2 = Car();

car2.brand = "Honda";

car2.model = "Civic";

car2.year = 2022;

car2.displayInfo(); // Output: Car: Honda Civic, Year: 2022

class Person {

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.');

}
2. Encapsulation

Encapsulation is the concept of hiding the internal details of an object and only
exposing the necessary methods and properties. In Dart, you can make a variable
private by prefixing it with an underscore (_).

Example:

class Counter {

int _count = 0; // Private variable

int get count => _count; // Getter

int get count => _count; // Getter

set count(int value) => _count = value; //


Setter

void increment() {

_count++;

void reset() {

_count = 0;
}

// Example: Object creation

var counter = Counter();

counter.increment();

print(counter.count); // Output: 1

//// 2

class BankAccount {

String _accountHolderName = '';

double _balance = 0.0;

// Constructor

BankAccount(this._accountHolderName, this._balance);

// Getter for balance

double get balance => _balance;

// Method to deposit money

void deposit(double amount) {

if (amount > 0) {
_balance += amount;

print("Deposited: \$${amount}");

// Method to withdraw money

void withdraw(double amount) {

if (amount > 0 && amount <= _balance) {

_balance -= amount;

print("Withdrew: \$${amount}");

} else {

print("Insufficient balance");

void main() {

BankAccount account = BankAccount('John Doe', 500.0);

account.deposit(150.0); // Deposited: $150.0

account.withdraw(100.0); // Withdrew: $100.0

print("Balance: \$${account.balance}"); // Balance: $550.0


}

3. Inheritance

Inheritance allows one class (child class) to inherit the properties and methods of
another class (parent class).

Example:

lass Student extends Person {


String school;

// Constructor
Student(String name, int age, this.school);

// Method
void study() {
print('$name is studying at $school.');
}
}

// Parent class

class Animal {

String eyes = “”;

Int Legs = “”;

void makeSound() {

print("Animal is making a sound");

}
}

// Child class inheriting from Animal

class Dog extends Animal {

// Overriding the parent method

@override

void strongSmell() {

print("Dog is strong smell sense");

void main() {

Dog dog = Dog();

dog.makeSound(); // Output: Animal is making a sound

4. Polymorphism

Polymorphism allows objects of different classes to be treated as objects of a


common superclass. In Dart, polymorphism is often seen when overriding
methods.

Example:

dart

Copy code
class Animal {

void makeSound() {

print("Animal is making a sound");

class Dog extends Animal {

@override

void makeSound() {

print("Dog is barking");

class Cat extends Animal {

@override

void makeSound() {

print("Cat is meowing");

void main() {
Animal animal;

animal = Dog();

animal.makeSound(); // Output: Dog is barking

animal = Cat();

animal.makeSound(); // Output: Cat is meowing

5. Abstraction

Abstraction allows us to hide the implementation details and show only the
functionality to the user. In Dart, you can achieve abstraction using abstract
classes.

​Example in Real Life:

Think about a TV remote:

●​ When you press the volume up button, you don’t care about the circuitry
inside the remote. You just care that it increases the volume.
●​ The complex details (like wiring and circuits) are hidden from you. You only
interact with the simple buttons.

Example:

// Abstract class

abstract class Shape {


void draw(); // Abstract method

// Subclass implementing the abstract class

class Circle extends Shape {

@override

void draw() {

print("Drawing a circle");

class Rectangle extends Shape {

@override

void draw() {

print("Drawing a rectangle");

void main() {

Shape shape;
shape = Circle();

shape.draw(); // Output: Drawing a circle

shape = Rectangle();

shape.draw(); // Output: Drawing a rectangle

Summary:

●​ Class: Blueprint for creating objects.


●​ Object: Instance of a class.
●​ Encapsulation: Hiding details and providing controlled access.
●​ Inheritance: Child class inherits properties and methods from a parent
class.
●​ Polymorphism: A way to use different objects interchangeably with a
common interface.
●​ Abstraction: Hiding the implementation details and providing a simple
interface.

Each of these concepts works together to provide a flexible and reusable


structure for your Dart applications.

The @override annotation in Dart is used to indicate that a method in a subclass


is intended to override a method in its superclass. This is a way of informing both
the compiler and other developers that you're intentionally replacing the inherited
behavior of a method from the parent class.

Here's what it does:


●​ It allows a subclass to provide its own implementation of a method that is
already defined in the parent class.
●​ The @override annotation ensures that you're correctly overriding a
method. If there is no corresponding method in the superclass, the
compiler will give an error.

The @override annotation is optional, but using it is considered a good practice


as it improves code readability and reduces errors.

Why Use @override?

1.​ Intentional Override: It clearly indicates to the reader (and the Dart
compiler) that this method is meant to replace the method in the parent
class.
2.​ Error Prevention: If you misspell the method name or have a mismatch in
method signature, the compiler will throw an error, preventing potential
bugs.
Flutter UI
Step 1: System Requirements

Before you begin, ensure that your system meets the following minimum requirements:

●​ Windows 10 or later
●​ Git for Windows
●​ At least 600 MB of disk space (excluding IDE/tools)
●​ USB driver for your Android device (if using Android)

Step 2: Install Flutter SDK

1.​ Download Flutter SDK:


2.​ https://docs.flutter.dev/get-started/install/windows/mobile
○​ Go to the official Flutter website and download the latest Flutter SDK for
Windows.
○​ Extract the downloaded .zip file to a location where you want to store the SDK
(e.g., C:\src\flutter).
3.​ Update System Path:
○​ Add the Flutter bin directory to your system path:
■​ Open Search > Type "Environment Variables" > Open Edit the system
environment variables.
■​ Under the System Properties window, click on Environment Variables.
■​ In the System variables section, find Path and click Edit.
■​ Click New and add the path to Flutter’s bin directory, e.g.,
C:\src\flutter\bin.
■​ Click OK to apply changes.
4.​ Check Installation:

Open Command Prompt and run:​


bash​
Copy code​
flutter doctor

○​
○​ This command checks your environment for any missing dependencies. Ensure
that all checkmarks are green or install any missing components.
Step 3: Install Android Studio (Optional for Android Device)

To use a physical Android device, you'll need Android Studio for the Android SDK and device
drivers.

1.​ Download and Install Android Studio:


○​ Go to the official Android Studio download page.
○​ Install Android Studio, and during installation, select the Android SDK, SDK
Platform Tools, and Android Virtual Device (AVD).
2.​ Configure Android Studio:
○​ Open Android Studio, go to Settings > System Settings > Android SDK.
○​ Ensure that the SDK Platforms and SDK Tools tabs have the latest Android
SDK installed.
○​ Open SDK Manager and install the Google USB Driver if using a physical
Android device.
3.​ Configure USB Debugging on Android:
○​ On your Android device, go to Settings > About phone and tap Build number 7
times to enable Developer options.
○​ Then, go to Settings > Developer options and enable USB Debugging.

Step 4: Install VS Code

1.​ Download and Install VS Code:


○​ Go to the VS Code website and download the installer for Windows.
○​ Follow the prompts to install Visual Studio Code.
2.​ Install Flutter & Dart Extensions:
○​ Open VS Code.
○​ Go to the Extensions tab on the left side (or press Ctrl+Shift+X).
○​ Search for Flutter and install the Flutter extension (this also installs the Dart
extension).

Step 5: Set Up a Physical Device

1.​ Connect Your Android Device:


○​ Connect your Android device to your computer via USB.
○​ Ensure that USB Debugging is enabled on your Android device.
2.​ Verify Device is Recognized:

In the Command Prompt or VS Code Terminal, run the following command to verify that your
device is detected:​
bash​
Copy code​
flutter devices

○​
○​ Your connected Android device should appear in the list. If not, ensure that you
have installed the necessary USB drivers for your device.

Step 6: Create a Flutter Project in VS Code

1.​ Create a New Flutter Project:


○​ In VS Code, press Ctrl+Shift+P to open the Command Palette.
○​ Type Flutter: New Project and select it. Flutter Application
○​ Choose a project name and directory.
2.​ Open the Project:
○​ After the project is created, VS Code will automatically open it. If not, manually
open the folder in VS Code.

Step 7: Run the Flutter App on a Physical Device

1.​ Select the Target Device:

In the bottom-right corner of VS Code, you should see your physical device listed as a target. If
not, run:​
bash​
Copy code​
flutter devices

○​
○​ Ensure the device is selected in VS Code.
2.​ Run the App:
○​ Press F5 or go to Run > Start Debugging to run the Flutter app on the
connected physical device.

Step 8: Troubleshooting
If you encounter any issues, run:​
bash​
Copy code​
flutter doctor
●​ This command helps diagnose any missing dependencies or issues.
●​ If your physical device isn’t recognized:
○​ Ensure USB Debugging is enabled.
○​ Check if Google USB Driver is installed via SDK Manager.
○​ Try changing the USB mode to File Transfer (MTP) on your Android device.

Flutter History with Version Updates

Flutter, initially released by Google in 2017, has undergone significant development over the
years. Here’s a brief overview of its history with notable version updates:

1.​ Flutter 1.0 (December 2018)


○​ Officially launched as the first stable release.
○​ Aimed at providing a cross-platform solution for iOS and Android app
development.
○​ Featured the use of the Dart programming language.
2.​ Flutter 1.2 (February 2019)
○​ Introduced various new features, bug fixes, and performance improvements.
○​ Enhanced support for Android App Bundles and iOS 32-bit devices.
3.​ Flutter 1.5 (May 2019)
○​ Improved iOS support, including better support for iOS 13 and integration with
Material design.
○​ Added several widgets, including Cupertino widgets for iOS-style applications.
4.​ Flutter 1.9 (September 2019)
○​ Brought web support in a technical preview.
○​ Updated the Material design specification.
○​ Supported the Apple iOS 13 dark mode.
5.​ Flutter 1.12 (December 2019)
○​ Significant improvements in Android and iOS performance.
○​ Improved Add-to-App feature for integrating Flutter into existing Android and
iOS apps.
6.​ Flutter 2.0 (March 2021)
○​ Major release with full support for web and desktop (Windows, macOS, Linux)
applications.
○​ Enhanced sound null safety support for improved code quality.
○​ Introduced Flutter DevTools.
7.​ Flutter 2.2 (May 2021)
○​ Brought new performance optimizations, especially for web applications.
○​ Improved desktop support, including better text rendering and keyboard
shortcuts.
8.​ Flutter 2.5 (September 2021)
○​ Enhanced Android full-screen mode.
○​ Improved touch support for web applications.
○​ Improved Dart DevTools for performance profiling.
9.​ Flutter 2.8 (December 2021)
○​ Released with significant performance improvements.
○​ Brought a lot of changes for Flutter web, improving accessibility and keyboard
interaction.
10.​Flutter 3.0 (May 2022)
○​ Official stable release for macOS and Linux desktop support.
○​ New Material 3 design system.
○​ Enhanced Firebase integration with new FlutterFire CLI.
11.​Flutter 3.3, 3.7 (September 2022 - February 2023)
○​ Improvements in developer tools and added new widgets.
○​ More Material 3 support and optimization for web and desktop platforms.
12.​Flutter 3.10 (May 2023)
○​ Enhanced support for macOS and Windows platforms.
○​ Improved navigation APIs.

○​
Updates for performance and memory management.
Metarial App.
MaterialApp is an extension of the generic top-level widget provided by Flutter
: WidgetsApp. WidgetsApp is a convenience widget that abstracts away several

features required for most mobile apps, such as setting up a navigator and using an app-wide
theme.

....................Listing 4.5. The top-level widget in

void main() { //////////1

runApp(
MyApp(), //////////2
);
}

class MyApp extends StatelessWidget { ///////////3

@override
Widget build(BuildContext context) {
// ...
return MaterialApp( ////////////4
title: 'App',
debugShowCheckedModeBanner: false,
theme: theme,
home: PageContainer(),
);
}
}

Explanation of Key Flutter Widgets with Examples

1.​ SafeArea
○​ Description: A widget that insets its child to avoid system UI areas like the
notch, status bar, and bottom navigation.
○​ Use Case: Use SafeArea to ensure that the UI elements are displayed within
safe bounds on devices with curved edges, notches, or status bars.



import 'package:flutter/material.dart';

void main() {

runApp(MyApp());

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(

home: SafeArea(

child: Scaffold(

body: Center(child: Text('SafeArea Example')),

),

),

);

}
2.​ Scaffold
○​ Description: Provides a basic material design layout structure like an AppBar,
Drawer, FloatingActionButton, and BottomNavigationBar.
○​ Use Case: Scaffold is typically the base for building any screen in a Flutter
app.​

import 'package:flutter/material.dart';

void main() {

runApp(MyApp());

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(

home: Scaffold(

appBar: AppBar(title: Text('Scaffold Example')),

body: Center(child: Text('Hello, Flutter!')),

floatingActionButton: FloatingActionButton(

onPressed: () {},

child: Icon(Icons.add),

),

),

);
}

3.​
4.​ Text
○​ Description: Used to display a string of text with various styles and
configurations.
○​ Use Case: Text is the most commonly used widget for displaying labels, titles,
or other textual information.

dart​
Copy code​
import 'package:flutter/material.dart';

void main() {

runApp(MyApp());

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(

home: Scaffold(

body: Center(

child: Text(

'Hello, Flutter!',

style: TextStyle(fontSize: 24, color: Colors.blue),

),
),

),

);

5.​
6.​ Center
○​ Description: A widget that centers its child both horizontally and vertically within
its parent.
○​ Use Case: Center is often used to centralize widgets, making the UI design
clean and balanced.

dart​
Copy code​
import 'package:flutter/material.dart';

void main() {

runApp(MyApp());

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(

home: Scaffold(

body: Center(

child: Text('Centered Text'),


),

),

);

7.​
8.​ AppBar
○​ Description: A material design app bar with optional actions, titles, and icons.
○​ Use Case: AppBar is used as the top app bar on each screen, often containing
the screen title, navigation icons, and actions.

import 'package:flutter/material.dart';

void main() {

runApp(MyApp());

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(

home: Scaffold(

appBar: AppBar(

title: Text('AppBar Example'),

actions: [

IconButton(
icon: Icon(Icons.search),

onPressed: () {},

),

],

),

body: Center(child: Text('Hello, AppBar!')),

),

);

9.​ }

Safearea
Scaffold
Text
center
appbar.
Flutter Widgets 2-Week Study Plan
Day Widgets Topics & Notes

Week
1

Day 1 Scaffold, SafeArea Scaffold (basic page layout), SafeArea (avoid


notches); build a basic app layout.

Day 2 AppBar, Center, Text AppBar (top bar), Center (centering widget), Text (text
display); create an app with title and centered text.

Day 3 Container, Padding, Align Container (box styling), Padding (spacing), Align
(alignment); explore layout adjustments and spacing
with a styled box.

Day 4 Row, Column, SizedBox Row & Column (layout), SizedBox (spacer); arrange
widgets in rows and columns and manage spacing.

Day 5 Icon, Image, CircleAvatar Icon (display icons), Image (display images),
CircleAvatar (circular image); display an avatar with
icons and images.

Day 6 ListTile, Divider, Card ListTile (list items), Divider (separator), Card (material
card); create a list with separators and cards.

Day 7 FloatingActionButton, FloatingActionButton (FAB), ElevatedButton (elevated


ElevatedButton, InkWell button), InkWell (tap effect); create buttons with
different functionalities.

Week
2

Day 8 TextField, Form, Checkbox TextField (input), Form (form validation), Checkbox
(checkbox); create a simple form with input fields and
checkboxes.

Day 9 Switch, Slider, RadioListTile Switch (toggle), Slider (range selector), RadioListTile
(radio button); build a settings screen with switches
and sliders.

Day Stack, Positioned Stack (overlapping widgets), Positioned (control stack


10 positioning); experiment with layered UI using Stack
and Positioned.
Day Expanded, Flexible Expanded & Flexible (adjustable child sizes); manage
11 responsive layout and widget resizing.

Day Drawer, Drawer (side menu), BottomNavigationBar (bottom


12 BottomNavigationBar, nav bar), TabBar (tab navigation); create an app with
TabBar multiple screens and navigation options.

Day GestureDetector, GestureDetector (detect gestures), Draggable (drag


13 Draggable, Dismissible and drop), Dismissible (swipe to dismiss); add
interactivity through gesture-based actions.

Day FutureBuilder, FutureBuilder & StreamBuilder (async handling);


14 StreamBuilder create apps that display real-time or future-based data
like an API call or a counter with Stream.
Day 4​

Container

The Container widget is one of the most versatile and commonly used widgets in Flutter. It
can be used for styling, positioning, and adding spacing or padding around widgets.

●​ Properties:
○​ color: Sets the background color of the container.
○​ margin: Adds space around the container.
○​ padding: Adds space inside the container.
○​ width & height: Sets the container’s size.
○​ alignment: Aligns child widgets within the container.
○​ decoration: Adds styling like borders, rounded corners, and shadows.

Example:

dart
Copy code
Container(
width: 150,
height: 100,
color: Colors.blue,
alignment: Alignment.center,
child: Text(
"Hello",
style: TextStyle(color: Colors.white),
),
);

In this example:

●​ The container is given a specific width and height.


●​ The text inside is aligned at the center, with a background color of blue.

2. Padding
The Padding widget provides space around a child widget by specifying padding on one or
more sides. It is generally used to avoid having a widget directly touch another widget or the
edges of the screen.

●​ Properties:
○​ padding: Accepts an EdgeInsets value to specify padding. Commonly used
options include:
■​ EdgeInsets.all(double value): Adds equal padding to all sides.
■​ EdgeInsets.symmetric(horizontal: double, vertical:
double): Adds padding horizontally or vertically.
■​ EdgeInsets.only(left: double, top: double, right:
double, bottom: double): Adds padding to specific sides.

Example:

dart
Copy code
Padding(
padding: EdgeInsets.all(20),
child: Text("This text has padding around it."),
);

In this example:

●​ The text widget will have a padding of 20 pixels on all sides, creating space around the
text.

3. Align

The Align widget is used to align its child within itself based on alignment coordinates, which
can be positioned anywhere within the parent.

●​ Properties:
○​ alignment: Accepts an Alignment value to specify where the child should be
positioned. Some common values are:
■​ Alignment.center: Centers the child widget.
■​ Alignment.topLeft: Positions the child at the top left.
■​ Alignment.bottomRight: Positions the child at the bottom right.
○​ widthFactor & heightFactor: Scale the Align widget based on the child widget's
size.
Example:

dart
Copy code
Align(
alignment: Alignment.bottomRight,
child: Icon(
Icons.star,
size: 50,
color: Colors.yellow,
),
);

In this example:

●​ The Align widget positions the star icon at the bottom right corner.

Summary Example with All Three Widgets

Below is an example of how Container, Padding, and Align can work together to create a
simple, styled layout.

dart
Copy code
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Container, Padding, and Align Example"),
),
body: Center(
child: Container(
width: 200,
height: 200,
color: Colors.blueAccent,
child: Padding(
padding: EdgeInsets.all(16),
child: Align(
alignment: Alignment.bottomRight,
child: Icon(
Icons.star,
color: Colors.yellow,
size: 50,
),
),
),
),
),
),
);
}
}

In this example:

●​ The container is styled with a background color and fixed dimensions.


●​ Padding creates space between the container’s edges and the aligned icon.
●​ Align positions the icon in the bottom-right corner.
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Container Shadow and Radius Example"),
),
body: Center(
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
color: Colors.blueAccent,
borderRadius: BorderRadius.circular(20), // Rounded corners
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5), // Shadow color
spreadRadius: 5, // Extent of the shadow
blurRadius: 10, // Softness of the shadow
offset: Offset(0, 5), // Position of the shadow (horizontal, vertical)
),
],
),
child: Center(
child: Text(
"Rounded & Shadowed",
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
),
),
),
);
}
}
DAY 7

FloatingActionButton

FloatingActionButton (FAB) is a circular button that floats above the UI, usually positioned
at the bottom right of the screen. It’s typically used for primary actions, like adding a new item or
starting a new activity.

●​ Properties:
○​ onPressed: A callback function triggered when the button is pressed.
○​ child: A widget (usually an icon) to display on the FAB.
○​ backgroundColor: Sets the background color of the button.
○​ tooltip: Shows a tooltip on a long press.

Example:

dart
Copy code
FloatingActionButton(
onPressed: () {
print("FAB pressed");
},
backgroundColor: Colors.blue,
tooltip: "Add Item",
child: Icon(Icons.add),
);

This FAB displays a + icon and executes the callback when tapped. It’s commonly used within a
Scaffold widget.

2. ElevatedButton

ElevatedButton is a standard button that elevates (gains a shadow) when pressed, giving it
a 3D look. It's used for primary actions, where you want the button to stand out against the
background.

●​ Properties:
○​ onPressed: A callback function triggered when the button is pressed.
○​ child: Text or other widgets to display inside the button.
○​ style: Allows customization of the button’s appearance (background color,
padding, shape, etc.).

Example:

dart
Copy code
ElevatedButton(
onPressed: () {
print("ElevatedButton pressed");
},
style: ElevatedButton.styleFrom(
primary: Colors.green, // Background color
onPrimary: Colors.white, // Text color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), // Rounded corners
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15),
),
child: Text("Click Me"),
);

In this example, the button has customized padding, rounded corners, and a green background
color with white text.

3. InkWell

InkWell is a touchable area that shows a ripple effect on press. Unlike the other button
widgets, InkWell is more flexible since it can be wrapped around any widget to make it
clickable.

●​ Properties:
○​ onTap: A callback function triggered when the InkWell is tapped.
○​ child: The widget inside InkWell that will be interactive.
○​ splashColor: Sets the ripple effect color when the InkWell is pressed.
○​ borderRadius: Adds rounded corners to the ripple effect.

Example:

dart
Copy code
InkWell(
onTap: () {
print("InkWell tapped");
},
splashColor: Colors.blue.withOpacity(0.3),
borderRadius: BorderRadius.circular(10), // Rounded ripple effect
child: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.lightBlueAccent,
borderRadius: BorderRadius.circular(10),
),
child: Text("Tap Me"),
),
);

In this example:

●​ InkWell is wrapped around a Container, making the entire area tappable.


●​ The ripple effect appears in light blue when tapped.

The Card widget in Flutter is a material design widget that provides a way to display information
in a rectangular box with rounded corners and an optional shadow effect. It’s commonly used to
highlight content within a container, making it stand out on the screen, such as displaying user
profiles, product details, or other individual data items.

Key Features of the Card Widget

●​ Elevation: Adds a shadow effect to the card to give it a lifted look.


●​ Shape: Controls the card's border shape, such as rounded corners.
●​ Margin: Adds space around the card.
●​ Color: Sets the card's background color.

Example: Simple Card with Content

Here’s a simple example of a Card widget with text and an icon.


dart

Copy code

import 'package:flutter/material.dart';

void main() {

runApp(MyApp());

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(

home: Scaffold(

appBar: AppBar(

title: Text("Card Widget Example"),

),

body: Center(

child: Card(

elevation: 4, // Controls the shadow depth

margin: EdgeInsets.all(16),

shape: RoundedRectangleBorder(

borderRadius: BorderRadius.circular(10), // Rounded


corners
),

child: Padding(

padding: EdgeInsets.all(16),

child: Row(

mainAxisSize: MainAxisSize.min,

children: [

Icon(Icons.account_circle, size: 40, color:


Colors.blue),

SizedBox(width: 16),

Column(

crossAxisAlignment: CrossAxisAlignment.start,

children: [

Text(

"John Doe",

style: TextStyle(fontSize: 18, fontWeight:


FontWeight.bold),

),

Text("Software Developer"),

],

),

],

),

),
),

),

),

);

Explanation of the Code

●​ elevation: Adds a shadow to make the card look lifted. Higher values increase the
shadow.
●​ margin: Adds space around the card so it doesn’t touch other elements or the screen
edge directly.
●​ shape: Specifies the card's shape. Here, RoundedRectangleBorder with
borderRadius adds rounded corners.
●​ Padding inside the Card: Adds internal spacing, so content isn’t directly touching the
edges of the card.
●​ Row and Column: Used to organize the icon and text within the card.

Additional Customizations

Color: You can change the card's background color:​


dart​
Copy code​
color: Colors.white,

1.​

Border: To add a border, you can use a shape with a BorderSide:​


dart​
Copy code​
shape: RoundedRectangleBorder(

borderRadius: BorderRadius.circular(10),

side: BorderSide(color: Colors.grey, width: 1),


),

2.​

The Card widget provides a flexible, easy-to-use layout tool for highlighting individual items.
This widget, combined with ListView or GridView, can make lists of Cards that work well for
complex apps like e-commerce and social media applications.

import
'package:flutter/material.dart';

class testingWidget extends


StatefulWidget {

const testingWidget({super.key});
@override

State<testingWidget> createState() =>


_testingWidgetState();

class _testingWidgetState extends


State<testingWidget> {

@override

void didChangeDependencies() {

// TODO: implement
didChangeDependencies

super.didChangeDependencies();

print("here is my
didChangeDependencies");

}
@override

void initState() {

super.initState();

print("here is my initState");

int counter = 0;

@override

Widget build(BuildContext context) {

print("recall my complete screen");

return Scaffold(

body: Column(
mainAxisAlignment:
MainAxisAlignment.center,

crossAxisAlignment:
CrossAxisAlignment.center,

children: [

Text(

"Here is counter value


$counter",

style: TextStyle(fontSize:
24),

),

TextButton(

onPressed: () {

setState(() {

counter++;
print("here is
counter value $counter");

});

},

child: Text("Increment
Data"))

],

),

);

}
example demonstrates:

1.​ initState:
●​ Called when the widget is first created
●​ Used for one-time initialization
●​ Initialize controllers and subscriptions
●​ Make initial API calls
2.​ didChangeDependencies:
●​ Called when an InheritedWidget that this widget depends on changes
●​ Access context-dependent resources
●​ Update data based on new dependencies
3.​ setState:
●​ Used to update the widget's state
●​ Triggers a rebuild of the widget
●​ Updates UI based on new state
4.​ dispose:
●​ Called when the widget is removed from the tree
●​ Clean up resources to prevent memory leaks
●​ Cancel subscriptions and close controllers

Here's how to use this widget with different scenarios:

dart

import
'package:flutter/material.dart';
class ThemeSwitcherPage extends
StatefulWidget {

@override

_ThemeSwitcherPageState createState()
=> _ThemeSwitcherPageState();

class _ThemeSwitcherPageState extends


State<ThemeSwitcherPage> {
bool isDarkMode = false; // Tracks
the current theme state

@override

Widget build(BuildContext context) {

return Scaffold(

appBar: AppBar(

title: Text("Dark/Light Theme


Toggle"),

backgroundColor: isDarkMode ?
Colors.black : Colors.blue,

actions: [

IconButton(

icon: Icon(isDarkMode ?
Icons.wb_sunny : Icons.nights_stay),
onPressed: () {

setState(() {

isDarkMode =
!isDarkMode; // Toggle theme mode

});

},

),

],

),

body: Container(

color: isDarkMode ?
Colors.black : Colors.white, //
Background color

alignment: Alignment.center,

child: Text(
"Hello, Flutter!",

style: TextStyle(

fontSize: 24,

color: isDarkMode ?
Colors.white : Colors.black, // Text
color

),

),

),

);

}
Explanation

1.​ State Variable (isDarkMode): Tracks whether the current theme is dark or light. Initially,
it's set to false (light mode).
2.​ AppBar: The AppBar includes a toggle button (sun/moon icon) in the actions list to
switch themes.
○​ The icon changes based on the current theme (Icons.wb_sunny for light and
Icons.nights_stay for dark).
3.​ setState: When the icon button is pressed, setState() toggles isDarkMode,
updating the UI.
4.​ Body:
○​ The Container background color changes based on the theme.
○​ The Text widget color is also dynamically updated to ensure readability in both
themes (white text for dark mode, black text for light mode).

This code provides a simple, responsive way to switch themes within a single screen using
setState.
ListView in Flutter

ListView is a scrollable list of widgets, commonly used to display data in a vertical list.

Example of a Simple ListView


dart
Copy code
class SimpleListView extends StatelessWidget {
final List<String> items = ["Item 1", "Item 2", "Item 3", "Item 4"];

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('ListView Example')),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index]),
);
},
),
);
}
}

Properties of ListView

●​ ListView.builder: Creates items on demand, good for long lists.


●​ ListView.separated: Adds a separator widget between items.
●​ ListView.custom: Customizes items using a SliverChildDelegate.
GridView in Flutter

GridView is a scrollable, two-dimensional array of widgets, often used to display images or


content in a grid pattern.

Example of a Simple GridView


dart
Copy code
class SimpleGridView extends StatelessWidget {
final List<String> items = ["One", "Two", "Three", "Four"];

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('GridView Example')),
body: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // Number of columns
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: items.length,
itemBuilder: (context, index) {
return Card(
color: Colors.amber,
child: Center(child: Text(items[index])),
);
},
),
);
}
}
Properties of GridView

●​ GridView.count: Creates a grid with a fixed number of tiles in each row.


●​ GridView.extent: Creates a grid with tiles that have a maximum cross-axis extent.
●​ GridView.builder: Builds items on demand, similar to ListView.builder.

To create a simple Flutter application that displays a list of products using a


ListView.builder, we first need to define a model for our product. The model will
contain four fields: productName, image, price, and description. After that, we will create
a list of products and display them in a list view

class Product {
final String productName;
final String image;
final double price;
final String description;

Product({
required this.productName,
required this.image,
required this.price,
required this.description,
});
}

You can create a list of products in your main widget or in a separate file.
List<Product> products = [
Product(
productName: 'Product 1',
image: 'https://via.placeholder.com/150',
price: 29.99,
description: 'This is the description for Product 1.',
),
Product(
productName: 'Product 2',
image: 'https://via.placeholder.com/150',
price: 19.99,
description: 'This is the description for Product 2.',
),
Product(
productName: 'Product 3',
image: 'https://via.placeholder.com/150',
price: 39.99,
description: 'This is the description for Product 3.',
),
Product(
productName: 'Product 4',
image: 'https://via.placeholder.com/150',
price: 49.99,
description: 'This is the description for Product 4.',
),
];

Now, we can create the main widget that uses ListView.builder to display the list of
products.

import 'package:flutter/material.dart';

class ProductListScreen extends StatelessWidget {


@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Products'),
),
body: ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return Card(
margin: EdgeInsets.all(8.0),
child: ListTile(
leading: Image.network(product.image),
title: Text(product.productName),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('\$${product.price.toStringAsFixed(2)}'),
SizedBox(height: 4.0),
Text(product.description),
],
),
),
);
},
),
);
}
}
The TextField widget in Flutter is used to create a text input field. It has
numerous properties to control its appearance, behavior, and how it
interacts with user input.

Here’s a breakdown of the main properties, along with examples of how


they’re used:

1. Basic Properties

●​ controller: Manages the text inside the TextField.


●​ decoration: Used for styling the TextField (e.g., adding labels,
icons, borders).
●​ keyboardType: Specifies the type of keyboard shown, such as for
numbers, emails, or plain text.
●​ obscureText: Hides the text for secure fields like passwords.

dart

Copy code

TextField(

controller: _controller, // Manages text input

decoration: InputDecoration(

labelText: "Username", // Label above the field

prefixIcon: Icon(Icons.person), // Icon at the start

border: OutlineInputBorder(), // Adds border around the


field

),
keyboardType: TextInputType.text, // Standard text
keyboard

obscureText: false, // Set to true for password fields

);

2. Styling Properties

●​ style: Controls the style of the input text.


●​ cursorColor: Changes the cursor color.
●​ cursorWidth: Sets the cursor’s width.
●​ textAlign: Aligns text (e.g., left, right, center).

dart

Copy code

TextField(

decoration: InputDecoration(

hintText: "Enter your name",

),

style: TextStyle(

fontSize: 18,

color: Colors.black,

),
cursorColor: Colors.blue,

cursorWidth: 2.0,

textAlign: TextAlign.left,

);

3. Behavioral Properties

●​ onChanged: Fires every time the text changes.


●​ onSubmitted: Fires when the user submits the input (e.g., presses
"Enter").
●​ enabled: Disables the TextField if set to false.
●​ readOnly: Makes the TextField non-editable but selectable.

dart

Copy code

TextField(

onChanged: (text) {

print("Text changed to: $text");

},

onSubmitted: (text) {

print("Submitted text: $text");

},
enabled: true, // Set to false to disable the field

readOnly: false, // Set to true to make the field


non-editable

);

4. Text Control Properties

●​ maxLength: Sets the maximum length of input.


●​ maxLines: Sets the maximum number of lines.
●​ minLines: Sets the minimum number of lines.
●​ expands: If true, makes the TextField expand to fill its parent.

dart

Copy code

TextField(

maxLength: 20, // Limits input to 20 characters

maxLines: 1, // Single-line input

minLines: 1,

expands: false, // Set to true to expand the TextField


vertically

);
5. Focus Management Properties

●​ focusNode: Allows manual control of the TextField's focus.


●​ autofocus: Automatically focuses the TextField when it appears.

dart

Copy code

FocusNode _focusNode = FocusNode();

TextField(

focusNode: _focusNode, // Control the focus state

autofocus: true, // TextField gains focus automatically

);

6. Advanced Properties

●​ textCapitalization: Controls the capitalization (e.g., characters,


words).
●​ inputFormatters: Allows formatting the input (e.g., limiting
characters).
●​ textInputAction: Specifies the action button on the keyboard (e.g.,
"done").

dart

Copy code
TextField(

textCapitalization: TextCapitalization.words, //
Capitalizes each word

inputFormatters: [LengthLimitingTextInputFormatter(10)],
// Limits length

textInputAction: TextInputAction.done, // Shows "Done"


button on keyboard

);

Full Example

Here's a comprehensive example that combines these properties into a


single TextField widget:

dart

Copy code

import 'package:flutter/material.dart';

import 'package:flutter/services.dart';

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

class MyApp extends StatelessWidget {


@override

Widget build(BuildContext context) {

return MaterialApp(

home: Scaffold(

appBar: AppBar(title: Text("TextField Example")),

body: Padding(

padding: const EdgeInsets.all(16.0),

child: MyTextField(),

),

),

);

class MyTextField extends StatefulWidget {

@override

_MyTextFieldState createState() => _MyTextFieldState();

}
class _MyTextFieldState extends State<MyTextField> {

TextEditingController _controller =
TextEditingController();

FocusNode _focusNode = FocusNode();

@override

void dispose() {

_controller.dispose();

_focusNode.dispose();

super.dispose();

@override

Widget build(BuildContext context) {

return TextField(

controller: _controller,

focusNode: _focusNode,

decoration: InputDecoration(

labelText: "Enter Text",

hintText: "Type something here...",


prefixIcon: Icon(Icons.edit),

suffixIcon: IconButton(

icon: Icon(Icons.clear),

onPressed: () => _controller.clear(),

),

border: OutlineInputBorder(),

),

keyboardType: TextInputType.text,

obscureText: false,

style: TextStyle(fontSize: 18, color: Colors.blue),

cursorColor: Colors.green,

cursorWidth: 2.0,

textAlign: TextAlign.left,

onChanged: (text) {

print("Text changed: $text");

},

onSubmitted: (text) {

print("Text submitted: $text");

},
maxLength: 30,

maxLines: 1,

autofocus: false,

textCapitalization: TextCapitalization.sentences,

inputFormatters: [

FilteringTextInputFormatter.allow(RegExp("[a-zA-Z0-9
]")),

LengthLimitingTextInputFormatter(30),

],

);

In this example:

●​ The TextField has a clear button in the suffix, controlled by the


controller.
●​ Custom input formatting allows only alphanumeric input.
●​ Text length is limited to 30 characters, with capitalization applied to
the beginning of each sentence.
Basic Routing Using Navigator.push and Navigator.pop

This is a straightforward method, where we use Navigator.push to navigate to a new screen


and Navigator.pop to return to the previous screen.

dart
Copy code
Navigator.push(
context,
MaterialPageRoute(builder: (context) => NewScreen()),
);

Navigator.pop(context);

Use Case: Ideal for one-time navigation without the need to manage a specific route name.

2. Named Routes

Named routes allow us to define routes globally, making it easier to manage and navigate to
various screens without specifying each screen’s details repeatedly.

Define Named Routes in MaterialApp


dart
Copy code
void main() {
runApp(
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
'/second': (context) => SecondScreen(),
},
),
);
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Home")),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/second');
},
child: Text("Go to Second Screen"),
),
),
);
}
}

Use Case: When you have multiple screens and want an organized way to reference each one.

3. Generated Routes (onGenerateRoute)

Generated routes are helpful for handling complex routing scenarios, especially when dynamic
arguments are passed to screens.

Example:
dart
Copy code
void main() {
runApp(MaterialApp(
onGenerateRoute: (settings) {
if (settings.name == '/second') {
final String message = settings.arguments as String;
return MaterialPageRoute(
builder: (context) => SecondScreen(message: message),
);
}
return null; // Default route
},
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
},
));
}

class HomeScreen extends StatelessWidget {


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

class SecondScreen extends StatelessWidget {


final String message;
SecondScreen({required this.message});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Second Screen")),
body: Center(
child: Text(message),
),
);
}
}

Use Case: When needing to pass data with routes or manage routes dynamically.

4. PushReplacement (Replacing the Current Screen)

Using pushReplacement, you replace the current screen with a new one, removing the
previous screen from the stack.

dart
Copy code
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => NewScreen()),
);

Use Case: Common in login flows, where after logging in, the login screen is replaced by the
home screen.

5. PushAndRemoveUntil (Clearing Navigation Stack)

Navigator.pushAndRemoveUntil pushes a new route and removes all previous routes from
the stack until a specific condition is met.

dart
Copy code
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => HomeScreen()),
(route) => false, // Removes all previous routes
);
Use Case: Useful for clearing the stack after actions like logout, where you want to take the
user back to the home screen without a back option.

6. Custom Route Transitions

Custom transitions allow you to modify the default animation between screens, such as adding
a fade or slide effect.

Example with Slide Transition


dart
Copy code
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) =>
NewScreen(),
transitionsBuilder: (context, animation, secondaryAnimation,
child) {
var begin = Offset(1.0, 0.0);
var end = Offset.zero;
var tween = Tween(begin: begin, end: end);
var offsetAnimation = animation.drive(tween);

return SlideTransition(
position: offsetAnimation,
child: child,
);
},
),
);

Use Case: Enhances UX with customized screen transitions.

Summary
Routing Method Use Case
Navigator.push/p Simple navigation to new screens and back.
op

Named Routes Multiple screens requiring organized


references.

onGenerateRoute Dynamic routes with arguments.

pushReplacement Replace the current screen (e.g., login flow).

pushAndRemoveUnt Clear all routes after navigation (e.g., logout).


il

Custom Transitions Enhance UX with animation effects.

Each approach suits different navigation needs, allowing Flutter developers to build responsive
and organized app flows.
In Flutter, you can use Radio, Checkbox, and Switch widgets to create radio buttons, check
buttons, and switch buttons, respectively. To manage their states, you can use setState() to
update the UI whenever the value of these widgets changes.

Here’s a step-by-step guide to implement each of them using setState().

1. Radio Button

The Radio widget in Flutter lets you create mutually exclusive options where only one can be
selected at a time. To use it, define a common variable to hold the selected value and update it
using setState().

dart
Copy code
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Radio, Checkbox, and Switch
Demo')),
body: MyRadioCheckSwitch(),
),
);
}
}

class MyRadioCheckSwitch extends StatefulWidget {


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

class _MyRadioCheckSwitchState extends State<MyRadioCheckSwitch> {


int _selectedRadio = 1;

void _handleRadioValueChange(int? value) {


setState(() {
_selectedRadio = value!;
});
}

bool _isChecked = false;


bool _isSwitched = false;

@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Radio Button
Text(
'Radio Button:',
style: TextStyle(fontSize: 18, fontWeight:
FontWeight.bold),
),
Row(
children: [
Radio(
value: 1,
groupValue: _selectedRadio,
onChanged: _handleRadioValueChange,
),
Text("Option 1"),
Radio(
value: 2,
groupValue: _selectedRadio,
onChanged: _handleRadioValueChange,
),
Text("Option 2"),
],
),

// Checkbox
SizedBox(height: 20),
Text(
'Checkbox:',
style: TextStyle(fontSize: 18, fontWeight:
FontWeight.bold),
),
Row(
children: [
Checkbox(
value: _isChecked,
onChanged: (bool? value) {
setState(() {
_isChecked = value!;
});
},
),
Text("Check this box"),
],
),

// Switch
SizedBox(height: 20),
Text(
'Switch:',
style: TextStyle(fontSize: 18, fontWeight:
FontWeight.bold),
),
Row(
children: [
Switch(
value: _isSwitched,
onChanged: (bool value) {
setState(() {
_isSwitched = value;
});
},
),
Text("Switch this on/off"),
],
),
],
),
);
}
}

Explanation of Code

1.​ Radio Button:


○​ A variable _selectedRadio holds the currently selected radio button value.
○​ Each Radio widget has a unique value and a common groupValue.
○​ The onChanged callback calls _handleRadioValueChange, which updates
_selectedRadio with setState().
2.​ Checkbox:
○​ _isChecked keeps track of whether the checkbox is checked.
○​ The onChanged callback updates _isChecked to the new value using
setState().
3.​ Switch:
○​ _isSwitched keeps track of the switch state.
○​ The onChanged callback toggles _isSwitched between true and false with
setState().
In Flutter, the Provider package is used for state management, making it easier to handle and
share state across the app. Here, I’ll explain Provider, show how to implement Switch,
Checkbox, and Radio buttons with it, and go over the differences between using Provider
and setState in Flutter.

Definition of Provider in Flutter

Provider is a wrapper around InheritedWidget that simplifies state management by


allowing data to be accessible anywhere in the widget tree. It promotes a reactive programming
approach and is especially useful for separating business logic from the UI.

Why use Provider over setState?

Provider setState

Designed for managing and sharing state across Useful for managing local state
different widgets in the widget tree. within a single widget.

Makes it easy to separate business logic from UI. Leads to tightly coupled UI and logic.

Can be optimized to reduce unnecessary rebuilds. Rebuilds the entire widget tree for
each change.

Works well for complex applications with shared data. Better for small applications with
simple state.

Switch, Checkbox, and Radio Button Example with Provider

Below is an example of implementing Switch, Checkbox, and Radio buttons using Provider
to manage state globally.

Step 1: Add provider Package


In your pubspec.yaml file, add:

yaml

Copy code

dependencies:

provider: ^6.0.0

Then run flutter pub get.

Step 2: Define the State Model

dart

Copy code

import 'package:flutter/foundation.dart';

class SelectionProvider with ChangeNotifier {

bool isSwitched = false;

bool isChecked = false;

String? selectedRadioValue;

void toggleSwitch() {

isSwitched = !isSwitched;

notifyListeners();

void toggleCheckbox(bool value) {


isChecked = value;

notifyListeners();

void selectRadio(String? value) {

selectedRadioValue = value;

notifyListeners();

Step 3: Main App with Provider

Wrap the MaterialApp with ChangeNotifierProvider to provide SelectionProvider


to the widget tree.

dart

Copy code

import 'package:flutter/material.dart';

import 'package:provider/provider.dart';

void main() {

runApp(

ChangeNotifierProvider(

create: (_) => SelectionProvider(),

child: MyApp(),
),

);

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(

home: Scaffold(

appBar: AppBar(title: Text('Provider Example')),

body: SelectionExample(),

),

);

Step 4: Implementing Widgets with Provider

dart

Copy code

class SelectionExample extends StatelessWidget {

@override

Widget build(BuildContext context) {

final selectionProvider = Provider.of<SelectionProvider>(context);


return Column(

mainAxisAlignment: MainAxisAlignment.center,

children: [

// Switch

Row(

mainAxisAlignment: MainAxisAlignment.center,

children: [

Text('Switch: '),

Switch(

value: selectionProvider.isSwitched,

onChanged: (_) => selectionProvider.toggleSwitch(),

),

],

),

// Checkbox

Row(

mainAxisAlignment: MainAxisAlignment.center,

children: [

Text('Checkbox: '),

Checkbox(

value: selectionProvider.isChecked,
onChanged: selectionProvider.toggleCheckbox,

),

],

),

// Radio Buttons

Row(

mainAxisAlignment: MainAxisAlignment.center,

children: [

Text('Radio 1'),

Radio<String>(

value: 'Option 1',

groupValue: selectionProvider.selectedRadioValue,

onChanged: selectionProvider.selectRadio,

),

Text('Radio 2'),

Radio<String>(

value: 'Option 2',

groupValue: selectionProvider.selectedRadioValue,

onChanged: selectionProvider.selectRadio,

),

],

),
// Display the selections

Padding(

padding: const EdgeInsets.all(16.0),

child: Column(

children: [

Text('Switch is ${selectionProvider.isSwitched ? 'ON' :


'OFF'}'),

Text('Checkbox is ${selectionProvider.isChecked ?
'Checked' : 'Unchecked'}'),

Text('Selected Radio:
${selectionProvider.selectedRadioValue ?? 'None'}'),

],

),

),

],

);

Explanation of Code:

1.​ SelectionProvider Class: This ChangeNotifier class holds the state for Switch,
Checkbox, and Radio button. Each state-changing method calls
notifyListeners() to update any listening widgets.
2.​ ChangeNotifierProvider: Wraps the MyApp widget to provide the
SelectionProvider instance throughout the widget tree.
3.​ Switch, Checkbox, Radio Widgets: These widgets update state using methods in
SelectionProvider, allowing for centralized state management.

By using Provider, you can efficiently manage app-wide state updates without manually
passing data down through constructors, making your code cleaner and more scalable.

Determinate Progress

For determinate progress, you specify a value between 0.0 (0%) and 1.0 (100%).

Here's an example that increments the progress over time:

dart

Copy code

class DeterminateProgressExample extends StatefulWidget {

@override

_DeterminateProgressExampleState createState() =>


_DeterminateProgressExampleState();

class _DeterminateProgressExampleState extends


State<DeterminateProgressExample> {

double _progressValue = 0.0;


@override

void initState() {

super.initState();

_startProgress();

void _startProgress() {

Future.delayed(Duration(seconds: 1), () {

setState(() {

if (_progressValue < 1.0) {

_progressValue += 0.1; // Increment progress

_startProgress(); // Continue updating progress

});

});

@override

Widget build(BuildContext context) {

return Center(

child: Padding(

padding: const EdgeInsets.all(16.0),

child: Column(
mainAxisAlignment: MainAxisAlignment.center,

children: [

Text('Determinate Progress', style: TextStyle(fontSize:


18)),

SizedBox(height: 20),

LinearProgressIndicator(value: _progressValue), //
Determinate progress bar

SizedBox(height: 10),

Text('${(_progressValue * 100).round()}%'),

],

),

),

);

Explanation of Code

●​ Indeterminate Progress: The LinearProgressIndicator() without a value


animates continuously to show that progress is ongoing but undefined.
●​ Determinate Progress:
○​ _progressValue holds the current progress level.
○​ _startProgress() method increases _progressValue every second by
10%.
○​ LinearProgressIndicator(value: _progressValue) uses
_progressValue to show progress visually, and we display the percentage
alongside.
Application
// screen 1

class ImagesPath {
static String kOnboarding1 = 'assets/images/onboarding1.png';
static String kOnboarding2 = 'assets/images/onBoarding2.png';
static String kOnboarding3 = 'assets/images/onBoarding3.png';
}

class AppColor {
static Color kPrimary = const Color(0XFF1460F2);
static Color kWhite = const Color(0XFFFFFFFF);
static Color kOnBoardingColor = const Color(0XFFFEFEFE);
static Color kGrayscale40 = const Color(0XFFAEAEB2);
static Color kGrayscaleDark100 = const Color(0XFF1C1C1E);
}

class OnBoardingScreen extends StatefulWidget {


const OnBoardingScreen({super.key});

@override
State<OnBoardingScreen> createState() => _OnBoardingScreenState();
}

class _OnBoardingScreenState extends State<OnBoardingScreen> {


final PageController _pageController1 = PageController(initialPage: 0);
final PageController _pageController2 = PageController(initialPage: 0);
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.kOnBoardingColor,
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
Container(
height: 10,
width: 10,
margin: const EdgeInsets.all(30),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColor.kPrimary,
),
),
const SizedBox(height: 16),
Expanded(
flex: 5,
child: PageView.builder(
scrollDirection: Axis.horizontal,
itemCount: onBoardinglist.length,
physics: const BouncingScrollPhysics(),
controller: _pageController1 ?? _pageController2,
onPageChanged: (index) {
setState(() {
_currentIndex = index;
});
},
itemBuilder: (context, index) {
return OnBoardingCard(
onBoardingModel: onBoardinglist[index],
);
}),
),
const SizedBox(height: 40),
Center(
child: DotsIndicator(
dotsCount: onBoardinglist.length,
position: _currentIndex,
decorator: DotsDecorator(
color: AppColor.kPrimary.withOpacity(0.4),
size: const Size.square(8.0),
activeSize: const Size(20.0, 8.0),
activeShape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
activeColor: AppColor.kPrimary,
),
),
),
const SizedBox(height: 37),
Expanded(
flex: 2,
child: PageView.builder(
scrollDirection: Axis.horizontal,
itemCount: onBoardinglist.length,
physics: const BouncingScrollPhysics(),
controller: _pageController2 ?? _pageController1,
onPageChanged: (index) {
setState(() {
_currentIndex = index;
});
},
itemBuilder: (context, index) {
return OnboardingTextCard(
onBoardingModel: onBoardinglist[index],
);
}),
),
const SizedBox(height: 30),
Padding(
padding: const EdgeInsets.only(left: 25, right: 23, bottom: 36),
child: PrimaryButton(
elevation: 0,
onTap: () {
if (_currentIndex == onBoardinglist.length - 1) {
} else {
_pageController1.nextPage(
duration: const Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn,
);
_pageController2.nextPage(
duration: const Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn,
);
}
},
text: _currentIndex == onBoardinglist.length - 1
? 'Get Started'
: 'Next',
bgColor: AppColor.kPrimary,
borderRadius: 20,
height: 46,
width: 327,
textColor: AppColor.kWhite,
),
),
],
),
);
}
}

class PrimaryButton extends StatefulWidget {


final VoidCallback onTap;
final String text;
final double? width;
final double? height;
final double? borderRadius, elevation;
final double? fontSize;
final IconData? iconData;
final Color? textColor, bgColor;
const PrimaryButton(
{Key? key,
required this.onTap,
required this.text,
this.width,
this.height,
this.elevation = 5,
this.borderRadius,
this.fontSize,
required this.textColor,
required this.bgColor,
this.iconData})
: super(key: key);

@override
State<PrimaryButton> createState() => _PrimaryButtonState();
}

class _PrimaryButtonState extends State<PrimaryButton>


with SingleTickerProviderStateMixin {
late AnimationController _controller;
final Duration _animationDuration = const Duration(milliseconds: 300);
final Tween<double> _tween = Tween<double>(begin: 1.0, end: 0.95);
@override
void initState() {
_controller = AnimationController(
vsync: this,
duration: _animationDuration,
)..addListener(() {
setState(() {});
});
super.initState();
}

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

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
_controller.forward().then((_) {
_controller.reverse();
});
widget.onTap();
},
child: ScaleTransition(
scale: _tween.animate(
CurvedAnimation(
parent: _controller,
curve: Curves.easeOut,
reverseCurve: Curves.easeIn,
),
),
child: Card(
elevation: widget.elevation ?? 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(widget.borderRadius!),
),
child: Container(
height: widget.height ?? 55,
alignment: Alignment.center,
width: widget.width ?? double.maxFinite,
decoration: BoxDecoration(
color: widget.bgColor,
borderRadius: BorderRadius.circular(widget.borderRadius!),
),
child: Text(
widget.text,
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: widget.textColor,
fontWeight: FontWeight.w500,
fontSize: widget.fontSize),
),
),
),
),
);
}
}

class OnBoardingCard extends StatefulWidget {


OnBoarding onBoardingModel;
OnBoardingCard({
super.key,
required this.onBoardingModel,
});

@override
State<OnBoardingCard> createState() => _OnBoardingCardState();
}

class _OnBoardingCardState extends State<OnBoardingCard> {


@override
Widget build(BuildContext context) {
return Image.asset(
widget.onBoardingModel.image,
height: 300,
width: double.maxFinite,
fit: BoxFit.fitWidth,
);
}
}

class OnboardingTextCard extends StatelessWidget {


final OnBoarding onBoardingModel;
const OnboardingTextCard({required this.onBoardingModel, super.key});

@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 23),
child: Column(
children: [
Text(
onBoardingModel.title,
textAlign: TextAlign.center,
style: GoogleFonts.plusJakartaSans(
fontSize: 24,
fontWeight: FontWeight.bold,
color: AppColor.kGrayscaleDark100,
).copyWith(fontSize: 24),
),
const SizedBox(
height: 16,
),
Text(
onBoardingModel.description,
textAlign: TextAlign.center,
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(color: AppColor.kGrayscale40, fontSize: 14),
),
],
),
);
}
}

class OnBoarding {
String title;
String description;
String image;

OnBoarding({
required this.title,
required this.description,
required this.image,
});
}

List<OnBoarding> onBoardinglist = [
OnBoarding(
title: ' Can be accessed from anywhere at any time',
image: ImagesPath.kOnboarding1,
description:
'The essential language learning tools and resources you need to seamlessly transition into
mastering a new language',
),
OnBoarding(
title: 'Offers a dynamic and interactive experience',
image: ImagesPath.kOnboarding2,
description:
'Engaging features including test, story telling, and conversations that motivate and
inspire language learners to unlock their full potential'),
OnBoarding(
title: "Experience the Premium Features with Our App",
image: ImagesPath.kOnboarding3,
description:
'Updated TalkGpt with premium materials and a dedicated following, providing language
learners with immersive content for effective learning'),
];

//screen 2

class ImagesPath {
static String kGoogleIcon = 'assets/images/googleSymbol.png';
}

class AppColor {
static Color kPrimary = const Color(0XFF1460F2);
static Color kWhite = const Color(0XFFFFFFFF);
static Color kBackground = const Color(0XFFFAFAFA);
static Color kGrayscaleDark100 = const Color(0XFF1C1C1E);
static Color kLine = const Color(0XFFEBEBEB);
static Color kBackground2 = const Color(0XFFF6F6F6);
static Color kGrayscale40 = const Color(0XFFAEAEB2);
}

class SignInScreen extends StatelessWidget {


SignInScreen({super.key});
TextEditingController emailC = TextEditingController();
TextEditingController passwordC = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.kWhite,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: SizedBox(
width: 327,
child: Column(children: [

👋
Text(
'Hi, Welcome Back! ',
textAlign: TextAlign.center,
style: GoogleFonts.plusJakartaSans(
fontSize: 20,
fontWeight: FontWeight.w600,
).copyWith(color: AppColor.kGrayscaleDark100, fontSize: 20),
),
const SizedBox(height: 8),
Text(
'We happy to see you. Sign In to your account',
textAlign: TextAlign.center,
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(color: AppColor.kGrayscale40, fontSize: 14),
),
const SizedBox(height: 36),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Email',
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: AppColor.kGrayscaleDark100,
fontWeight: FontWeight.w600,
fontSize: 14),
),
const SizedBox(
height: 8,
),
PrimaryTextFormField(
borderRadius: BorderRadius.circular(24),
hintText: '[email protected]',
controller: emailC,
width: 327,
height: 52)
],
),
const SizedBox(
height: 16,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Password',
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: AppColor.kGrayscaleDark100,
fontWeight: FontWeight.w600,
fontSize: 14),
),
const SizedBox(height: 8),
PasswordTextField(
borderRadius: BorderRadius.circular(24),
hintText: 'Password',
controller: passwordC,
width: 327,
height: 52)
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
PrimaryTextButton(
onPressed: () {},
title: 'Forgot Password?',
textStyle: const TextStyle(),
)
],
),
const SizedBox(height: 32),
Column(
children: [
PrimaryButton(
elevation: 0,
onTap: () {},
text: 'LogIn',
bgColor: AppColor.kPrimary,
borderRadius: 20,
height: 46,
width: 327,
textColor: AppColor.kWhite,
fontSize: 14,
),
const SizedBox(
height: 24,
),
Padding(
padding: const EdgeInsets.only(left: 4),
child: CustomRichText(
title: 'Don’t have an account?',
subtitle: ' Create here',
onTab: () {},
subtitleTextStyle: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: AppColor.kPrimary,
fontWeight: FontWeight.w600,
fontSize: 14),
),
)
],
),
const SizedBox(height: 32),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 45),
child: Column(
children: [
const DividerRow(title: 'Or Sign In with'),
const SizedBox(height: 24),
SecondaryButton(
height: 56,
textColor: AppColor.kGrayscaleDark100,
width: 280,
onTap: () {},
borderRadius: 24,
bgColor: AppColor.kBackground.withOpacity(0.3),
text: 'Continue with Google',
icons: ImagesPath.kGoogleIcon),
],
),
),
const SizedBox(height: 50),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 40),
child: TermsAndPrivacyText(
title1: ' By signing up you agree to our',
title2: ' Terms ',
title3: ' and',
title4: ' Conditions of Use',
),
),
const SizedBox(height: 24),
]),
),
),
),
);
}
}

class TermsAndPrivacyText extends StatelessWidget {


const TermsAndPrivacyText(
{super.key,
required this.title1,
required this.title2,
required this.title3,
this.color2,
required this.title4});
final Color? color2;
final String title1, title2, title3, title4;
@override
Widget build(BuildContext context) {
return RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: AppColor.kGrayscale40,
fontWeight: FontWeight.w500,
fontSize: 14),
children: [
TextSpan(
text: title1,
),
TextSpan(
text: title2,
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: color2 ?? AppColor.kGrayscaleDark100,
fontWeight: FontWeight.w500,
fontSize: 14),
),
TextSpan(
text: title3,
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: AppColor.kGrayscale40,
fontWeight: FontWeight.w500,
fontSize: 14),
),
TextSpan(
text: title4,
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: AppColor.kGrayscaleDark100,
fontWeight: FontWeight.w500,
fontSize: 14),
),
],
),
);
}
}

class SecondaryButton extends StatefulWidget {


final VoidCallback onTap;
final String text, icons;
final double width;
final double height;
final double borderRadius;
final double? fontSize;
final Color textColor, bgColor;
const SecondaryButton(
{super.key,
required this.onTap,
required this.text,
required this.width,
required this.height,
required this.icons,
required this.borderRadius,
this.fontSize,
required this.textColor,
required this.bgColor});

@override
State<SecondaryButton> createState() => _SecondaryButtonState();
}

class _SecondaryButtonState extends State<SecondaryButton>


with SingleTickerProviderStateMixin {
late AnimationController _controller;
final Duration _animationDuration = const Duration(milliseconds: 300);
final Tween<double> _tween = Tween<double>(begin: 1.0, end: 0.95);
@override
void initState() {
_controller = AnimationController(
vsync: this,
duration: _animationDuration,
)..addListener(() {
setState(() {});
});
super.initState();
}

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

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
_controller.forward().then((_) {
_controller.reverse();
});
widget.onTap();
},
child: ScaleTransition(
scale: _tween.animate(
CurvedAnimation(
parent: _controller,
curve: Curves.easeOut,
reverseCurve: Curves.easeIn,
),
),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 17),
height: widget.height,
alignment: Alignment.center,
width: widget.width,
decoration: BoxDecoration(
color: widget.bgColor,
border: Border.all(color: AppColor.kLine),
borderRadius: BorderRadius.circular(widget.borderRadius),
),
child: Row(
children: [
Image.asset(widget.icons, width: 23.85, height: 23.04),
const SizedBox(width: 12),
Text(widget.text,
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: widget.textColor,
fontSize: 14,
fontWeight: FontWeight.w600,
)),
],
),
),
),
);
}
}

class DividerRow extends StatelessWidget {


final String title;
const DividerRow({
required this.title,
super.key,
});
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
flex: 2,
child: Divider(
color: AppColor.kLine,
)),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 6),
child: Text(
title,
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: AppColor.kGrayscale40,
fontWeight: FontWeight.w500,
fontSize: 14),
),
),
Expanded(
flex: 2,
child: Divider(
color: AppColor.kLine,
))
],
);
}
}

class CustomRichText extends StatelessWidget {


const CustomRichText({
super.key,
required this.title,
required this.subtitle,
required this.onTab,
required this.subtitleTextStyle,
});
final String title, subtitle;
final TextStyle subtitleTextStyle;
final VoidCallback onTab;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTab,
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: title,
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: AppColor.kGrayscale40,
fontWeight: FontWeight.w600,
fontSize: 14),
children: <TextSpan>[
TextSpan(
text: subtitle,
style: subtitleTextStyle,
),
],
),
),
);
}
}

class PrimaryButton extends StatefulWidget {


final VoidCallback onTap;
final String text;
final double? width;
final double? height;
final double? borderRadius, elevation;
final double? fontSize;
final IconData? iconData;
final Color? textColor, bgColor;
const PrimaryButton(
{Key? key,
required this.onTap,
required this.text,
this.width,
this.height,
this.elevation = 5,
this.borderRadius,
this.fontSize,
required this.textColor,
required this.bgColor,
this.iconData})
: super(key: key);

@override
State<PrimaryButton> createState() => _PrimaryButtonState();
}

class _PrimaryButtonState extends State<PrimaryButton>


with SingleTickerProviderStateMixin {
late AnimationController _controller;
final Duration _animationDuration = const Duration(milliseconds: 300);
final Tween<double> _tween = Tween<double>(begin: 1.0, end: 0.95);
@override
void initState() {
_controller = AnimationController(
vsync: this,
duration: _animationDuration,
)..addListener(() {
setState(() {});
});
super.initState();
}

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

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
_controller.forward().then((_) {
_controller.reverse();
});
widget.onTap();
},
child: ScaleTransition(
scale: _tween.animate(
CurvedAnimation(
parent: _controller,
curve: Curves.easeOut,
reverseCurve: Curves.easeIn,
),
),
child: Card(
elevation: widget.elevation ?? 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(widget.borderRadius!),
),
child: Container(
height: widget.height ?? 55,
alignment: Alignment.center,
width: widget.width ?? double.maxFinite,
decoration: BoxDecoration(
color: widget.bgColor,
borderRadius: BorderRadius.circular(widget.borderRadius!),
),
child: Text(
widget.text,
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: widget.textColor,
fontWeight: FontWeight.w500,
fontSize: widget.fontSize),
),
),
),
),
);
}
}

class PrimaryTextButton extends StatelessWidget {


const PrimaryTextButton(
{super.key,
required this.onPressed,
required this.title,
required this.textStyle});
final Function() onPressed;
final String title;
final TextStyle textStyle;

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: Text(
title,
style: textStyle,
),
);
}
}

class PasswordTextField extends StatefulWidget {


final String hintText;
final double width, height;
final TextEditingController controller;
final BorderRadiusGeometry borderRadius;
const PasswordTextField(
{Key? key,
required this.hintText,
required this.height,
required this.controller,
required this.width,
required this.borderRadius})
: super(key: key);
@override
_PasswordTextFieldState createState() => _PasswordTextFieldState();
}

class _PasswordTextFieldState extends State<PasswordTextField> {


bool _obscureText = false;
@override
Widget build(BuildContext context) {
InputBorder enabledBorder = InputBorder.none;
InputBorder focusedErrorBorder = InputBorder.none;
InputBorder errorBorder = InputBorder.none;
InputBorder focusedBorder = InputBorder.none;

return Container(
width: widget.width,
height: widget.height,
decoration: BoxDecoration(
borderRadius: widget.borderRadius,
color: AppColor.kBackground2,
border: Border.all(color: AppColor.kLine)),
child: TextFormField(
obscureText: _obscureText,
controller: widget.controller,
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: AppColor.kGrayscaleDark100,
),
decoration: InputDecoration(
filled: true,
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
suffixIcon: IconButton(
icon: Icon(
_obscureText
? Icons.visibility_outlined
: Icons.visibility_off_outlined,
color: AppColor.kGrayscaleDark100,
size: 17,
),
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
hintText: widget.hintText,
hintStyle: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: AppColor.kGrayscale40,
fontWeight: FontWeight.w600,
fontSize: 14),
enabledBorder: enabledBorder,
focusedBorder: focusedBorder,
errorBorder: errorBorder,
focusedErrorBorder: focusedErrorBorder,
)),
);
}
}

class PrimaryTextFormField extends StatelessWidget {


PrimaryTextFormField(
{super.key,
required this.hintText,
this.keyboardType,
required this.controller,
required this.width,
required this.height,
this.hintTextColor,
this.onChanged,
this.onTapOutside,
this.prefixIcon,
this.prefixIconColor,
this.inputFormatters,
this.maxLines,
this.borderRadius});
final BorderRadiusGeometry? borderRadius;

final String hintText;

final List<TextInputFormatter>? inputFormatters;


Widget? prefixIcon;
Function(PointerDownEvent)? onTapOutside;
final Function(String)? onChanged;
final double width, height;
TextEditingController controller;
final Color? hintTextColor, prefixIconColor;
final TextInputType? keyboardType;
final int? maxLines;
@override
Widget build(BuildContext context) {
InputBorder enabledBorder = InputBorder.none;
InputBorder focusedErrorBorder = InputBorder.none;
InputBorder errorBorder = InputBorder.none;
InputBorder focusedBorder = InputBorder.none;

return Container(
width: width,
height: height,
decoration: BoxDecoration(
borderRadius: borderRadius,
color: AppColor.kBackground,
border: Border.all(color: AppColor.kLine)),
child: TextFormField(
controller: controller,
maxLines: maxLines,
keyboardType: keyboardType,
style: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: AppColor.kGrayscaleDark100,
),
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
filled: true,
hintText: hintText,
hintStyle: GoogleFonts.plusJakartaSans(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColor.kWhite)
.copyWith(
color: AppColor.kGrayscaleDark100,
fontWeight: FontWeight.w600,
fontSize: 14),
prefixIcon: prefixIcon,
prefixIconColor: prefixIconColor,
enabledBorder: enabledBorder,
focusedBorder: focusedBorder,
errorBorder: errorBorder,
focusedErrorBorder: focusedErrorBorder,
),
onChanged: onChanged,
inputFormatters: inputFormatters,
onTapOutside: onTapOutside,
),
);
}
}
class PlanetCard {
final String title;
final String image;

PlanetCard({required this.title, required this.image});


}

class SpaceHomeScreen extends StatelessWidget {


SpaceHomeScreen({Key? key}) : super(key: key);
final planetCards = [
PlanetCard(title: "Neptune", image:
"https://firebasestorage.googleapis.com/v0/b/flutterbricks-public.appspot.com/o/Planets%2Fplan
et1.png?alt=media&token=b275a4f8-1abb-4e69-b218-b796cf144352"),
PlanetCard(title: "Venus", image:
"https://firebasestorage.googleapis.com/v0/b/flutterbricks-public.appspot.com/o/Planets%2Fplan
et2.png?alt=media&token=6dc4d3f4-09d8-47fc-8639-b19eba6e3ed5"),
PlanetCard(title: "Orbit", image:
"https://firebasestorage.googleapis.com/v0/b/flutterbricks-public.appspot.com/o/Planets%2Fplan
et3.png?alt=media&token=497fbf32-30c7-4ce5-ae0a-c387d517aa1a"),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Stack(
children: [
const Background(),
SingleChildScrollView(
child: Column(
children: [
const IntroCard(),
SizedBox(
width: MediaQuery.of(context).size.width,
height: 335,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: planetCards.length,
itemBuilder: (context, index) {
return PlanetCardWidget2(
planetCard: planetCards[index]);
}),
),
InformationCard(
planetCard: PlanetCard(
image:
"https://firebasestorage.googleapis.com/v0/b/flutterbricks-public.appspot.com/o/Planets%2Fsola
r.png?alt=media&token=50992182-d228-484a-b6a7-ffab59023027", title: "Solar System"),
),
InformationCard(
planetCard: PlanetCard(
image:
"https://firebasestorage.googleapis.com/v0/b/flutterbricks-public.appspot.com/o/Planets%2Fplan
et3.png?alt=media&token=497fbf32-30c7-4ce5-ae0a-c387d517aa1a", title: "Mercury"),
),
],
),
)
],
),
),
);
}
}

class Background extends StatelessWidget {


const Background({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue[50]!, Colors.blueAccent, Colors.purple[300]!],
begin: Alignment.topCenter,
end: Alignment.bottomCenter),
),
);
}
}

class SearchInput extends StatelessWidget {


final TextEditingController textController;
final String hintText;
const SearchInput(
{required this.textController, required this.hintText, Key? key})
: super(key: key);

@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
offset: const Offset(0, 26),
blurRadius: 50,
spreadRadius: 0,
color: Colors.white.withOpacity(.25)),
]),
child: TextField(
controller: textController,
onChanged: (value) {
//Do something wi
},
decoration: InputDecoration(
prefixIcon: const Icon(
Icons.search,
color: Color(0xff4338CA),
),
filled: true,
fillColor: Colors.blue[50],
hintText: hintText,
hintStyle:
const TextStyle(color: Colors.grey, fontWeight: FontWeight.w300),
contentPadding:
const EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(50.0)),
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.white, width: 1.0),
borderRadius: BorderRadius.all(Radius.circular(50.0)),
),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.white, width: 2.0),
borderRadius: BorderRadius.all(Radius.circular(50.0)),
),
),
),
);
}
}

class IntroCard extends StatelessWidget {


const IntroCard({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(top: 50.0, left: 15.0, right: 15.0),
height: 190,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.0),
boxShadow: [
BoxShadow(
color: Colors.indigo[800]!.withOpacity(.15),
offset: const Offset(0, 10),
blurRadius: 0,
spreadRadius: 0,
)
],
gradient: const RadialGradient(
colors: [Color(0xff0E5C9E), Color(0xff031965)],
focal: Alignment.topCenter,
radius: .85,
)),
padding: const EdgeInsets.all(25.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text("Let's Explore",
style: TextStyle(
color: Colors.white,
fontSize: 32,
height: 1.25,
fontFamily: "BigBottom",
fontWeight: FontWeight.bold)),
Text("Travel the galaxy",
style: TextStyle(
color: Colors.white.withOpacity(.75),
fontSize: 14,
)),
],
),
Image.network(

"https://firebasestorage.googleapis.com/v0/b/flutterbricks-public.appspot.com/o/Planets%2Fbox-
1.png?alt=media&token=4b0259b3-8bc3-4ab7-9157-a61baacd3846",
width: 50,
)
],
),
const SizedBox(height: 15.0),
SearchInput(
textController: TextEditingController(),
hintText: "Search",
),
const SizedBox(height: 5.0),
],
),
);
}
}

class PlanetCardWidget2 extends StatelessWidget {


final PlanetCard planetCard;

const PlanetCardWidget2({required this.planetCard, Key? key})


: super(key: key);

@override
Widget build(BuildContext context) {
return Center(
child: GestureDetector(
onTap: () => Navigator.push(
context,
PageRouteBuilder(
transitionDuration: const Duration(milliseconds: 700),
pageBuilder: (_, __, ___) => PlanetHero(
planetCard: planetCard,
))),
child: Container(
margin: const EdgeInsets.only(top: 15.0, left: 15.0, right: 15.0),
width: 250,
height: 290,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(.05),
offset: const Offset(0, 10),
blurRadius: 0,
spreadRadius: 0,
)
],
gradient: const RadialGradient(
colors: [Color(0xff0E5C9E), Color(0xff031965)],
focal: Alignment.topCenter,
radius: .85,
)),
padding: const EdgeInsets.all(15.0),
child: Column(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100.0),
boxShadow: [
BoxShadow(
color: Colors.blue.withOpacity(.3),
offset: const Offset(0, 5),
blurRadius: 25,
spreadRadius: 0,
)
],
),
child: Hero(
tag: planetCard.image,
child: Image.network(
planetCard.image,
width: 170,
),
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(planetCard.title,
style: const TextStyle(
color: Colors.white,
fontFamily: "BigBottom",
fontSize: 22)),
Text("About planet",
style: TextStyle(
color: Colors.white.withOpacity(.45),
fontSize: 14)),
],
),
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100.0),
gradient: const LinearGradient(
colors: [Colors.yellow, Colors.orange],
begin: Alignment.topCenter,
end: Alignment.bottomCenter),
),
child: const Icon(
Icons.keyboard_arrow_right,
color: Colors.white,
size: 30,
)),
],
),
),
],
),
),
),
);
}
}

class InformationCard extends StatelessWidget {


final PlanetCard planetCard;
const InformationCard({required this.planetCard, Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(top: 25.0, left: 15.0, right: 15.0),
height: 155,
padding: const EdgeInsets.all(25.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(.1),
offset: const Offset(0, 10),
blurRadius: 0,
spreadRadius: 0,
)
],
gradient: const RadialGradient(
colors: [Color(0xff0E5C9E), Color(0xff031965)],
focal: Alignment.topCenter,
radius: 1.25,
)),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
planetCard.image,
width: 50,
),
const SizedBox(
width: 15,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
planetCard.title,
style: const TextStyle(
fontFamily: "BigBottom",
color: Colors.white,
fontSize: 22),
),
],
),
const SizedBox(height: 10),
Text(
"""Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas vel sint commodi!""",
maxLines: 3,
style: TextStyle(
color: Colors.white.withOpacity(.75), fontSize: 14),
),
],
),
),
],
),
);
}
}

class PlanetHero extends StatefulWidget {


final PlanetCard planetCard;
const PlanetHero({required this.planetCard, Key? key}) : super(key: key);

@override
State<PlanetHero> createState() => _PlanetHeroState();
}

class _PlanetHeroState extends State<PlanetHero> {


@override
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Stack(
alignment: Alignment.topCenter,
children: [
const Background(),
SingleChildScrollView(
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 49.0),
child: PlanetCardWidget(
planetCard: PlanetCard(
title: "Venus", image: widget.planetCard.image),
),
),
Container(
margin: const EdgeInsets.only(top: 25.0, left: 0.0, right: 0.0),
height: 255,
padding: const EdgeInsets.all(25.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(.1),
offset: const Offset(0, 10),
blurRadius: 0,
spreadRadius: 0,
)
],
gradient: const RadialGradient(
colors: [Color(0xff0E5C9E), Color(0xff031965)],
focal: Alignment.topCenter,
radius: .85,
)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
"Neptune Travel",
style: TextStyle(
fontFamily: "BigBottom",
color: Colors.white,
fontSize: 26),
),
Container(
width: 75,
height: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100.0),
gradient: const LinearGradient(
colors: [Colors.yellow, Colors.orange],
begin: Alignment.topCenter,
end: Alignment.bottomCenter),
),
child: const Center(
child: Text(
"More",
style: TextStyle(color: Colors.white),
)),
),
],
),
const SizedBox(height: 10),
Text(
"""Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime mollitia,
molestiae quas vel sint commodi repudi conseqr voluptatum laborum numquam blanditiis harum
quisquam eius sed odit!""",
style: TextStyle(
color: Colors.white.withOpacity(.75),
fontSize: 14),
),
const SizedBox(height: 15),
Expanded(
child: Container(
width: double.infinity,
height: 75,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.0),
color: Colors.blue[50],
),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Row(
children: [
CircleAvatar(
backgroundColor: Color(0xff031965),
radius: 25,
child: Icon(
Icons.directions,
color: Colors.white,
),
),
SizedBox(width: 10),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text("Neptune - Earth"),
Text(
"2.76 billion mi",
style: TextStyle(
color: Color(0xff031965),
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
],
)
],
),
),
),
)
],
),
),
],
),
),
],
),
),
);
}
}

class PlanetCardWidget extends StatefulWidget {


final PlanetCard planetCard;

const PlanetCardWidget({required this.planetCard, Key? key})


: super(key: key);

@override
State<PlanetCardWidget> createState() => _PlanetCardWidgetState();
}

class _PlanetCardWidgetState extends State<PlanetCardWidget> {


@override
Widget build(BuildContext context) {
return SizedBox(
height: 515,
child: Stack(
alignment: Alignment.topCenter,
children: [
Container(
margin: const EdgeInsets.only(top: 70.0, left: 0.0, right: 0.0),
width: 400,
height: 470,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(.1),
offset: const Offset(0, 10),
blurRadius: 0,
spreadRadius: 0,
)
],
gradient: const RadialGradient(
colors: [Color(0xff0E5C9E), Color(0xff031965)],
focal: Alignment.topCenter,
radius: .85,
)),
),
Container(
width: 370,
height: 265,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.5),
gradient: LinearGradient(colors: [
Colors.indigo[700]!,
Colors.blue,
Colors.purple[300]!
], begin: Alignment.topLeft, end: Alignment.bottomRight)),
),
Padding(
padding: const EdgeInsets.only(left: 25.0, right: 25.0, top: 20.0),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: const Icon(
Icons.keyboard_arrow_left,
color: Colors.white,
),
onPressed: () {
Navigator.pop(context);
},
),
const Text("Neptune",
style: TextStyle(
color: Colors.white,
fontSize: 22,
height: 1.25,
fontFamily: "BigBottom",
fontWeight: FontWeight.bold)),
IconButton(
icon: const Icon(
Icons.notification_add_sharp,
color: Colors.white,
),
onPressed: () {},
),
],
),
const SizedBox(
height: 10,
),
Container(
height: 285,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100.0),
boxShadow: [
BoxShadow(
color: Colors.blue.withOpacity(.3),
offset: const Offset(0, 5),
blurRadius: 25,
spreadRadius: 0,
)
],
),
child: Hero(
tag: widget.planetCard.image,
child: Image.network(
widget.planetCard.image,
width: 250,
),
),
),
const SizedBox(
height: 15,
),
const Text(
"Solar systems",
style: TextStyle(color: Colors.white, fontSize: 16),
),
const SizedBox(
height: 15,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Image.network(

"https://firebasestorage.googleapis.com/v0/b/flutterbricks-public.appspot.com/o/Planets%2Fplan
et1.png?alt=media&token=b275a4f8-1abb-4e69-b218-b796cf144352",
width: 63,
),
Image.network(

"https://firebasestorage.googleapis.com/v0/b/flutterbricks-public.appspot.com/o/Planets%2Fplan
et2.png?alt=media&token=6dc4d3f4-09d8-47fc-8639-b19eba6e3ed5",
width: 63,
),
Image.network(

"https://firebasestorage.googleapis.com/v0/b/flutterbricks-public.appspot.com/o/Planets%2Fplan
et3.png?alt=media&token=497fbf32-30c7-4ce5-ae0a-c387d517aa1a",
width: 63,
),
Image.network(

"https://firebasestorage.googleapis.com/v0/b/flutterbricks-public.appspot.com/o/Planets%2Fsola
r.png?alt=media&token=50992182-d228-484a-b6a7-ffab59023027",
width: 63,
),
],
)
],
),
),
],
),
);
}
}
Backend FIrebase
Connecting a Flutter app to Firebase involves configuring Firebase in your project and adding
the necessary Firebase packages. Below are the steps to integrate Firebase with a Flutter app,
using Firebase Authentication as an example.

Step-by-Step Guide to Connect Firebase to Flutter

Step 1: Create a Firebase Project

1.​ Go to the Firebase Console.


2.​ Click on "Add Project".
3.​ Enter your project name and click Continue.
4.​ Disable Google Analytics (optional) and click Create Project.

Step 2: Add an App to Firebase

1.​ In the Firebase Console, select your project.


2.​ Click on Add App and select Android or iOS:
○​ For Android:
■​ Enter your app's package name (e.g., com.example.myapp).
■​ Download the google-services.json file.
■​ Place the google-services.json file in the android/app directory.
○​ For iOS:
■​ Enter your iOS app's bundle ID.
■​ Download the GoogleService-Info.plist file.
■​ Place the GoogleService-Info.plist file in the ios/Runner
directory.
■​ Modify the ios/Runner/Info.plist file with required Firebase
configurations (refer to Firebase documentation for iOS).

Step 3: Configure Your Flutter Project


Add Firebase Plugins: Open the pubspec.yaml file and add the required dependencies:​
yaml​
Copy code​
dependencies:
flutter:
sdk: flutter
firebase_core: ^latest_version
firebase_auth: ^latest_version
cloud_firestore: ^latest_version

1.​ Replace ^latest_version with the current plugin versions from pub.dev.

Run the command:​


bash​
Copy code​
flutter pub get

2.​
3.​ Update Android Configuration:

Open android/build.gradle and add:​


gradle​
Copy code​
dependencies {
classpath 'com.google.gms:google-services:4.3.15' // Add this line
}

○​

Open android/app/build.gradle and add:​


gradle​
Copy code​
apply plugin: 'com.google.gms.google-services' // Add this line at the
bottom

○​
4.​ Update iOS Configuration:

Open ios/Podfile and ensure platform is set:​


ruby​
Copy code​
platform :ios, '12.0'

○​

Run:​
bash​
Copy code​
cd ios
pod install
○​

Step 4: Initialize Firebase in Flutter


Open the main.dart file and initialize Firebase:​
dart​
Copy code​
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';

void main() async {


WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Firebase Example',
home: HomePage(),
);
}
}

class HomePage extends StatelessWidget {


@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Firebase Setup")),
body: Center(child: Text("Firebase Connected!")),
);
}
}

1.​
Step 5: Add Firebase Authentication (Optional)

Here’s how to add Firebase Authentication:

1.​ Sign-in Method:


○​ Go to Firebase Console > Authentication > Sign-in Method.
○​ Enable Email/Password authentication.

Authentication Example Code: Add the following code to register and sign in users:​
dart​
Copy code​
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';

class AuthExample extends StatelessWidget {


final FirebaseAuth _auth = FirebaseAuth.instance;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Firebase Auth')),
body: Column(
children: [
ElevatedButton(
onPressed: () async {
// Register User
try {
UserCredential userCredential = await
_auth.createUserWithEmailAndPassword(
email: "[email protected]",
password: "123456",
);
print("User registered:
${userCredential.user?.email}");
} catch (e) {
print("Error: $e");
}
},
child: Text("Register"),
),
ElevatedButton(
onPressed: () async {
// Sign In User
try {
UserCredential userCredential = await
_auth.signInWithEmailAndPassword(
email: "[email protected]",
password: "123456",
);
print("User signed in:
${userCredential.user?.email}");
} catch (e) {
print("Error: $e");
}
},
child: Text("Sign In"),
),
],
),
);
}
}

2.​

Step 6: Test Your App


Run your app using a physical device or emulator:​
bash​
Copy code​
flutter run

1.​
2.​ Ensure Firebase is working correctly:
○​ Test the Firebase Authentication by checking the Authentication section in the
Firebase Console.
Notes

●​ Always keep your Firebase configuration files (google-services.json or


GoogleService-Info.plist) private.
●​ Ensure the app package name matches the one registered in Firebase.

This step-by-step guide should help you connect and integrate Firebase into your Flutter project
effectively.

///

WidgetsFlutterBinding.ensureInitialized()

This is a method in Flutter that ensures the Flutter engine is properly initialized before running
any code that depends on it. It is particularly useful when performing asynchronous operations
during the app's startup or when you need to access native platform services.

When Do We Use It?

We use WidgetsFlutterBinding.ensureInitialized() in the following scenarios:

Before Initializing Firebase​


When using plugins like Firebase, you need to initialize them before your app starts.​
Example:​
dart​
Copy code​
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}

1.​
2.​ Accessing Platform-Specific Code Early​
If your code depends on platform channels (e.g., accessing native Android/iOS features)
or accessing SharedPreferences/file systems.
3.​ During Asynchronous Initialization​
When you want to perform an asynchronous operation (like fetching data) before calling
runApp().

What Happens Internally?

●​ The WidgetsFlutterBinding class is the glue between the Flutter framework and
the Flutter engine.
●​ Calling WidgetsFlutterBinding.ensureInitialized() ensures that the binding
is initialized if it hasn’t been already. It initializes core services like rendering and event
handling.

What Happens If You Don’t Use It?

If you call asynchronous functions or initialize certain plugins (like Firebase) without ensuring
that the WidgetsFlutterBinding is initialized, you may encounter runtime errors, such as:

●​ "Binding not initialized"


●​ Unhandled exceptions when accessing platform-specific features.

Example Use Case

Without WidgetsFlutterBinding.ensureInitialized()
dart
Copy code
void main() async {
await Firebase.initializeApp(); // ❌ Error: Binding not
initialized!
runApp(MyApp());
}

Correct Way
dart
Copy code
void main() async {
WidgetsFlutterBinding.ensureInitialized(); // Ensures everything is
ready
await Firebase.initializeApp();
runApp(MyApp());
}

Key Points

1.​ It is safe to call WidgetsFlutterBinding.ensureInitialized() multiple times; it


will only initialize the binding once.
2.​ Use it whenever you need to perform asynchronous or platform-dependent tasks before
starting your Flutter app.

This ensures your app starts without errors or unexpected behaviors!


Day 2
Proper Email and Password Validation in Flutter

Here, we will implement email and password validation and Firebase


user authentication using the Provider package for user signup.

Step 1: Create Email and Password Validation

Validation Logic

We use RegExp to validate:

1.​Email:
○​ Should include an @ sign.
2.​Password:
○​ At least 8 characters.
○​ Includes at least:
■​ One uppercase letter.
■​ One lowercase letter.
■​ One digit.
■​ One special symbol.

Step 2: Code Implementation

1. UI Code for Signup Form


dart
Copy code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'firebase_auth_provider.dart';

class SignUpPage extends StatefulWidget {


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

class _SignUpPageState extends State<SignUpPage> {


final _formKey = GlobalKey<FormState>();
final TextEditingController _emailController =
TextEditingController();
final TextEditingController _passwordController =
TextEditingController();

@override
Widget build(BuildContext context) {
final authProvider = Provider.of<FirebaseAuthProvider>(context);

return Scaffold(
appBar: AppBar(title: Text("Sign Up")),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(labelText: "Email"),
validator: (vue) {
if (value == null || value.isEmpty) {
return "Email cannot be empty";
} else if
(!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
return "Enter a valid email (e.g.,
[email protected])";
}
return null;
},
),
SizedBox(height: 16),
TextFormField(
controller: _passwordController,
obscureText: true,
decoration: InputDecoration(labelText: "Password"),
validator: (value) {
if (value == null || value.isEmpty) {
return "Password cannot be empty";
} else if (value.length < 8) {
return "Password must be at least 8 characters";
} else if
(!RegExp(r'^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%
*?&]{8,}$').hasMatch(value)) {
return "Password must include uppercase,
lowercase, digit, and symbol";
}
return null;
},
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
final email = _emailController.text.trim();
final password = _passwordController.text.trim();

String? result = await authProvider.signUp(email,


password);

if (result == null) {
// Success

ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("Signup successful!"),
backgroundColor: Colors.green,
));
} else {
// Error

ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(result),
backgroundColor: Colors.red,
));
}
}
},
child: Text("Sign Up"),
),
],
),
),
),
);
}
}

2. Modify the FirebaseAuthProvider Class

Update the FirebaseAuthProvider class to include saving user data to SharedPreferences


after signup.

Updated FirebaseAuthProvider
dart
Copy code
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class FirebaseAuthProvider with ChangeNotifier {
final FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseFirestore _firestore = FirebaseFirestore.instance;

// Signup Function with Firestore and SharedPreferences Integration


Future<String?> signUp(String email, String password) async {
try {
// Create user in Firebase Authentication
UserCredential userCredential = await
_auth.createUserWithEmailAndPassword(
email: email,
password: password,
);

String userId = userCredential.user!.uid;

// Store user details in Firestore


await _firestore.collection("users").doc(userId).set({
'userId': userId,
'email': email,
});

// Save user details in SharedPreferences


SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('userId', userId);
await prefs.setString('email', email);

return null; // Success


} on FirebaseAuthException catch (e) {
if (e.code == 'email-already-in-use') {
return "Email is already in use.";
} else if (e.code == 'weak-password') {
return "The password provided is too weak.";
} else {
return e.message; // Other Firebase errors
}
} catch (e) {
return "An error occurred. Please try again.";
}
}

// Get User Data from SharedPreferences


Future<Map<String, String>> getUserData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? userId = prefs.getString('userId');
String? email = prefs.getString('email');

return {
'userId': userId ?? '',
'email': email ?? '',
};
}

// Clear User Data from SharedPreferences


Future<void> clearUserData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.remove('userId');
await prefs.remove('email');
}
}

3. Explanation

1.​ SharedPreferences Integration:


○​ Saving Data:
■​ prefs.setString('key', value) saves user ID and email locally.
○​ Fetching Data:
■​ prefs.getString('key') retrieves user data.
○​ Clearing Data:
■​ prefs.remove('key') deletes specific data when needed (e.g.,
logout).
2.​ Local Storage:
○​ Storing the userId and email ensures the app can work offline for certain
features.
4. Usage in the App

You can retrieve and display user data or clear it when required.

Fetch and Display User Data


dart
Copy code
void fetchUserData(FirebaseAuthProvider authProvider) async {
Map<String, String> userData = await authProvider.getUserData();
print("User ID: ${userData['userId']}");
print("Email: ${userData['email']}");
}

Clear User Data (Logout)


dart
Copy code
void logout(FirebaseAuthProvider authProvider) async {
await authProvider.clearUserData();
print("User data cleared from SharedPreferences.");
}
Below is a complete login function implementation for the same FirebaseAuthProvider
class. It authenticates the user using Firebase Authentication, retrieves the user's data from
Firestore, assigns the data to a model, and stores the data in SharedPreferences for
persistence.

1. Update FirebaseAuthProvider Class

Add the login function to authenticate users and retrieve their data:

dart
Copy code
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

// User Model Class


class UserModel {
final String userId;
final String email;

UserModel({
required this.userId,
required this.email,
});

// Factory to create UserModel from Firestore data


factory UserModel.fromMap(Map<String, dynamic> data, String id) {
return UserModel(
userId: id,
email: data['email'] ?? '',
);
}
}

class FirebaseAuthProvider with ChangeNotifier {


final FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseFirestore _firestore = FirebaseFirestore.instance;

UserModel? _currentUser;

UserModel? get currentUser => _currentUser;

// Login Function
Future<String?> login(String email, String password) async {
try {
// Authenticate user with FirebaseAuth
UserCredential userCredential = await
_auth.signInWithEmailAndPassword(
email: email,
password: password,
);

String userId = userCredential.user!.uid;

// Retrieve user data from Firestore


DocumentSnapshot userDoc = await
_firestore.collection('users').doc(userId).get();
if (userDoc.exists) {
// Convert Firestore data to UserModel
_currentUser = UserModel.fromMap(userDoc.data() as Map<String,
dynamic>, userId);

// Save user data in SharedPreferences


SharedPreferences prefs = await
SharedPreferences.getInstance();
await prefs.setString('userId', _currentUser!.userId);
await prefs.setString('email', _currentUser!.email);

notifyListeners();
return null; // Login successful
} else {
return "User data not found in Firestore.";
}
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
return "No user found for this email.";
} else if (e.code == 'wrong-password') {
return "Incorrect password.";
} else {
return e.message; // Other FirebaseAuth errors
}
} catch (e) {
return "An error occurred. Please try again.";
}
}

// Logout Function
Future<void> logout() async {
await _auth.signOut();
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.clear();
_currentUser = null;
notifyListeners();
}

// Fetch User Data from SharedPreferences


Future<void> fetchUserFromPreferences() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? userId = prefs.getString('userId');
String? email = prefs.getString('email');

if (userId != null && email != null) {


_currentUser = UserModel(userId: userId, email: email);
notifyListeners();
}
}
}
2. Explanation

1.​ Login Function:


○​ Firebase Authentication: Authenticates the user via FirebaseAuth.
○​ Firestore Retrieval: Retrieves user data from Firestore by userId.
○​ UserModel Assignment: Converts Firestore data to a UserModel.
○​ SharedPreferences Storage: Stores user email and ID locally for persistence.
2.​ Logout:
○​ Clears user data from both FirebaseAuth and SharedPreferences.
3.​ Fetch from SharedPreferences:
○​ Initializes the currentUser when the app launches if data exists in
SharedPreferences.

To show the logged-in user's email in a Drawer on the home screen, you can modify the
HomePage as follows:

Updated HomePage with Drawer

dart

Copy code

import 'package:flutter/material.dart';

import 'package:provider/provider.dart';

import 'firebase_auth_provider.dart';

class HomePage extends StatelessWidget {


@override

Widget build(BuildContext context) {

final authProvider = Provider.of<FirebaseAuthProvider>(context);

return Scaffold(

appBar: AppBar(

title: Text("Home"),

),

drawer: Drawer(

child: ListView(

padding: EdgeInsets.zero,

children: [

UserAccountsDrawerHeader(

accountName: Text("Welcome!"),

accountEmail: Text(

authProvider.currentUser?.email ?? "No email


available",

style: TextStyle(fontSize: 16),

),

currentAccountPicture: CircleAvatar(

child: Icon(Icons.person, size: 40),

),

decoration: BoxDecoration(
color: Colors.blue,

),

),

ListTile(

leading: Icon(Icons.home),

title: Text("Home"),

onTap: () {

Navigator.pop(context);

},

),

ListTile(

leading: Icon(Icons.logout),

title: Text("Logout"),

onTap: () async {

await authProvider.logout();

Navigator.pop(context); // Close the drawer

Navigator.pushReplacementNamed(context, '/login');

},

),

],

),

),

body: Center(
child: Text(

"Welcome to the Home Screen!",

style: TextStyle(fontSize: 18),

),

),

);

Explanation of Changes

1.​ Drawer Setup:


○​ Added a Drawer widget to the Scaffold.
○​ Used UserAccountsDrawerHeader to display the user's email and a
placeholder name.
○​ Fallback text ("No email available") is shown if no email is available.
2.​ User Email Display:
○​ Accessed authProvider.currentUser?.email to display the logged-in
user's email in the Drawer.
3.​ Logout Option:
○​ Added a ListTile for logging out, which:
■​ Calls the logout method from the FirebaseAuthProvider.
■​ Redirects the user to the login screen using
Navigator.pushReplacementNamed.

Routing Setup

Ensure your app has routing configured in main.dart for navigation to work correctly:

dart
Copy code

void main() async {

WidgetsFlutterBinding.ensureInitialized();

final authProvider = FirebaseAuthProvider();

// Fetch user data from SharedPreferences

await authProvider.fetchUserFromPreferences();

runApp(

ChangeNotifierProvider(

create: (_) => authProvider,

child: MyApp(),

),

);

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(

debugShowCheckedModeBanner: false,

initialRoute: '/',

routes: {
'/': (context) => Consumer<FirebaseAuthProvider>(

builder: (context, authProvider, _) {

return authProvider.currentUser != null

? HomePage()

: LoginPage();

},

),

'/login': (context) => LoginPage(),

},

);

Output

1.​ Drawer Header:


○​ Shows the logged-in user's email and a placeholder profile picture.
2.​ Home Screen:
○​ Displays a welcome message and provides a logout option.
3.​ Logout Behavior:
○​ Clears user data and redirects to the login page.

This approach ensures a smooth user experience with a clean and professional interface!
Firebase Provider Class

Create a PlanetProvider class to handle all the Firebase operations.

dart

Copy code

import 'package:cloud_firestore/cloud_firestore.dart';

import 'package:flutter/material.dart';

class PlanetProvider with ChangeNotifier {

final FirebaseFirestore _firestore = FirebaseFirestore.instance;

// Add Data

Future<void> addPlanet({

required String uid,

required String name,

required String title,

required String year,

required String description,

required String image,

}) async {

try {

await _firestore.collection('planets').add({
'uid': uid,

'name': name,

'title': title,

'year': year,

'description': description,

'image': image,

'createdAt': FieldValue.serverTimestamp(),

});

notifyListeners();

} catch (e) {

print("Error adding planet: $e");

// Get Data by UID

Stream<List<Map<String, dynamic>>> getPlanetsByUID(String uid) {

return _firestore

.collection('planets')

.where('uid', isEqualTo: uid)

.snapshots()

.map((snapshot) {

return snapshot.docs.map((doc) {

return {
'id': doc.id,

'name': doc['name'],

'title': doc['title'],

'year': doc['year'],

'description': doc['description'],

'image': doc['image'],

};

}).toList();

});

// Update Data

Future<void> updatePlanet({

required String id,

required String name,

required String title,

required String year,

required String description,

required String image,

}) async {

try {

await _firestore.collection('planets').doc(id).update({

'name': name,
'title': title,

'year': year,

'description': description,

'image': image,

'updatedAt': FieldValue.serverTimestamp(),

});

notifyListeners();

} catch (e) {

print("Error updating planet: $e");

// Delete Data

Future<void> deletePlanetByUid({

required String uid,

}) async {

try {

// Query the collection to find the document with the specified


UID

final querySnapshot = await _firestore

.collection('planets')

.where('uid', isEqualTo: uid)

.get();
// Check if the document exists

if (querySnapshot.docs.isNotEmpty) {

// Get the document ID

final docId = querySnapshot.docs.first.id;

// Delete the document

await _firestore.collection('planets').doc(docId).delete();

print("Planet deleted successfully.");

} else {

print("No planet found with UID: $uid");

} catch (e) {

print("Error deleting planet: $e");

UI Implementation

Add Planet UI
A simple form to add a planet.

dart

Copy code

import 'package:flutter/material.dart';

import 'package:provider/provider.dart';

import 'planet_provider.dart';

class AddPlanetPage extends StatelessWidget {

final TextEditingController nameController =


TextEditingController();

final TextEditingController titleController =


TextEditingController();

final TextEditingController yearController =


TextEditingController();

final TextEditingController descriptionController =


TextEditingController();

final TextEditingController imageController =


TextEditingController();

@override

Widget build(BuildContext context) {

final planetProvider = Provider.of<PlanetProvider>(context);

return Scaffold(

appBar: AppBar(
title: Text("Add Planet"),

),

body: Padding(

padding: const EdgeInsets.all(16.0),

child: Column(

children: [

TextField(

controller: nameController,

decoration: InputDecoration(labelText: "Name"),

),

TextField(

controller: titleController,

decoration: InputDecoration(labelText: "Title"),

),

TextField(

controller: yearController,

decoration: InputDecoration(labelText: "Year"),

),

TextField(

controller: descriptionController,

decoration: InputDecoration(labelText: "Description"),

),

TextField(
controller: imageController,

decoration: InputDecoration(labelText: "Image URL"),

),

SizedBox(height: 20),

ElevatedButton(

onPressed: () {

planetProvider.addPlanet(

uid: "user123", // Replace with actual user UID

name: nameController.text,

title: titleController.text,

year: yearController.text,

description: descriptionController.text,

image: imageController.text,

);

},

child: Text("Add Planet"),

),

],

),

),

);

}
Display Planets using StreamBuilder

dart

Copy code

import 'package:flutter/material.dart';

import 'package:provider/provider.dart';

import 'planet_provider.dart';

class PlanetListPage extends StatelessWidget {

@override

Widget build(BuildContext context) {

final planetProvider = Provider.of<PlanetProvider>(context);

return Scaffold(

appBar: AppBar(

title: Text("Planets"),

),

body: StreamBuilder<List<Map<String, dynamic>>>(

stream: planetProvider.getPlanetsByUID("user123"), // Replace


with actual UID

builder: (context, snapshot) {

if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());

if (!snapshot.hasData || snapshot.data!.isEmpty) {

return Center(child: Text("No Planets Found"));

final planets = snapshot.data!;

return ListView.builder(

itemCount: planets.length,

itemBuilder: (context, index) {

final planet = planets[index];

return ListTile(

title: Text(planet['name']),

subtitle: Text(planet['description']),

trailing: IconButton(

icon: Icon(Icons.delete),

onPressed: () {

planetProvider.deletePlanet(planet['id']);

},

),

onTap: () {

// Update functionality (if needed)

},

);
},

);

},

),

);

Update Planet UI

dart

Copy code

import 'package:flutter/material.dart';

import 'package:provider/provider.dart';

import 'planet_provider.dart';

class UpdatePlanetPage extends StatelessWidget {

final String id;

final String name;

final String title;

final String year;

final String description;

final String image;


UpdatePlanetPage({

required this.id,

required this.name,

required this.title,

required this.year,

required this.description,

required this.image,

});

final TextEditingController nameController =


TextEditingController();

final TextEditingController titleController =


TextEditingController();

final TextEditingController yearController =


TextEditingController();

final TextEditingController descriptionController =


TextEditingController();

final TextEditingController imageController =


TextEditingController();

@override

Widget build(BuildContext context) {

final planetProvider = Provider.of<PlanetProvider>(context);


nameController.text = name;

titleController.text = title;

yearController.text = year;

descriptionController.text = description;

imageController.text = image;

return Scaffold(

appBar: AppBar(

title: Text("Update Planet"),

),

body: Padding(

padding: const EdgeInsets.all(16.0),

child: Column(

children: [

TextField(

controller: nameController,

decoration: InputDecoration(labelText: "Name"),

),

TextField(

controller: titleController,

decoration: InputDecoration(labelText: "Title"),

),

TextField(
controller: yearController,

decoration: InputDecoration(labelText: "Year"),

),

TextField(

controller: descriptionController,

decoration: InputDecoration(labelText: "Description"),

),

TextField(

controller: imageController,

decoration: InputDecoration(labelText: "Image URL"),

),

SizedBox(height: 20),

ElevatedButton(

onPressed: () {

planetProvider.updatePlanet(

id: id,

name: nameController.text,

title: titleController.text,

year: yearController.text,

description: descriptionController.text,

image: imageController.text,

);

},
child: Text("Update Planet"),

),

],

),

),

);

Summary

●​ Add: Add a planet to Firebase.


●​ Get: Fetch planets based on the UID using StreamBuilder.
●​ Update: Update an existing planet's data.
●​ Delete: Remove a planet from Firebase.

You can now create routes to navigate between the Add, Update, and Planet List pages

Update Function

Create a function to show an AlertDialog for updating the planet name.

dart

Copy code

void _showUpdateDialog({
required BuildContext context,

required String planetId,

required String currentName,

}) {

final TextEditingController nameController =

TextEditingController(text: currentName);

showDialog(

context: context,

builder: (BuildContext context) {

return AlertDialog(

title: Text("Update Planet Name"),

content: TextField(

controller: nameController,

decoration: InputDecoration(

labelText: "Planet Name",

border: OutlineInputBorder(),

),

),

actions: [

TextButton(

child: Text("Cancel"),

onPressed: () {
Navigator.of(context).pop();

},

),

TextButton(

child: Text("Update"),

onPressed: () async {

// Update logic

await Provider.of<PlanetProvider>(context, listen:


false)

.updatePlanet(

id: planetId,

name: nameController.text,

title: '', // Keep the title as is

year: '', // Keep the year as is

description: '', // Keep the description as is

image: '', // Keep the image as is

);

Navigator.of(context).pop();

},

),

],

);

},
);

Delete Function

Create a function to show an AlertDialog for deleting a planet.

dart

Copy code

void _showDeleteDialog({

required BuildContext context,

required String planetId,

}) {

showDialog(

context: context,

builder: (BuildContext context) {

return AlertDialog(

title: Text("Are you sure to delete this planet?"),

actions: [

TextButton(

child: Text("Cancel"),

onPressed: () {

Navigator.of(context).pop();

},
),

TextButton(

child: Text("Delete"),

onPressed: () async {

// Delete logic

await Provider.of<PlanetProvider>(context, listen:


false)

.deletePlanet(planetId);

Navigator.of(context).pop();

},

),

],

);

},

);

PlanetProvider Update and Delete Logic

Ensure the updatePlanet function updates only the provided fields by fetching the document
and merging the new data. Here’s how:

dart

Copy code

Future<void> updatePlanet({
required String id,

String? name,

String? title,

String? year,

String? description,

String? image,

}) async {

try {

// Fetch current data

final docRef = _firestore.collection('planets').doc(id);

final docSnapshot = await docRef.get();

final currentData = docSnapshot.data() ?? {};

// Merge updated fields with current data

final updatedData = {

'name': name ?? currentData['name'],

'title': title ?? currentData['title'],

'year': year ?? currentData['year'],

'description': description ?? currentData['description'],

'image': image ?? currentData['image'],

'updatedAt': FieldValue.serverTimestamp(),

};
await docRef.update(updatedData);

notifyListeners();

} catch (e) {

print("Error updating planet: $e");

Future<void> deletePlanet(String id) async {

try {

await _firestore.collection('planets').doc(id).delete();

notifyListeners();

} catch (e) {

print("Error deleting planet: $e");

Explanation

●​ Update Functionality:
○​ Opens an AlertDialog to update the planet name.
○​ Uses the updatePlanet function to update the Firebase record.
○​ Other fields remain unchanged.
●​ Delete Functionality:
○​ Confirms deletion via AlertDialog.
○​ Deletes the record from Firebase using the deletePlanet function.
This approach ensures real-time updates to your UI using the StreamBuilder.

Updated Code

Stream Function with Update and Delete

dart

Copy code

SizedBox(

height: height / 3.7,

child: StreamBuilder<List<Map<String, dynamic>>>(

stream: planetProvider.getPlanetsByUID(

"Vb8eaBjgwBSAauzzpWmZBJhUzLC2"), // Replace with actual UID

builder: (context, snapshot) {

if (snapshot.connectionState == ConnectionState.waiting) {

return Center(child: CircularProgressIndicator());

if (!snapshot.hasData || snapshot.data!.isEmpty) {

return Center(child: Text("No Planets Found"));

final planets = snapshot.data!;


return ListView.builder(

itemCount: planets.length,

itemBuilder: (context, index) {

final planet = planets[index];

return InkWell(

onTap: () {

Navigator.push(

context,

MaterialPageRoute(

builder: (context) => DetailScreen(

imageUrl: planet['image'],

title: planet['name'],

),

),

);

},

child: commonBGcontainer(

state: 2,

width: double.infinity,

height: height / 5,

child: Row(

crossAxisAlignment: CrossAxisAlignment.start,

children: [
Image.network(

planet['image'],

scale: 10,

),

const SizedBox(

width: 10,

),

Column(

crossAxisAlignment: CrossAxisAlignment.start,

children: [

Row(

children: [

Text(

planet['name'],

style: fontWhite35,

),

Padding(

padding: const EdgeInsets.only(left: 40),

child: Row(

children: [

IconButton(

onPressed: () {

// Update Functionality
_showUpdateDialog(

context: context,

planetId: planet['id'],

currentName: planet['name'],

);

},

icon: Icon(

Icons.edit,

size: 25,

color: AppColor.homeScreenC5,

),

),

IconButton(

onPressed: () {

// Delete Functionality

_showDeleteDialog(

context: context,

planetId: planet['id'],

);

},

icon: Icon(

Icons.delete,

size: 25,
color: AppColor.homeScreenC5,

),

),

],

),

],

),

SizedBox(

width: 220,

height: 60,

child: Text(

planet["description"],

style: fontwhite14,

),

],

),

],

),

),

);

},
);

},

),

);

Last day​

Here's the updated code to pick an image from the gallery, upload it to Firebase Storage, and
print the uploaded image URL in the console.

Complete Functionality:
dart
Copy code
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:firebase_storage/firebase_storage.dart';

class ImageHandler {
final ImagePicker imagePicker = ImagePicker();
File? pickedImage;
Size imageSize = Size(0, 0);
bool isImageEmpty = false;

/// Pick an image from the specified source


Future<void> pickImageAndUpload(ImageSource imageSource) async {
try {
// Pick an image
final img = await imagePicker.pickImage(source: imageSource);

if (img != null) {
// Decode image to get its size
final image = await decodeImageFromList(await
img.readAsBytes());
pickedImage = File(img.path);
imageSize = Size(image.width.toDouble(),
image.height.toDouble());

// Upload the image to Firebase and get the URL


final imageUrl = await uploadImageToFirebase(pickedImage!);

if (imageUrl != null) {
print("Uploaded Image URL: $imageUrl");
}
} else {
print("No image selected.");
isImageEmpty = true;
}
} catch (e) {
isImageEmpty = true;
print("Error picking or uploading image: $e");
}
}

/// Upload the image to Firebase Storage and return the URL
Future<String?> uploadImageToFirebase(File imageFile) async {
try {
// Define a unique path for the image in Firebase Storage
final storageRef = FirebaseStorage.instance
.ref()

.child("images/${DateTime.now().millisecondsSinceEpoch}.jpg");

// Upload the image


final uploadTask = await storageRef.putFile(imageFile);

// Get the download URL after successful upload


final downloadUrl = await uploadTask.ref.getDownloadURL();
return downloadUrl;
} catch (e) {
print("Error uploading image: $e");
return null;
}
}
}

Usage:

1.​ Call the pickImageAndUpload() function from your widget or button press, specifying
the source (ImageSource.gallery or ImageSource.camera):

dart
Copy code
final imageHandler = ImageHandler();

ElevatedButton(
onPressed: () async {
await imageHandler.pickImageAndUpload(ImageSource.gallery);
},
child: Text("Pick Image and Upload"),
);

Key Features:

1.​ Image Picking: Picks an image from the specified source (gallery or camera).
2.​ Image Size: Decodes the image to get its dimensions.
3.​ Firebase Upload: Uploads the image to Firebase Storage and retrieves its download
URL.
4.​ Error Handling: Prints relevant error messages for debugging
1. Set Up Firebase in Your Project

●​ Create a Firebase Project:


1.​ Go to Firebase Console.
2.​ Create a new project or select an existing project.
●​ Add Your App:
1.​ For Android: Add an Android app, enter your package name, download the
google-services.json file, and place it in the android/app directory.
2.​ For iOS: Add an iOS app, download the GoogleService-Info.plist file,
and place it in the ios/Runner directory.
3.​ For Web: Copy your Firebase configuration object from Firebase Console under
Project Settings > General > Firebase SDK snippet > Config.
●​ Enable Google Sign-In in Firebase Console:
1.​ Go to Authentication > Sign-in Method in the Firebase Console.
2.​ Enable Google as a sign-in provider.

2. Add Dependencies

In your pubspec.yaml file, add the following dependencies:

yaml
Copy code
dependencies:
firebase_auth: ^5.0.1
firebase_core: ^3.7.0
google_sign_in: ^7.6.0

Run:

bash
Copy code
flutter pub get

3. Initialize Firebase

In the main.dart file, initialize Firebase:


dart
Copy code
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

void main() async {


WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}

class MyApp extends StatelessWidget {


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

4. Google Sign-In Functionality

Create a Dart file google_sign_in.dart:

dart
Copy code
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';

class GoogleSignInProvider {
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn _googleSignIn = GoogleSignIn();

/// Sign in with Google


Future<User?> signInWithGoogle() async {
try {
// Trigger the Google Sign-In process
final GoogleSignInAccount? googleUser = await
_googleSignIn.signIn();

if (googleUser != null) {
// Obtain the Google authentication details
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;

// Create a new credential


final AuthCredential credential =
GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);

// Sign in to Firebase
final UserCredential userCredential =
await _auth.signInWithCredential(credential);

// Return the Firebase user


return userCredential.user;
} else {
print("Google Sign-In canceled");
return null;
}
} catch (e) {
print("Error during Google Sign-In: $e");
return null;
}
}

/// Sign out


Future<void> signOut() async {
await _auth.signOut();
await _googleSignIn.signOut();
}
}
5. Create the UI

Create a GoogleSignInScreen widget to handle the sign-in UI.

dart
Copy code
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'google_sign_in.dart';

class GoogleSignInScreen extends StatefulWidget {


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

class _GoogleSignInScreenState extends State<GoogleSignInScreen> {


final GoogleSignInProvider _googleSignInProvider =
GoogleSignInProvider();

User? _user;

Future<void> _handleSignIn() async {


final user = await _googleSignInProvider.signInWithGoogle();
if (user != null) {
setState(() {
_user = user;
});
}
}

void _handleSignOut() async {


await _googleSignInProvider.signOut();
setState(() {
_user = null;
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Google Sign-In with Firebase"),
),
body: Center(
child: _user == null
? ElevatedButton.icon(
icon: Icon(Icons.login),
label: Text("Sign In with Google"),
onPressed: _handleSignIn,
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircleAvatar(
backgroundImage: NetworkImage(_user?.photoURL ??
""),
radius: 40,
),
SizedBox(height: 16),
Text("Name: ${_user?.displayName}"),
Text("Email: ${_user?.email}"),
SizedBox(height: 16),
ElevatedButton.icon(
icon: Icon(Icons.logout),
label: Text("Sign Out"),
onPressed: _handleSignOut,
),
],
),
),
);
}
}

6. Firebase Console Authentication


You can verify signed-in users in the Firebase Console under Authentication > Users.

7. Test Your App

●​ Run the app on an emulator or device.


●​ Click on "Sign In with Google."
●​ Choose a Google account to log in.
●​ The user's information (name, email, photo URL) will appear on the screen after a
successful login.

Key Features

1.​ Login: Allows users to sign in using their Google account.


2.​ Profile Information: Displays the user's name, email, and profile picture after login.
3.​ Logout: Allows users to sign out from the app.

Notes:
For iOS, configure Info.plist by adding:​
xml​
Copy code​
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>com.googleusercontent.apps.YOUR_CLIENT_ID</string>
</array>
</dict>
</array>
<key>GoogleSignIn</key>
<string>YOUR_CLIENT_ID</string>

●​ Replace YOUR_CLIENT_ID with the Google Client ID from Firebase Console.


●​ For Android, ensure your SHA-1 fingerprint is added in Firebase Console.

This setup will fully integrate Google Sign-In with Firebase in your Flutter app! 🚀
API,s
Understanding APIs and Their Usage in Flutter

What is an API?

An API (Application Programming Interface) is a software intermediary that allows two


applications or systems to communicate with each other. It acts as a bridge, enabling one
system to request data or services from another system and receive responses.

Key Points about APIs:

●​ APIs define the rules and protocols for communication.


●​ They abstract the internal complexity of a system, exposing only what is necessary for
interaction.
●​ APIs are widely used in mobile, web, and software applications for accessing external
data or functionalities.

Examples of API Operations

1.​ GET API:


○​ Purpose: To fetch or retrieve data from the server.
○​ Example: When you open a weather app, it sends a GET request to an API to
retrieve current weather data.
2.​ POST API:
○​ Purpose: To send or upload data to the server.
○​ Example: When you submit a login form, your credentials (email and password)
are sent to the server using a POST request.

How APIs Work:

Let’s break down the process with a simple analogy:

1.​ A Client (e.g., your mobile app) requests data from a Server (e.g., a backend system).
2.​ The API acts as the waiter in a restaurant:
○​ The waiter (API) takes your order (client request).
○​ Hands it over to the kitchen (server) for preparation.
○​ Returns with the food (response).
3.​ Similarly, the API receives the client request, processes it, and sends the appropriate
response back to the client.
API Workflow Explained

1.​ Request:
○​ The client sends a request to the API. This includes:
■​ Endpoint: The URL of the API (e.g., /users).
■​ Method: Defines the type of operation (e.g., GET or POST).
■​ Headers: Include metadata like API keys or content types.
■​ Body (Optional): Data sent with the request (used in POST, PUT, etc.).
2.​ Processing:
○​ The server receives the request, processes it, and performs the required
operations (e.g., fetch data from a database).
3.​ Response:
○​ The server sends a response back to the client. This includes:
■​ Status Code: Indicates success or failure (e.g., 200 OK or 404 Not
Found).
■​ Data: The requested information or confirmation of the operation.

Diagram: API as a Bridge

Here’s a simple illustration of how APIs act as a bridge between the client and the server:

Copy code

1.​ [Client (Mobile App)]


2.​ | Sends Request (GET/POST) |
3.​ -------------------------------->
4.​ [API]
5.​ -------------------------------->
6.​ | Processes Request in Server |
7.​ [Server (Backend)]
8.​ | Sends Response (JSON) |
9.​ <--------------------------------
10.​ [API]
11.​ <--------------------------------
12.​ | Displays Response in the App |
13.​[Client (Mobile App)]
GET and POST API Examples in Flutter

GET API: Fetch Data

●​ Request: "Get a list of users."


●​ Response: A JSON array with user details.
●​ Usage: Displaying the list of users in a Flutter app.

POST API: Send Data

●​ Request: "Create a new user with name and email."


●​ Response: Confirmation or the created user’s details.
●​ Usage: Adding a new record to the server from a form in Flutter.

Benefits of APIs

1.​ Modularity: APIs allow separation of the client and server, enabling independent
updates.
2.​ Reusability: Same API can be used across different apps (e.g., mobile, web).
3.​ Scalability: Simplifies data handling between systems.
14.​

In Flutter, HTTP APIs are commonly used for communication between a mobile app and a
backend server.

Steps to Use APIs in Flutter


Add http Package: Add the http package in pubspec.yaml:​
yaml​
Copy code​
dependencies:
http: ^1.1.0
Run:​
bash​
Copy code​
flutter pub get

1.​
2.​ Create a Data Model: Models represent structured data, making it easy to parse the API
response and assign values.
3.​ Create API Service: A dedicated class will handle the API calls (e.g., GET and POST
requests).
4.​ Show Data in UI: Use Flutter widgets to display the fetched data from the API.

Example: GET and POST APIs in Flutter

Scenario:

We will fetch and display a list of users from a sample API and post a new user's data.

1. Create a Data Model

Define a model to represent the user data:

dart
Copy code
class User {
final int id;
final String name;
final String email;

// Constructor
User({required this.id, required this.name, required this.email});

// Factory method to create a User from JSON


factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'],
name: json['name'],
email: json['email'],
);
}

// Method to convert User to JSON for POST request


Map<String, dynamic> toJson() {
return {
'name': name,
'email': email,
};
}
}

2. Create API Service

Create a file api_service.dart:

dart
Copy code
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'user_model.dart';

class ApiService {
final String baseUrl = "https://jsonplaceholder.typicode.com";

// GET request to fetch users


Future<List<User>> fetchUsers() async {
try {
final response = await http.get(Uri.parse("$baseUrl/users"));

if (response.statusCode == 200) {
final List<dynamic> data = jsonDecode(response.body);
return data.map((json) => User.fromJson(json)).toList();
} else {
throw Exception("Failed to load users");
}
} catch (e) {
print("Error fetching users: $e");
throw e;
}
}

// POST request to add a new user


Future<User> createUser(User user) async {
try {
final response = await http.post(
Uri.parse("$baseUrl/users"),
headers: {"Content-Type": "application/json"},
body: jsonEncode(user.toJson()),
);

if (response.statusCode == 201) {
return User.fromJson(jsonDecode(response.body));
} else {
throw Exception("Failed to create user");
}
} catch (e) {
print("Error creating user: $e");
throw e;
}
}
}

3. Implement UI in Flutter

Display Users (GET API)

In your main.dart file:

dart
Copy code
import 'package:flutter/material.dart';
import 'api_service.dart';
import 'user_model.dart';

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

class MyApp extends StatelessWidget {


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

class UserScreen extends StatefulWidget {


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

class _UserScreenState extends State<UserScreen> {


late Future<List<User>> _users;

@override
void initState() {
super.initState();
_users = ApiService().fetchUsers();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Users List")),
body: FutureBuilder<List<User>>(
future: _users,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text("Error: ${snapshot.error}"));
} else if (snapshot.hasData) {
final users = snapshot.data!;
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
final user = users[index];
return ListTile(
title: Text(user.name),
subtitle: Text(user.email),
);
},
);
} else {
return Center(child: Text("No data found"));
}
},
),
);
}
}

Add User (POST API)

Add a button to the above screen to create a new user:

dart
Copy code
FloatingActionButton(
onPressed: () async {
final newUser = User(id: 0, name: "John Doe", email:
"[email protected]");
final createdUser = await ApiService().createUser(newUser);
print("User Created: ${createdUser.name}, ${createdUser.email}");
},
child: Icon(Icons.add),
)

4. Explanation

GET API:

●​ fetchUsers method calls the API endpoint /users.


●​ Decodes the JSON response into a list of User objects using the User.fromJson
factory method.
●​ Displays the list of users using a ListView.builder.

POST API:

●​ createUser method sends a POST request with the new user's data (converted to
JSON using toJson).
●​ Parses the server's response to create a User object for the new user.

5. Testing the API

1.​ Run the app.


2.​ The list of users fetched via the GET API will appear.
3.​ Tap the Floating Action Button to create a new user via the POST API. The created
user's details will be printed in the console.

6. Sample API Endpoints

For testing, you can use the following public API:

●​ GET: https://jsonplaceholder.typicode.com/users
●​ POST: https://jsonplaceholder.typicode.com/users

🚀
This setup demonstrates how to work with APIs in Flutter, from creating models to handling
HTTP requests and displaying data in the UI. Let me know if you have further questions!
short skill
Create website free with hosting​
odoo.com..

Prompt Engineering

Prompt engineering is the practice of designing and refining prompts to communicate


with generative AI models effectively. It involves structuring instructions in a way that
maximizes the quality and relevance of the AI's responses. This discipline is essential
for optimizing interactions with language models, ensuring that users receive the
desired outputs efficiently. Key Concepts of Prompt Engineering

●​ Definition: Prompt engineering is the process of creating and optimizing prompts


to enhance the performance of generative AI models, particularly large language
models (LLMs). It involves crafting instructions that the AI can interpret
effectively.
●​ Importance: Effective prompt engineering helps in understanding the capabilities
and limitations of LLMs, allowing users to leverage these models for various
applications, including question answering, creative writing, and more.

Techniques in Prompt Engineering

●​ Zero-shot Prompting: Directly instructing the AI without additional context. This


method is suitable for simple tasks.
●​ Few-shot Prompting: Providing examples to guide the AI's output, making it more
effective for complex tasks.
●​ Chain-of-Thought (CoT) Prompting: Breaking down complex reasoning into
intermediate steps to improve accuracy in the AI's responses.
●​ Prompt Chaining: Dividing a complex task into smaller subtasks, using the AI's
outputs to achieve the overall goal.

Applications of Prompt Engineering

●​ Text Generation: Crafting prompts for generating coherent and contextually


relevant text, such as articles, summaries, or creative writing.
●​ Image Generation: Designing prompts for text-to-image models, specifying
details like style, subject, and medium to achieve desired visual outputs.
●​ Interactive Systems: Enhancing chatbots and virtual assistants to handle
complex queries and provide accurate responses.

Future of Prompt Engineering

●​ Career Opportunities: The demand for prompt engineers is growing, with job
openings and competitive salaries reflecting the importance of this skill in the AI
landscape.
●​ Evolving Techniques: As generative AI technology advances, new prompting
techniques and strategies will continue to emerge, making prompt engineering a
dynamic field.
●​ Ethical Considerations: Ensuring fairness and transparency in AI outputs will be
crucial as prompt engineering practices evolve.

https://platform.openai.com/docs/guides/prompt-engineering

Typing Jobs

https://www.truelancer.com/freelance-copy-typing-jobs

https://coursiv.io/v4?prc_id=31&utm_source=google&utm_medium=cpc&utm_campaign=21874
930052&utm_adgroupid=173546800481&utm_keyword=data%20entry%20work%20from%20ho
me&utm_type=nonbrand_s&utm_acc=1308143291&utm_alen=1&gad_source=1&gclid=Cj0KCQ
iAx9q6BhCDARIsACwUxu4ptEolY8NBi5toeNUXYu8qcGeVlGsEEMq2BuTvgwWnQDtQLfLl40U
aAlUBEALw_wcB

https://remotejobsfinder.co/en/onboarding/27/quiz?utm_medium=cpc&utm_medium=cpc&utm_s
ource=google&utm_source=google&utm_campaign=rjf_wide_search_world_exp&utm_campaig
n=cid%7C21541065497%7Caid%7C715695690161&utm_term=data+entry+jobs&utm_content=
gid%7C172155661830%7Cphr%7Ckwd-10388071%7Cdvc%7Cc%7Cpos%7C%7Cmch%7Cb%
7Csrc%7C%7Creg%7C1011084%7Crin%7C%7Ckw%7Cdata+entry+jobs&gad_source=1&gclid
=Cj0KCQiAx9q6BhCDARIsACwUxu4wAskknjpyBiXCwJS016bAue13VLqlIuF7GacCVXwt0hzXD
Xx9nBIaAisUEALw_wcB

https://www.remotejobs.io/welcome/discover-your-dream-remote-job?utm_source=google&utm_
medium=cpc&utm_campaign=20876014487&utm_term=data%20entry%20jobs%20from%20ho
me&network=g&device=c&adposition=&adgroupid=157342343976&placement=&adid=6851406
60878&gad_source=1&gclid=Cj0KCQiAx9q6BhCDARIsACwUxu7pkZ2cT79PXOlz3OqNVtGU3
n9XCD1bfPHdMOYSgIrCOMG_JYO9WtgaAssEEALw_wcB

Data analytics course


https://drive.google.com/file/d/1aMk_lM1d0yWEODQfWgGTPCJ0XbKo0l_q/view

You might also like