0% found this document useful (0 votes)
11 views87 pages

Cours8 StateManagement

Uploaded by

chsidahmed05
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)
11 views87 pages

Cours8 StateManagement

Uploaded by

chsidahmed05
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/ 87

Sciences faculty

Computer Science Department


Computer Science Licence3

Mobile applications
-Flutter framework-

Mrs Asma Hamitou Mehiaoui


[email protected] University year: 2023 - 2024
Chapter 9:
State Management

2
Plan
• Ephemeral states & app states.

• Why State Management is important ?

• State Management approaches:

 Provider.
 BLoC.

3
Ephemeral state & App state
• Ephemeral state : is the state contained in a
single widget. It is called, UI state or local
state.

• Examples :
 state of a checkbox,
 state of a Textfield,
…

 If no other part of the app needs to access  use


a Statefull widget and its setState () function to
handle such local states. 4
Ephemeral state & App state
• App state : is the state used across many parts
of the app. It is called shared state.

• Examples :

 Login info,
 Notifications in a social networking app,
 The shopping cart in an e-commerce app, …

 To handle such shared states  use State


Management approaches.

5
Ephemeral state & App state
• Ephemeral states issues :

1) Ng
2) J
3) J

6
Why State Management ?
• It is used to respond to ephemeral state
(Statefull) issues. It allows:

1) Separation between the UI and the business


logic.

2) Maximum possible improvement of the


performance (rebuild only widgets that need
to be update => fast & optimized).

3) Centralization of the state to be access from


different app parts.
7
State Management approaches
Illustrative example: MyApp

Basic Counter App MaterialApp

CounterPage

Counter = 0 CounterState
Build(context) SetState()
Scaffold
appBar
floatingButton
body
SetState(() {
AppBar Center Row ++Counter; })
child children
title Column FloatingButton FloatingButton

children child child


Text
Text(CounterVal=>$Counter) Icon(-) Icon(+) 8
State Management approaches
Illustrative example: MyApp

Basic Counter App MaterialApp

CounterPage

Counter = 0 CounterState

 View logic and business logic are combined.


Build(context) SetState()
 Counter state is available locally in the CounterPage.
Scaffold
appBar
 Each time setState is run, all widget elements are rebuild.
floatingButton
body
SetState(() {
AppBar Center Row ++Counter; })
child children
title Column FloatingButton FloatingButton

children child child


Text
Text(CounterVal=>$Counter) Icon(-) Icon(+) 9
State management approaches

State provider BLoC

bloc

cubit
10
State Management approaches: Provider
• Provider: is used to centralize a State within an
App. It is based on the Observer pattern.

Observable
State 2 - Change
3 - Notify

3 - Notify
Observer 1 Observer 2

11
State Management approaches: Provider
• In provider:
 The State is an object of a class that extends
ChangeNotifier (observable) class.
 notifyListener method is invoked to inform
listener widgets about the state change.
 MultiProvider widget is used to make State
available to all app components.
 ChangeNotifierProvider is used to provide an
instance of ChangeNotifier (instance of the state).
 Consumer (observer) widget allows a widget to
subscribe to listen to a State.
12
State Management approaches: Provider
Basic Counter App MyApp ChangeNotifier
ChangeNotifier
with State Provider Provider
MultiProvider CounterState
Counter = 0
MaterialApp Increment( );
Decrement( );

CounterState.Increment();
CounterState CounterPage

NotifyListeners
Build(context)
Scaffold
appBar
body floating
AppBar Center Button
title child Row
Text Column
children
children Subscribe Floating Floating
Consumer Button Button
child child
Build(context)
Text(CounterVal=>$Counter) Icon(-) Icon(+)
State Management approaches: Provider
• Implementation steps:

 Adding the provider package dependency.

 State creation.

 Expose the State.

 The use of the State: Consumer (listener)


& Provider.of (not listener).

14
State Management approaches: Provider
• Implementation steps: adding provider package.

 Run the command : flutter pub add provider

 Or, add the line : dependencies: provider: ^6.1.1 to the


