Event Loop in Dart
Dart is a single-threaded language.
Dart uses an event loop to schedule asynchronous tasks.
Dart always executes synchronous code immediately. It only
executes the tasks in the microtask queue once the synchronous
tasks are empty. And it only executes the tasks in the event queue
once both synchronous tasks and the microtask queue are empty.
https://www.dhiwise.com/post/how-dart-
event-loop-influence-the-execution-dart-code
Async Await in Dart
When an await expression is encountered, the
function is suspended, and control is returned to
the caller until the awaited operation completes.
Once the awaited operation finishes, the function
resumes execution from where it left off.
Container
When you create a Container and give it a child widget, remember that
it's the parent widget in the render tree that decides the Container's size.
SizedBox
If given a child, this widget forces it to have a specific width and/or
height. These values will be ignored if this widget's parent does not permit
them.
Difference between Container and SizedBox
The main differences lie in the way a container creates a more decorated
box for its child widget to sit in. On the other hand, SizedBox acts like a box
widget that has a constant height and width for its child widget.
Understanding constraints
Constraints go down. Sizes go up. Parent sets position.
In Flutter, understanding the layout system is crucial for building
responsive and visually appealing user interfaces. The rule "Constraints
go down. Sizes go up. Parent sets position." is a guideline to help
developers comprehend how Flutter's layout mechanism works. Let's
break it down:
Constraints go down
This means that the parent widget imposes constraints on its children.
Constraints define the minimum and maximum width and height that a
child widget can have. These constraints are passed from the parent to
the child during the layout phase.
For example, consider a Container widget inside a Column. The Column
will provide constraints to the Container, such as how wide it can be and
how tall it can be. These constraints are determined based on the
parent's size and the specific properties of the layout widgets involved.
Column(
children: [
Container(
width: 100, // Explicit width constraint
height: 50, // Explicit height constraint
color: Colors.red,
),
],
)
In this case, the Container receives constraints from the Column, and it
will use these constraints to decide its own size.
Sizes go up
After a child widget receives constraints from its parent, it decides its
own size based on these constraints. Once the child widget has
determined its size, it reports this size back to the parent.
Continuing with the previous example, the Container decides that it will
be 100 pixels wide and 50 pixels tall (as specified). This size information
is then reported back to the Column.
Parent sets position
Finally, the parent widget uses the sizes reported by its children to
determine their positions within itself. The parent widget places its
children based on their sizes and any layout properties (like padding,
alignment, etc.).
For instance, the Column will position the Container within its own layout
according to the specified alignment. By default, children in a Column
are positioned one after another vertically.
Putting it all together
Consider the following complete example:
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
Container(
width: 100, // Child decides its width
height: 50, // Child decides its height
color: Colors.red,
),
Container(
width: 150, // Child decides its width
height: 70, // Child decides its height
color: Colors.blue,
),
],
),
),
);
}
}
Constraints go down: The Column widget imposes constraints on its
children. Each Container receives these constraints, which might limit
their maximum width and height.
Sizes go up: Each Container decides its size (100x50 for the red one,
150x70 for the blue one) within the given constraints and reports these
sizes back to the Column.
Parent sets position: The Column uses the sizes of its children to
position them. In this case, it stacks them vertically one after another
based on their reported sizes.
By following these principles, Flutter ensures a flexible and efficient
layout process where parents control constraints and positions, while
children determine their own sizes within those constraints.
Understanding constraints
Constraints go down. Sizes go up. Parent sets position.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Flutter Layout Rule')),
body: Center(
child: Container(
color: Colors.blue,
width: 200,
height: 200,
child: Container(
width: 100,
height: 100,
color: Colors.red,
child: const Text('Hello, World!'),
),
),
),
),
);
}
}
Explanation of the Layout Process
Constraints go down:
The Center widget centers its child within itself and passes down loose constraints allowing the child to
be as small or as large as it wants (within the constraints of the screen size).
The outer Container (blue) receives these loose constraints from the Center. It then passes constraints down to
its child (the inner Container).
Sizes go up:
The outer Container (blue) specifies its own size as 200x200, within the constraints it received from the
Center.
The inner Container (red) receives the constraints from its parent (the blue Container). Since it has
specified its own width and height as 100x100, it chooses this size within the constraints provided by the blue
Container.
Parent sets position:
The Center positions the blue Container at the center of the available space.
The blue Container then positions the red Container at the top-left corner of its own bounding box, because the
default alignment for a Container's child is the top-left corner.
Visual Representation
Here's a step-by-step visualization:
Step 1: The Center widget centers its child (the blue Container) in the middle of the screen.
Step 2: The blue Container sets its own size to 200x200 pixels.
Step 3: The blue Container passes constraints to its child (the red Container), allowing it to be at most 200x200
pixels.
Step 4: The red Container sets its own size to 100x100 pixels.
Step 5: The blue Container positions the red Container at the top-left corner of the 200x200 space it occupies.
Step 6: The text "Hello, World!" is placed inside the red Container.
Conclusion
This example demonstrates the layout rule in Flutter effectively:
Constraints go down: Constraints flow from the Center to the blue Container, and then to the red
Container.
Sizes go up: The red Container determines its size (100x100), which is then used by the blue Container
(200x200) to finalize its own layout.
Parent sets position: The Center positions the blue Container in the center, and the blue Container
positions the red Container at its top-left corner.
Factory Constructors
Factory constructors are a key part of Flutter. They can return an
instance of a class that is either a fresh instance or an instance that has
been previously created, i.e., an existing instance. This is different from
a default constructor which always returns a new instance. The factory
constructor can also return an instance of a different class.
Encoding and Decoding
Encoding and serialization are the same thing—turning a data
structure into a string. Decoding and deserialization are the opposite
process—turning a string into a data structure.
However, serialization also commonly refers to the entire process of
translating data structures to and from a more easily readable format.
Wireless Debugging Steps
1. Go to C:\Android\sdk\platform-tools> or where adb.exe exists in your
computer
2. Run ".\adb pair" command
3. Then Run ".\adb pair 192.168.0.193:37889 429215" -> .\adb pair
PHONEIP:PORT_NUMBER PAIR_CODE from the mobile
4. Then Run ".\adb connect PHONEIP:PORT_NUMBER PAIR_CODE from the
mobile
Riverpod State Management
Riverpod
Riverpod is a Reactive State Management and Dependency Injection
Framework that is the enhanced version of Provider.
Types of Riverpod Providers
Provider
o It is used to store immutable objects
StateProvider
o It is used to store simple mutable objects like string,
enum,number, boolean etc…
StateNotifierProvider
NotifierProvider
FutureProvider
StreamProvider
ChangeNotifierProvider
ProviderScope
It will stores all the state of the providers which we will create in our
application.
Behind the scene it actually creates the instance of Provider
Container.
Riverpod Provider
It is an object that encapsulates a piece of state and allows listening
to that state.
Provider Syntax & Use
final nameProvider = Provider<String>((ref) { return 'Hello
World; });
nameProvider -> Is a global variable that is used to read the state of
the provider
Provider<String> -> What typeof provider we are using and <Type
of state> it holds
It is used for accessing dependencies and objects that are
immutable
May be used to access Repository, Service and some other class
that are immutable
Read Provider Data
The Riverpod Provider that we created is outside the widget tree.
To read the value from the Provider we need ref object which is of
type WidgetRef
There are many ways to read data from provider
1. By extends the stateless widget by ConsumerWidget
2. If the widget is Stateless widget by using Consumer
3. By extends the stateful widget by ConsumerStatefulWidget
and extends the State by using ConsumerState
Difference between ref.watch and ref.read
ref.watch -> is used inside the build method. Whenever the
provider value changed it automatically rebuilds the widget
ref.read -> is used to read the provider value once
Update provider object value
To update the provider value one of the following 2 methods can be
used
1. Use ref.read(providerName.notifier).state++;
2. Use ref.read(providerName.notifier).update((state) => state+1;);
To reset the StateProvider state value
1. Use invalidate
2. Use refresh
To listen to the provider value changes
To listen to the provider value changes use ref.listen
ref.listen(counterProvider, ((previous, next) {
if (next == 5) {
Scaffold Messenger. of (context)showSnackBar (SnackBar
(content: Text('The value is $next'))
}));
StateNotifierProvider
StateNotifierProvider is a provider that is used to listen to and
expose a State Notifier
StateNotifierProvider along with StateNotifier is Riverpod's
recommended solution for managing state which may change in reaction
to a user interaction.
Used for centralizing the Business Logic in a single place, improving
maintainability over time.
Notifier
To update the state of a provider from within Consumer we need to
use Notifier
Notifiers are the "stateful widget" of providers.
Syntax is as follows:
final name =
SomeNotifierProvider.someModifier<MyNotifier,Result>(MyNotifier.new);
class MyNotifier extends SomeNotifier<Result> {
@override
Result build() {
<your logic here>
<your methods here>
Aspect NotifierProvider AsyncNotifierProvider
Asynchronous (AsyncValue<T>, handling
State Type Synchronous (plain data)
loading, data, error)
Notifier
Extends Notifier<T> Extends AsyncNotifier<T>
Class
Use Case Simple state management, no Asynchronous operations like API calls or
Aspect NotifierProvider AsyncNotifierProvider
async logic database queries
Example Future<String>, Stream<List<T>>,
int, bool, String, List
State AsyncValue<T>
State Synchronous (immediate
Asynchronous (delayed/fetched state)
Changes updates)
Error Built-in error handling with
Manual error handling
Handling AsyncValue.error