Cours8 StateManagement
Cours8 StateManagement
Mobile applications
-Flutter framework-
2
Plan
• Ephemeral states & app states.
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,
…
• Examples :
Login info,
Notifications in a social networking app,
The shopping cart in an e-commerce app, …
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:
CounterPage
Counter = 0 CounterState
Build(context) SetState()
Scaffold
appBar
floatingButton
body
SetState(() {
AppBar Center Row ++Counter; })
child children
title Column FloatingButton FloatingButton
CounterPage
Counter = 0 CounterState
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:
State creation.
14
State Management approaches: Provider
• Implementation steps: adding provider package.
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.
@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
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
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
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
}
}
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
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
stream
states
UI bloc
stream
events
30
State Management approaches: BLoC
• BLoC VS Cubit: overview.
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 ?
34
State Management approaches: BLoC
E.g. location in a weather app.
35
State Management approaches: BLoC
E.g. location in a weather app.
36
State Management approaches: BLoC
• Implementation: adding bloc package.
2.What are the possible interactions that the user can have with the
APP ? Click on one of the + or – buttons.
38
State Management approaches: BLoC
• Implementation of cubit (e.g. Counter app):
declaring the cubit responsible for managing the
counter state.
// 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):
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.
42
State Management approaches: BLoC
• Implementation: the use of Bloc/Cubit.
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();
And closes
automatically the blocs.
Bloc B = MyBloc();
Bloc B = MyBloc();
And closes
automatically the blocs.
Bloc B = MyBloc();
Bloc B = MyBloc();
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.
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
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
(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.
BlocProvider
BlocBuilder
cubit
bloc MultiBlocListener,
MultiBlocProvider,
MultiRepositoryProvider
BlocListener
BlocConsumer
63
State Management approaches: BLoC
• The use of BLoC : BlocConsumer widget.
}
), // BlocConsumer
64
State Management approaches: BLoC
• The use of BLoC : BlocConsumer implementation.
BlocProvider
BlocBuilder
cubit
bloc MultiBlocListener,
MultiBlocProvider,
MultiRepositoryProvider
BlocListener
BlocConsumer
66
State Management approaches: BLoC
• The use of BLoC : RepositoryProvider widget.
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
69
State Management approaches: BLoC
• The use of BLoC : multiple blocs/cubits
70
State Management approaches: BLoC
• The use of BLoC : multiple blocs/cubits
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:
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.
import 'package:bloc/bloc.dart';
75
State Management approaches: BLoC
• Implementation steps: select the feature.
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 ( )
AddNewTaskEvent
(Task myNewTask) NewTaskAddedState ( )
. .
. .
. .
78
State Management approaches: BLoC
• Implementation steps: determine events and states.
ToDoBloc
InitialtState ( )
InitialtEvent ( ) FailState ( )
PressAddButtonEvent IsLoadingState ( )
(Task myNewTask) .
. 79
State Management approaches: BLoC
• Implementation steps: bloc implementation.
ToDoBloc
*******************************************************************************************
emit (TodoListLoadedState([ ], isLoading: true)) });
*******************************************************************************************
catch (e) {
emit (TodoListLoadedState([ ], isLoading: false, error: e.toString() )) });
*******************************************************************************************
}
81
State Management approaches: BLoC
• Implementation steps: expose the bloc with
BlocProvider.
BlocProvider< ToDoBloc > (
);
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
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.
Riverpod,
GetX,
StateX,
….
87