# Flutter State Management
Reactive Programming Streams - BLoC Patern (opens new window)
cubit is now merged into bloc library
# BLoC vs flutter_block
https://bloclibrary.dev/#/gettingstarted (opens new window)
- BloC Architecture
- BloC context
- BloC Testing
# Why BLoC?
App should be able to handle EVERY interaction
- Business Logic Component , pattern created by google for presentation layer
- App should have a state for EVERY possible Everything
- if their is not Internet, show them
- if data could not be fetched, show them it (re)-trying
- STREAMS are the foundation of BLoC
- Asynchronous code sent in parts
note
GOOD Application must have a valid STATE VALUE at all times.
A StatefulWidget does not scale to larger applications. The BLOC pattern does.
# Why Streams?
Stream<int> someStream() async* {
for (int i = 1; i <= 10; i++>) {
print("SENT number = " + i.toString());
await Future.delayed(Duration(seconds: 2));
yield i;
}
}
void main(List<string> args) async {
Stream<int> stream = boatStream();
stream.listen((receivedData) {
print("Recieved number" + receivedData.toString());
});
}
# Cubit
- is a special type of Stream component which updates UI on state change
- emit event on stream using yield
- cubit is a component based of some functions
- functions that are not part of the stream
- cubit only emits streams of states to UI
- you can subscribe and listen to cubit states changes in components
- BLoC also recieves stream of events and emits stream of states
- To use BLoc or cubit, you first have to declare it.
difference | description |
---|---|
Cubit | streaming states + Functions events that are NOT streams |
BloC | streaming states + Streaming events |
# BLoC Widget
- use BlocProvider Widget when it has to be used at multiple places
- Dependency Injection widget
- same idea as React.contenxt Provide API
- Wrap the EXACT part to be re-built inside the
BlocBuilder
# Flutter BloC Concepts
bloc | description |
---|---|
BlockProvider | Accessable with a Widget tree, Dependency Injection in BloC Tree |
BlockBuilder | re build the UI on state change |
BlockListener | only listen for change, single time unlike BlockBuilder |
BlocConsumer | mixture of BlockListener and BlockProvider |
- BlockProvider are created lazily
- to access use
BlockProvider.of<myCubit>(context).myFunction();
- Annotate required sates as
@required
- Wrap only the part yu want to update inside
BlockBuilder<myCubit, myState>()
BlockListener<myCubit, myState>(listener: () {})
on top of BlockBuilderBlocConsumer(lstener: ..., builder: ...)
- The listener is guaranteed to only be called once for each state change unlike the builder in BlocBuilder.
- Repository -> classes which provides dart to Data Layer DAL
- transformations like filter, sort before passing data to BloC
- Local vs Route BloC Access
# Steps
When to BloC?
Every dynamic feature in app should have its own BloC
- Define a block by overriding its methods and then initialize it by calling its super
- Register Events on BloC using
on
API- define state changes in
on
callback function which acceptsevent
andemit
- modified states changes are
emitted
- define state changes in
- Using the Block in UI Widgets
- BlockProvide creates a instance of a bloc and all it's chold have access
- create an instance of BlocComponent as
final
and then call events on it usingadd
API - wait for events to deliver a response
await Future.delayed(Duration.zero)
wait for evnt-loop
await Future.delayed(Duration.zero)
is added to ensure we wait for the
next event-loop iteration (allowing the EventHandler to process the event).
- For RT subscriptions to changes, use Streams >> Futures
Future<void> main() async {
final bloc = CounterBloc();
final subscription = bloc.stream.listen(print); // 1
bloc.add(Increment());
await Future.delayed(Duration.zero);
await subscription.cancel();
await bloc.close();
}
# Enterprise App source organization
- data
- models
- repositories
- dataProviders
- business_logic
- bloc
- cubit
- presentation
- widgets
- pages
- screens
- animations
- routes
- Theme, ColorPallet
# questions
- When to use cubit and when to use bloc?
- Each feature should have a cubit or bloc to manage/control it.
- Difference is in how they recieve information
- cubits are cheaper than bloc
- Should I have a BLoC for each UI componet?
- Having multiple (many) instances of same BLoC is a terrible idea
- What is repository in flutter?
- communicating with external data-layers , DAL, network-calls, databases etc.
- How to start building a Application?
- start with appdb, or application modal
- Application Modal should be independent from the source
- How to structure your source code?
- seperate directory for each layer and sub-layer
- How to make internet connection aware mobile application?
- connectivity_plus package
# Build context
Every block must override the mapEventToState
function. This is a generator function, which accepts event
and returns a stream, that notifies of state change whatever is subscribed to it.
In redux world it is reducer.
- everything in flutter is a
Widget
- every widget in flutter has a
build
function - every build method is built from a
context
. So Every widget has its own context - in flutter
MyApp
widget is theroot
of widget tree of an application - build content ONlY knows about its parent context, bottoms-up, there is no other way
- new screens are pushed on widget tree and old routes are
popped
out from tree - Use Flutter devtools to visualize widget tree and context
How multiple context interact with each other?
WARNING
Never mutate and emit state from inside of BLoC. Let UI emit the events
# Hydrated Block
- restore app-data/state from localstorage
- fast and close to application
- uses hive under the-hood which is blazingly fast
# Debugging BLoC
- How to override all the
onChange
,onError
,onTransition
,onEvent
methods on BLoC ?- onCreated
- onClose
BlocObserver
BlocObserver
When we want to be able to do something in response to all Changes we can simply create our own BlocObserver.
# Data Fetching over network
data fetching in build method
Although itโs convenient, itโs not recommended to put an API call in a build()
method.
- Make networl call using
http
library using Futures is easy, but working with response is hard - Transform http.response --> Dart Object, using Models in repository
- Flutter data fetching (opens new window)
- Whent to make the netwrk call?
- exactly once?
initState
MyApp
widget becomes stateful when app wants data from API on initial load
- based on event emitted?
- exactly once?
- How to display the data on UI?
FutureBuilder
widget
- How to use an existing BloC on abother page?
BlocProvide.value()
constructor
# Use-cases
# Load a new view/page on a click
MaterialButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => BlocProvider.value(
value: existingCubit(); // NOT A new cubit
value: BlockProvider.of<yourCubitName>(context);
child: ....
)
)
)
}
)
# Search and filter items widget
as the user type. Use debounce feature to wait for sometime before making API call
# Best Practises
- keep BloC in seperate file for testability
- write BloC per feature
# Sample Repos
โ Flutter Plublish Functions โ