pubspec.yaml file.

 Run: flutter pub get if needed.

 Import the package from the App:


import 'package:provider/provider.dart';

15
State Management approaches: Provider
• Implementation steps: state creation.
import 'package:flutter/material.dart';
class CounterState extends ChangeNotifier {
int _counter = 0; // private variable representing the state

void increment() {
+ +_ counter;
notifyListeners();
}

void decrement() {
- - _ counter;
notifyListeners();
}
// Getter method
get valueCounter => _counter;
}
16
State Management approaches: Provider
• Implementation steps: expose the state.

class MyApp extends StatelessWidget {


const MyApp({super.key});

@override
Widget build (BuildContext context) {

return MultiProvider(
providers: [ChangeNotifierProvider (create: (context) => CounterState())],
child: MaterialApp ( theme: ThemeData(useMaterial3: true,),
home: ProviderCounterPage(),
), //MaterialApp
); // MultiProvider
} // build
} // MyApp

17
State Management approaches: Provider
• Implementation steps: use of the state (Consumer).
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'provider_counter.dart';
class ProviderCounterPage extends StatelessWidget {
@override
Widget build (BuildContext context) { // This build method is executed only one time
return Scaffold(
appBar: AppBar( backgroundColor: Colors.pink,
title: Text('Counter App with Provider'),
), // AppBar
body: Center( child: Column(children: <Widget>[
Consumer<CounterState> ( builder: (context, CS , child) {
return Text( 'Counter Val =>${CS.valueCounter}',);
}, // builder function
), // Consumer
], // children ), // Center 18
State Management approaches: Provider
• Implementation steps: use of the state (Provider.of).
floatingActionButton: Row( children: [ It’s compulsory to
avoid rebuilding the
FloatingActionButton( entire widgets tree.
child: const Icon(Icons.add),
onPressed: () {
Provider.of<CounterState> (context, listen: false).increment(); // Access to the State
}, ), // FloatingActionButton

FloatingActionButton(
child: const Icon(Icons.remove),
onPressed: () {
Provider.of<CounterState> (context, listen: false).decrement(); // Access to the State
}, ), // FloatingActionButton

], // children ) // Row
); // Scaffold
} // build
19
State management approaches

State provider BLoC

bloc

cubit
20
State Management approaches: BLoC
• BLoC (Business Logic Component): a state
management solution created by Google based
on the concept of blocs, which are small,
isolated pieces of state (data) that can be easily
managed.
• It’s used to manage the state of individual
screens or components (widgets), and the state
of the entire application.
• A bloc is used to deal with the business logic
independently of the view logic.
21
State Management approaches: BLoC

• Bloc package provides two implementations:


BLoC & Cubit.

• Cubit : another state management solution


that follows the principles of the BLoC.

22
State Management approaches: BLoC
Send 10
I can’t
boats down
• Stream basic concept: seeriver
the you

You

Shout out Me
the
boad number
neither
when arrived

Your friend
23
State Management approaches: BLoC
• Stream basic concept:
I don’t know just
When will the boat listen and you’ll see
arrive ?
You = the sender

Your friend = the receiver

24
State Management approaches: BLoC
• Stream implementation:
// Implement the sender part
Stream <int> boatStream ( ) async* { // * for using stream
for (int i =1; i<=10;i++) {
await Future.delayed ( Duration (seconds:2));
yield i; // pushes data on the Stream
}
}

// Implement the receiver part


void main (List<String> args) async {
Stream <int> MyStream = boatStream (); //Initializing the stream
MyStream.listen (( receivedDate) {//listent to the stream to wait for
incoming data
print(" I have received the boat number $ receivedDate.toString()");
);
}
}
25
State Management approaches: BLoC
• Why Stream are needed ?

26
State Management approaches: BLoC
• BLoC VS Cubit: cubit is a minimal version of
bloc i.e. bloc is an extension of cubit.

cubit
bloc

27
State Management approaches: BLoC
• BLoC VS Cubit: both bloc and cubit can
communicate with the outer data layer.

states request

cubit Data
UI
functions response

Out of this course scope


states request

bloc Data
UI
response
events
28
State Management approaches: BLoC
• Cubit: is a special kind of stream component
that uses functions to communicate interactions
from widgets (UI) to the cubit, and a stream that
widgets consume for which state changes are
emitted. stream

states

UI cubit

functions

These functions are not part of


a STREAM 29
State Management approaches: BLoC
• Bloc: uses streams to handle incoming events
from the UI and sinks to emit output states.

stream

states

UI bloc
stream

events

30
State Management approaches: BLoC
• BLoC VS Cubit: overview.

Stream bloc Stream

Actions
UI state UI
(events) state

cubit Stream

Function
UI Calls
state UI
state

31
State Management approaches: BLoC
• BLoC VS Cubit: when should I use a bloc/cubit
in my app?
Key
functionnality
bloc
Every Should have
Feature
or

 Example: cubit
rebuild

32
State Management approaches: BLoC
• BLoC VS Cubit: when should I use a bloc/cubit
in my app?
Key
functionnality
bloc
Every Should have
Feature
or

 Example: cubit
rebuild

33
State Management approaches: BLoC
• BLoC VS Cubit: isn’t bloc enough for every
situations ? When should I use cubit as opposed
to bloc ?

 Technically, it is. But in some situations


there is no need of stream of events (as in
counter app features).

• When input stream becomes essential ?

 E.g. location in a weather app.

34
State Management approaches: BLoC
 E.g. location in a weather app.

with cubit the state is emitted for each tapped letter


=> bad performance

35
State Management approaches: BLoC
 E.g. location in a weather app.

with bloc the state can be delayed and manipulated to


be emitted, for example, when the user finish typing.

36
State Management approaches: BLoC
• Implementation: adding bloc package.

 Run the command : flutter pub add flutter_bloc

 Or, add the line : dependencies: flutter_bloc: ^8.1.3


to the pubspec.yaml file.

 Run: flutter pub get if needed.

 Import the package from the App:


import 'package:flutter_bloc/flutter_bloc.dart';
37
State Management approaches: BLoC
• Implementation of cubit (e.g. Counter app):

1.What is the initial state ? It’s 0.

2.What are the possible interactions that the user can have with the
APP ? Click on one of the + or – buttons.

3.What are the functions within Cubit ? Two functions:


increment() {state ++; } & decrement() {state--;}.

38
State Management approaches: BLoC
• Implementation of cubit (e.g. Counter app):
declaring the cubit responsible for managing the
counter state.

class CounterCubit extends Cubit <int> {

// set the default value of the state directly from the constructor
CounterCubit(int state ):super(state); // Or CounterCubit():super(0);
// Create functions that modify the state and emit it when they are called
from the UI.
void increment () => emit (state + 1) ;
void decrement () => emit (state - 1) ;

39
State Management approaches: BLoC
• Implementation of bloc (e.g. Counter app):

1.What is the initial state ? It’s 0.

2.What are the possible events that can occur for the feature ?
Click on one of the + or – buttons (increment event or
decrement event).

3.What states can result from these events ? the counter value is
changed (increased or decreased).

40
State Management approaches: BLoC
• Implementation of bloc (e.g. Counter app):
declaring the bloc component.
// Define the stream of events
enum CounterEvent {increment, decrement}
class CounterBloc extends Bloc < CounterEvent , int> {
CounterBloc( ):super(0);
@override
Stream <int> mapEventToState (CounterEvent env) async* {
switch (env){
case CounterEvent . increment:
yield state+1;
break;
case CounterEvent . decrement:
yield state+1;
break;
}
}
} 41
State Management approaches: BLoC
• Implementation of bloc (e.g. Counter app):
declaring the bloc component.

// Define the stream of events


enum CounterEvent {increment, decrement}

class CounterBloc extends Bloc < CounterEvent , int> {


CounterBloc( ):super(0);

on < CounterEvent.increment > (event, emit) {


emit (state+1);
});

on < CounterEvent.decrement > (event, emit) {


emit (state-1);
});

42
State Management approaches: BLoC
• Implementation: the use of Bloc/Cubit.

Stream bloc Stream

Actions
UI state UI
(events) state

cubit Stream

Function
UI Calls
state UI
state

43
State Management approaches: BLoC
• The use of BLoC : how do we link the BLoc to
the UI ? More concepts are needed.
RepositoryProvider

BlocProvider

BlocBuilder

cubit
bloc MultiBlocListener,
MultiBlocProvider,
MultiRepositoryProvider

BlocListener
BlocConsumer 44
State Management approaches: BLoC
• The use of BLoC : BlocProvider widget.
BlocProvider(
create: (context=>MyBloc(),
Bloc B = MyBloc(); child: widgetsBelow(),
);

Bloc B = MyBloc(); Bloc B = MyBloc();

Bloc B = MyBloc();
Bloc B = MyBloc(); Bloc B = MyBloc();

And closes
automatically the blocs.
Bloc B = MyBloc();

Bloc B = MyBloc();

Extremely WRONG The correct way 45


State Management approaches: BLoC
• The use of BLoC : BlocProvider widget.
BlocProvider(
create: (context=>MyBloc(),
Bloc B = MyBloc(); child: widgetsBelow(),
);

Bloc B = MyBloc(); Bloc B = MyBloc();


BlocProvider provides a
SINGLE INSTANCE of a
Bloc B = MyBloc();
Bloc B = MyBloc();
Bloc B = MyBloc(); BLoc to the subtree below it.

And closes
automatically the blocs.
Bloc B = MyBloc();

Bloc B = MyBloc();

Extremely WRONG The correct way 46


State Management approaches: BLoC
• The use of BLoC : BlocProvider availability.
Home screen BlocProvider.value (
value:BlocProvider.of<MyBloc>(context),
child: AnotherScreen(),);

BlocProvider
Propagates the ONLY
instance of the Bloc to
the new tree.
Push

Another screen

?
Because
The the context
Bloc instance is
won’t be closed
different and unknown
automatically when the
AnotherScreen
for gets destroyed.
the BlocProvider.
47
State Management approaches: BLoC
• The use of BLoC : BlocProvider implementation.
BlocProvider(
// use the function create to create a single instance of the bloc MyBloc
create: (BuildContext context =>MyBloc( ), // Context provides information on
the position of its widget in the widgets tree
child: childX( ),
); By default, BlocProvider creates the bloc
lazily => override by setting lazy:false.

• To access to the provider from the subtree, use:

BlocProvider.of<MyBloc> (context);
OR
context.read <MyBloc> ( );

48
State Management approaches: BLoC
• The use of BLoC : BlocProvider implementation.
@override Declaration step
Widget build (BuildContext context){
return BlocProvider(
cerate: (context) => CounterCubit ( ),
child: MaterialApp( title:"BlocProvider Demo",
home: CounterPage( ););
); // BlocProvider
} // build

class CounterCubit extends Cubit <CounterState> {


CounterCubit( ):super( CounterState (couterValue: 0 ));
void increment () => emit (CounterState (couterValue: state.couterValue+1));
void decrement () => emit (CounterState (couterValue: state.couterValue-1));
}

class CounterState { int couterValue;


CounterState ( {@required this.couterValue}); }
49
State Management approaches: BLoC
• The use of BLoC : BlocProvider implementation.

50
State Management approaches: BLoC
• The use of BLoC : BlocProvider implementation.
import 'package:flutter/material.dart'; Use step
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_cubit.dart';
class CounterPage extends StatelessWidget {
@override
Widget build (BuildContext context) {
return Scaffold(
appBar: AppBar( backgroundColor: Colors.pink,
title: Text('Counter App with BLoc'),
), // AppBar
body: Center( child: Column(children: <Widget>[
… … … ], // children ), // Center
51
State Management approaches: BLoC
• The use of BLoC : BlocProvider implementation.
class CounterPage extends StatelessWidget { Use step
@override
Widget build (BuildContext context) {
return Scaffold(
body: Center( child: Column(children: <Widget>[
FloatingActionButton (
onPressed: () {
BlocProvider.of<CounterCubit >
(context).decrement();
}, // onPressed
child: Icon (Icons.remove),
), // FloatingActionButton
], // children ), // Center 52
State Management approaches: BLoC
• The use of BLoC : BlocProvider implementation.
class CounterPage extends StatelessWidget { Use step
@override
Widget build (BuildContext context) {
return Scaffold(
body: Center( child: Column(children: <Widget>[
FloatingActionButton (
onPressed: () {
BlocProvider.of<CounterCubit >
With Bloc: context.read<CounterBloc >().add(CounterEvent.decrement);
(context).decrement();
}, // onPressed
child: Icon (Icons.remove),
), // FloatingActionButton
], // children ), // Center 53
State Management approaches: BLoC
• The use of BLoC : how to use the new value of
the state in the UI ?
RepositoryProvider

BlocProvider

BlocBuilder

cubit
bloc MultiBlocListener,
MultiBlocProvider,
MultiRepositoryProvider

BlocListener
BlocConsumer 54
State Management approaches: BLoC
• The use of BLoC : BlocBuilder widget.

• BlocBuilder: helps re-building the UI based on


bloc state changes.

cubit.increment() Text widget rebuild

new CounterState (2)

BlocBuilder
(re-build)

55
State Management approaches: BLoC
• The use of BLoC : BlocBuilder implementation-
wrap the widget to rebuild with BlocBuilder.
BlocBuilder <CounterCubit, CounterState > (
// cubit: BlocProvider.of<CounterCubit> (context);
builder: (context, state){ // A pure function
// return widget based on CounterCubit state.
return Text(
"The current counter’s value is $state.counterValue.toString();"
); //Text
}, // builder function
// return true/false to determine whether or not to rebuild the widget
with state.
buildWhen: (previousState, state){
if (previousState.value < state.value){ return false;}
), // BlocBuilder 56
State Management approaches: BLoC
• The use of BLoC : BlocBuilder implementation-
wrap the widget to rebuild with BlocBuilder.
BlocBuilder <CounterCubit, CounterState > (
// cubit: BlocProvider.of<CounterCubit> (context);
builder: (context, state){ // A pure function
The bloc/cubit may be omitted &
// return widget based on CounterCubit state.the instance will be searched via
return Text( BlocProvider in the widget tree.
"The current counter’s value is $state.counterValue.toString();"
); //Text
}, // builder function
// return true/false to determine whether or not to rebuild the widget
with state.
buildWhen: (previousState, state){
if (previousState.value < state.value){ return false;}
), // BlocBuilder 57
State Management approaches: BLoC
• The use of BLoC : BlocBuilder implementation-
wrap the widget to rebuild with BlocBuilder.
BlocBuilder <CounterCubit, CounterState > (
// cubit: BlocProvider.of<CounterCubit> (context);
builder: (context, state){ // A pure function
// return widget based on CounterCubit state.A pure function is a function where
return Text( the return value depends only on
"The current counter’s value is $state.counterValue.toString();"
the function’s parameters.
); //Text
}, // builder function
// return true/false to determine whether or not to rebuild the widget
with state.
buildWhen: (previousState, state){
if (previousState.value < state.value){ return false;}
), // BlocBuilder 58
State Management approaches: BLoC
• The use of BLoC :
RepositoryProvider

BlocProvider

BlocBuilder

cubit
bloc MultiBlocListener,
MultiBlocProvider,
MultiRepositoryProvider

BlocListener
BlocConsumer
59
State Management approaches: BLoC
• The use of BLoC : BlocListener widget.

• BlocListener: is used when the UI is fixed, but


there are actions (e.g. navigation, show a popup)
needs to be done when the state change.

// The same structure as BlocBuilder


BlocListener <CounterCubit, CounterState > (
listener: (context, state){
…. … …
} // listener function is called only once per state which
makes the difference with BlocBuilder.
listenWhen: .... // Determine listening or not for a state.
), // BlocListener 60
State Management approaches: BLoC
• The use of BLoC : BlocListener implementation-
showing a snackBar displaying which button has
been pressed.

BlocListener <CounterCubit, CounterState > (


listener: (context, state){
if (state.wasIncremented = = true) {
Scaffold.of(context).showSnackBar
(SnackBar(content:Text ('INCREMENT'),
duration:Duration(seconds:1),
), // SnackBar
); // showSnackBar } // if
} // listener function
), // BlocListener
61
State Management approaches: BLoC
• The use of BLoC : BlocListener implementation-
showing a snackBar displaying which button has
been pressed.
class CounterState {
int couterValue;
bool wasIncremented;

CounterState ( {@required this.couterValue, this. wasIncremented});


}

class CounterCubit extends Cubit <CounterState> {

CounterCubit( ):super( CounterState (couterValue: 0 ));

void increment () => emit (CounterState (couterValue: state.couterValue+1,


state.wasIncremented: true));
void decrement () => emit (CounterState (couterValue: state.couterValue-1,
state.wasIncremented: false));} 62
State Management approaches: BLoC
• The use of BLoC :
RepositoryProvider

BlocProvider

BlocBuilder

cubit
bloc MultiBlocListener,
MultiBlocProvider,
MultiRepositoryProvider

BlocListener
BlocConsumer
63
State Management approaches: BLoC
• The use of BLoC : BlocConsumer widget.

• BlocConsumer = BlocBuilder + BlocListener

BlocConsumer <CounterCubit, CounterState > (


listener: (context, state) {
// do stuff here based on CounterCubit state
}
builder: (context, state) {
// return widget here based on CounterCubit state

}
), // BlocConsumer

64
State Management approaches: BLoC
• The use of BLoC : BlocConsumer implementation.

BlocConsumer <CounterCubit, CounterState > (


listener: (context, state) {
if (state.wasIncremented = = true) {
Scaffold.of(context).showSnackBar
(SnackBar(content:Text ('INCREMENT'),
duration:Duration(seconds:1),
), // SnackBar
); // showSnackBar } // if
}
builder: (context, state) {
return Text( "The current counter’s value is
$state.counterValue.toString();"); //Text
}, // builder function
), // BlocConsumer 65
State Management approaches: BLoC
• The use of BLoC :
RepositoryProvider

BlocProvider

BlocBuilder

cubit
bloc MultiBlocListener,
MultiBlocProvider,
MultiRepositoryProvider

BlocListener
BlocConsumer
66
State Management approaches: BLoC
• The use of BLoC : RepositoryProvider widget.

• RepositoryProvider is similar to BlocProvider


widget, except it provides a single instance of a
repository to multiple widgets within a subtree
rather than a single instance of Bloc/Cubit.

• A repository is a class which plays the role of a


mediator between the bloc and data sources
(BDD, API, Internet,…).

67
State Management approaches: BLoC
• The use of BLoC :
RepositoryProvider

BlocProvider

BlocBuilder

cubit
bloc MultiBlocListener,
MultiBlocProvider,
MultiRepositoryProvider

BlocListener
BlocConsumer
68
State Management approaches: BLoC
• The use of BLoC : multiple blocs/cubits

1 bloc 1 bloc 1 bloc 1 bloc 1 bloc

BlocProvider BlocBuilder BlocListener BlocConsumer RepositoryProvider

What about more rich app


(app with multiple blocs ?

69
State Management approaches: BLoC
• The use of BLoC : multiple blocs/cubits

1 bloc 1 bloc 1 bloc 1 bloc 1 bloc

BlocProvider BlocBuilder BlocListener BlocConsumer RepositoryProvider

Multiple blocs Multiple blocs Multiple blocs

MultiBlocProvider MultiBlocListener MultiRepositoryProvider

70
State Management approaches: BLoC
• The use of BLoC : multiple blocs/cubits

MultiBlocProvider MultiBlocListener MultiRepositoryProvider

MultiBlocProvider{ MultiBlocListener{ MultiRepositoryProvider{


providers:[ listeners:[ providers:[
BlocProvider <BlocA> BlocListener <BlocA, RepositoryProvider
(cretae: (BuildContext BlocAState> <RepositoryA>
context ) => BlocA( ),), (listener: (context, state){ }, ), (cretae: (context ) =>
RepositoryA( ),),
BlocProvider <BlocB> BlocListener <BlocB,
(cretae: (BuildContext BlocBState> RepositoryProvider
context ) => BlocB ( ),), (listener: (context, state){ }, ), <RepositoryB>
…… …… (cretae: (context ) =>
] ] RepositoryB( ),),
child: … child: … …… ]
} } child: … }
71
State Management approaches: BLoC
• The use of BLoC : summary.

BlocProvider BlocBuilder BlocListener RepositoryProvider

Creates & Provides Re-build the UI Used when the UI Creates & Provides
THE ONLY FOR EVERY is fixed, but there THE ONLY
INSTANCE of a NEW STATE are actions needs INSTANCE of a
bloc/cubit to the coming from the to be done when repository to the
subtree. bloc/cubit. the state change. subtree.

BlocConsumer

72
State Management approaches: BLoC
MyApp Bloc
Basic Counter App state=0;
with BLoC BlocProvider

context.read.<CounterBloc>().add
CounterBloc
on<CounterEvent.increment>

(CounterEvent.increment)
MaterialApp
((event, emit )
{ emit (state+1); });
CounterPage

Stream of state
Scaffold
appBar
body floating
AppBar Center Button
title child Row
Text Column
children
children listen Floating Floating
BlocBuilder Button Button
child child
builder(context,state)
Text(CounterVal=>$Counter) Icon(-) Icon(+)
State Management approaches: BLoC
• Implementation steps:

1) Adding the flutter_bloc package dependency.

2) Select the feature to implement with bloc.

3) Answer the key questions (determine events and states).

4) State (bloc) implementation.

5) Expose the bloc (the state) by using BlocProvider, ...

6) Link the bloc to the UI => use the bloc (the state)
by using BlocConsumer, BlocBuilder, BlocListener ...
& Access to the bloc by triggering events. 74
State Management approaches: BLoC
• Implementation steps: adding the flutter_bloc
package.

 Run the command : flutter pub add flutter_bloc

 Or, add the line : dependencies: flutter_bloc : ^8.1.2 to the


pubspec.yaml file.

 Run: flutter pub get if needed.

 Import the package from the App:

import 'package:bloc/bloc.dart';
75
State Management approaches: BLoC
• Implementation steps: select the feature.

 Illustrative example "ToDoApp" :

 General feature : Manage the list of todos (tasks).


 Or consider a particular feature like :
"add a new task to the list".

 For each feature do the following steps




76
State Management approaches: BLoC
• Implementation steps: determine events and states.
Possible app events Possible app states

InitialtEvent ( ) InitialtState ( )

AddNewTaskEvent NewTaskAddedState ( )
(Task myNewTask) ToDoBloc
TodoListLoadedState
LoadTodoListEvent ( ) The state = ToDoList (List <Task> ToDoList)

ClearTodoListEvent ( ) TodoListEmptyState ( )

DeleteATaskEvent DeleteATaskState (List


(Task myTask) <Task> NewToDoList)

UpdateATaskEvent UpdateATaskState (List


(Task myTask) <Task> NewToDoList)
. .
. . 77
State Management approaches: BLoC
• Implementation steps: determine events and states.

class ToDoBloc extends Bloc AbstractClassStates


AbstractClassEvents
< AbstractClassEvents , isLoading
AbstractClassStates > error
{ToDoBloc () : super (InitialState()) {}} success

Possible app events


Possible app states
InitialtEvent ( )/
InitialtState ( )

AddNewTaskEvent
(Task myNewTask) NewTaskAddedState ( )

. .
. .
. .
78
State Management approaches: BLoC
• Implementation steps: determine events and states.

ToDoBloc

The state = AddANewTask

Possible app states

InitialtState ( )

Possible app events


SuccessState ( )

InitialtEvent ( ) FailState ( )

PressAddButtonEvent IsLoadingState ( )
(Task myNewTask) .
. 79
State Management approaches: BLoC
• Implementation steps: bloc implementation.
ToDoBloc

on< InitialtEvent > ((event,emit){ emit (InitialtState()) });


***********************************************************************************************************************
on< AddNewTaskEvent > ((event,emit){
AddTaskMethod (ToDoList, event.myNewTask); // processing the add operation
emit (NewTaskAddedState()) });
************************************************************************************************************************
on< LoadTodoListEvent > ((event,emit){
ToDoList = LoadToDoListMethod ( ); // processing the load operation
emit (TodoListLoadedState(ToDoList)) });
************************************************************************************************************************
on< ClearTodoListEvent > ((event,emit){
ToDoList = [ ]; // processing the clear operation
emit (TodoListEmptyState( )) });
************************************************************************************************************************
on< DeleteATaskEvent > ((event,emit){
DeleteTaskMethod (ToDoList, event.myTask); // processing the delete operation
emit (DeleteATaskState( )) }); 80
State Management approaches: BLoC
• Implementation steps: bloc implementation.

on< LoadTodoListEvent > ((event,emit){

*******************************************************************************************
emit (TodoListLoadedState([ ], isLoading: true)) });
*******************************************************************************************

Try{ // processing the load operation


ToDoList = LoadToDoListMethod ( );
emit (TodoListLoadedState(ToDoList , isLoading: false, success: true)) });
} *****************************************************************************************

catch (e) {
emit (TodoListLoadedState([ ], isLoading: false, error: e.toString() )) });
*******************************************************************************************
}
81
State Management approaches: BLoC
• Implementation steps: expose the bloc with
BlocProvider.
BlocProvider< ToDoBloc > (

create: (BuildContext context ) => ToDoBloc ( ) . . add (InitialEvent ( )) ,


child: MaterialApp(....... ),

);

MultiBlocProvider (
providers: [ BlocProvider< ToDoBloc > (
create: (BuildContext context ) => ToDoBloc ( ) . . add (InitialEvent ( )) ,
],
child: MaterialApp(....... ),
);

82
State Management approaches: BLoC
• Implementation steps: use the bloc (BlocConsumer,
BlocBuilder, BlocListener).

BlocBuilder <ToDoBloc, AbstractClassStates > (


builder: (context, state){

if (state.isLoading){return Text("the state is loading");}


else if (state.success) {return ListView.builder
(itemCount: ToDoList.length, itemBuilder: ... ); }
else if (ToDoList.isEmpty) {return Text("No tasks found ");}
} // builder

), // BlocBuilder

83
State Management approaches: BLoC
• Implementation steps: access to the bloc by
triggering events.

IconButton (
icon: Icon(Icons.add),
onPressed: (){
context.read<ToDoBloc >().add(AddNewTaskEvent (Task myNewTask));
}
);

84
Conclusion
• BLoC design pattern offers a solution to
separate the business logic from the user
interface.

• It is based on the concept of streams to transmit


data asynchronously between the business logic
and the user interface.

• Answering key questions such as : the initial


state, possible interactions, and functions
available in Cubit, … => structure the app in a
modular way => easier to maintain and scale
the code.
85
Conclusion
• BlocProvider is the widget used to inject BLoC
instances into widget tree.

• BlocBuilder, BlocListener & BlocConsumer


are the widgets used to communicate with BLoC
from the user interface to react to state changes
and update the user interface in an efficient
manner.

• Cubit is a minimal version of bloc that offers a


simpler approach to certain features.
86
Conclusion
• Go further with other state management
solutions :

 Riverpod,
 GetX,
 StateX,
 ….

87

You might also like