Bloc: is it possible to yield 2 time the same state? - flutter

In the login view, if the user taps on the login button without having inserted his credentials, the LoginFailState is yield and the view reacts to it. If he taps again, this LoginFailstate is yield again, but the view doesn't react to it. So, is there a way to yield more times the same state?
There is some code to better explain my situation:
class LoginBloc extends Bloc<LoginEvent, LoginState> {
#override
LoginState get initialState => LoginUninitialized();
#override
Stream<LoginState> mapEventToState(LoginEvent event) {
if (event is loginButtonPressed) {
yield LoginFailState();
}
}
View:
#override
Widget build(BuildContext context) {
return BlocBuilder(
bloc: _loginBloc,
builder: (BuildContext context, LoginState state) {
if (state is LoginFail) {
print ('Login fail');
}
return Column(
...
)

You can receive an update for the "same" State if you don't extend Equitable, or implement your own '==' logic which makes the two LoginFailStates equal.
The solution is to yield a different State in between, like in the Bloc example.
yield LoginLoading();
It gets called on every login button tap. Felangel's LoginBloc example.

By default BLoC pattern will not emit state when the same state will be passed one after another. One way to do this is to pass your initial BLoC state after passing LoginFailState.
So after user clicks on the button with wrong credentials passed states will not be:
LoginFailState()
LoginFailState()
but
LoginFailState()
LoginEmptyState()
LoginFailState()
LoginEmptyState()
Which will make UI react to each of them.
But I think that the best and cleanest solution is to pass LoadingState from BLoC before passing LoginFailState().
You can follow the blog post that I have recently written regarding this topic.

Problem
When you try to emit a new state that compares equal to the current state of a Bloc, the new state won't be emitted.
This behavior is by design and is discussed here.
When I say "compares equal" I mean that the == operator for the two state objects returns true.
Solution
There are two proper approaches:
Your state class should NOT extend Equatable. Without Equatable, two objects of the same class with the same fields will NOT compare as equal, and this new state will always be emitted.
Sometimes you need your state class to extend Equatable. In this case, just add the result of the identityHashCode(this) function call to your props getter implementation:
class NeverEqualState extends Equatable {
#override
List<Object?> get props => [identityHashCode(this)];
}
Note that I use identityHashCode that works regardless the operator == is overloaded or not. In contrast, hashCode will not work here.
Warning:
Do not use random values in the getter implementation List<Object> get props => [Random().nextDouble()];. Random variables are random, meaning that with extremely low probability you still might get two equal values in a sequence that will break this workaround. This is extremely unlikely, so it's not possible to reproduce and debug this.
You can and should include fields in your get props implementation, but keep in mind that when all fields compare as equal the objects will also compare as equal.
Emitting some other state in-between two equal states works but it forces your BlocBuilder to rebuild part of UI and BlocListener to execute some logic. It's just inefficient.
Finally, why would you like to have a state class extend Equatable but still not compare equal? This might be needed when your state class is actually the root of a hierarchy, where some descendants need to implement the == operator properly, and some need to never compare equal. Here is the example:
class BaseMapState extends Equatable {
const BaseMapState();
#override
List<Object?> get props => [];
}
class MapState extends BaseMapState {
final Map<String, Report> reports;
final Report? selectedReport;
final LatLng? selectedPosition;
final bool isLoadingNewReports;
const MapState(
{this.reports = const {},
this.selectedReport,
this.selectedPosition,
this.isLoadingNewReports = false});
#override
List<Object?> get props => [
...reports.values,
selectedReport,
selectedPosition,
isLoadingNewReports
];
}
class ErrorMapState extends BaseMapState {
final String? error;
const ErrorMapState(this.error);
#override
List<Object?> get props => [identityHashCode(this), error];
}
class NeedsAuthMapState extends ErrorMapState {
const NeedsAuthMapState() : super('Authentication required');
}
class NoInternetMapState extends ErrorMapState {
const NoInternetMapState() : super("No Internet connection");
}

If you use Equitable and tries to emit two equal instances of the same State with different properties, make sure that you override props array. By overriding props array, Equitable will know how to compare state instances.
class TablesLoadedState extends Equatable {
final List<TableEntity> tablesList;
TablesLoadedState(this.tablesList);
#override
List<Object> get props => [tablesList];
}
So, when bloc emits two instances of the same state with different values, these state-instances will be passed to BlocListener and UI will be updated according to new data.

A late possible workaround would be adding a random double to the state get props, this way the state won't be equal and you can yield them one after the other if you want.
also, Random().nextDouble() complexity is O(1) so you don't need to worry about performance
class LoginFailState extends LoginState {
#override
List<Object> get props => [Random().nextDouble()];
}

Related

using Equatable class with flutter_bloc

Why do we need to use the Equatable class with flutter_bloc? Also, what do we use the props for? Below is sample code for making a state using the bloc pattern in Flutter.
abstract class LoginStates extends Equatable{}
class LoginInitialState extends LoginStates{
#override
List<Object> get props => [];
}
For comparison of data, we required Equatable. it overrides == and hashCode internally, which saves a lot of boilerplate code. In Bloc, we have to extend Equatable to States and Events classes to use this functionality.
abstract class LoginStates extends Equatable{}
So, that means LoginStates will not make duplicate calls and will not going to rebuild the widget if the same state occurs.
Define State:
class LoginInitialState extends LoginStates {}
Define State with props:
props declared when we want State to be compared against the values which declared inside props List
class LoginData extends LoginStates {
final bool status;
final String userName;
const LoginData({this.status, this.userName});
#override
List<Object> get props => [this.status, this.userName];
}
If we remove the username from the list and keep a list like [this.status], then State will only consider the status field, avoiding the username field. That is why we used props for handling State changes.
Bloc Stream Usage:
As we extending State with Equatable that makes a comparison of old state data with new state data. As an example let's look at the below example here LoginData will build a widget only once, which will avoid the second call as it is duplicated.
#override
Stream<LoginStates> mapEventToState(MyEvent event) async* {
yield LoginData(true, 'Hello User');
yield LoginData(true, 'Hello User'); // This will be avoided
}
Detail Blog: https://medium.com/flutterworld/flutter-equatable-its-use-inside-bloc-7d14f3b5479b
We are using the Equatable package so that we can compare instances of classes without having to manually override "==" and hashCode.
Equatable class allows us to compare two object for equality.
This is the equatable example. Let's say we have the following class:
class Person {
final String name;
const Person(this.name);
}
We can create instances of Person like so:
void main() {
final Person bob = Person("Bob");
}
Later if we try to compare two instances of Person either in our production code or in our tests we will run into a problem.
print(bob == Person("Bob")); // false
In order to be able to compare two instances of Person we need to change our class to override == and hashCode like so:
class Person {
final String name;
const Person(this.name);
#override
bool operator ==(Object other) =>
identical(this, other) ||
other is Person &&
runtimeType == other.runtimeType &&
name == other.name;
#override
int get hashCode => name.hashCode;
}
Now if we run the following code again:
print(bob == Person("Bob")); // true
it will be able to compare different instances of Person.
So you don't have to waste your time writing lots of boilerplate code when overrides "==" and hashCode.
Use Equatable like
class Person extends Equatable
In bloc case; if you try to use bloc with mutable state you will face with problems without Equatable. It makes resources immutable reduce performance. It’s more expensive to create copies than to mutate a property.
If it is not clear to you that I tried to explain, reading this could help you.
Equatable overrides == and hashCode for you so you don't have to waste your time writing lots of boilerplate code.
There are other packages that will actually generate the boilerplate for you; however, you still have to run the code generation step which is not ideal.
With Equatable there is no code generation needed and we can focus more on writing amazing applications and less on mundane tasks.
and the props is a getter of equatable that takes the properties that we want to
although it needs no focus on it i only putted properties to props getter
it is not that Important but i suggest you to read more about it in here

Why we should use Equatable in Flutter Bloc?

I understand that Equatable helps to compare two instances of object without doing it manually.
But where exactly I can use it in Flutter Bloc?
Here is the example of usage Equatable:
Where it could be useful?
abstract class TodosState extends Equatable {
const TodosState();
#override
List<Object> get props => [];
}
class TodosLoadInProgress extends TodosState {}
class TodosLoadSuccess extends TodosState {
final List<Todo> todos;
const TodosLoadSuccess([this.todos = const []]);
#override
List<Object> get props => [todos];
#override
String toString() => 'TodosLoadSuccess { todos: $todos }';
}
class TodosLoadFailure extends TodosState {}
Object and data comparison is always hard to do when it comes to stream as we need to decide state updation based on it.
we required Equatable as it overrides == and hashCode internally, which saves a lot of boilerplate code. In Bloc, we have to extend Equatable to States and Events classes to use this functionality.
abstract class TodosState extends Equatable {}
So, that means TodosState will not make duplicate calls and will not going to rebuild the widget if the same state occurs.
Let's see props usage in Equatable and what makes it special
Define State without props:
class TodosLoadSuccess extends TodosState {}
Define State with props:
props declared when we want State to be compared against the values which declared inside props List
class TodosLoadSuccess extends TodosState {
final String name;
final List<Todo> todos;
const TodosLoadSuccess([this.name, this.todos = const []]);
#override
List<Object> get props => [name, todos];
}
If we remove the name from the list and keep a list like [this.todos], then State will only consider the todos field, avoiding the name field. That is why we used props for handling State changes.
Bloc Stream Usage:
As we extending State with Equatable that makes a comparison of old state data with new state data. For example, let's look at the below example here TodosState will build a widget only once, which will avoid the second call as it is duplicated.
#override
Stream<TodosState> mapEventToState(MyEvent event) async* {
final List<Todo> todos = [Todo(), Todo()];
yield TodosLoadSuccess(todos);
yield TodosLoadSuccess(todos); // This will be avoided
}
Detail Blog: https://medium.com/flutterworld/flutter-equatable-its-use-inside-bloc-7d14f3b5479b
I think it is useful for comparing what state is in BlocBuilder.
Below code is a good example of using Equatable.
if(state is [Some State])
#override
Widget build(BuildContext context) {
return BlocBuilder<SongsSearchBloc, SongsSearchState>
bloc: BlocProvider.of(context),
builder: (BuildContext context, SongsSearchState state) {
if (state is SearchStateLoading) {
return CircularProgressIndicator();
}
if (state is SearchStateError) {
return Text(state.error);
}
if (state is SearchStateSuccess) {
return state.songs.isEmpty
? Text(S.EMPTY_LIST.tr())
: Expanded(
child: _SongsSearchResults(
songsList: state.songs,
),
);
} else {
return Text(S.ENTER_SONG_TITLE.tr());
}
},
);
}

Reducing verbosity of states in flutter_bloc library

I'm implementing a simple chat screen that lists messages and has a textbox and button to send.
I have the following states:
abstract class ChatState extends Equatable {
const ChatState();
#override
List<Object> get props => [];
}
class ChatFailure extends ChatState {
final Exception reason;
const ChatFailure({this.reason});
#override
List<Object> get props => [reason];
}
class ChatInProgress extends ChatState {}
class ChatSuccess extends ChatState {
final Chat chat;
final List<Message> messages;
final Map<int, User> users;
const ChatSuccess({
#required this.chat,
#required this.messages,
#required this.users,
});
#override
List<Object> get props => [chat, messages, users];
}
However, I now want to be able to show the messages in all the states (if not null), even error or loading states - it might be displaying a cache.
It seems I have two ways to do this, but neither seem like a good option:
Make ChatState the only state, and shove everything in there. Seems like an anti-pattern.
Put chat, messages and users in ChatState so all the states have them. However, then every state has a huge long constructor and it's really verbose, especially as this screen will become more complex.
I'm going with 2 for now, but is that really the best way? Am I using bloc_library in the correct way?
Since the suggestion from post comment seems reasonable, I'm moving it here:
If we consider the following business logic in steps:
User enters a screen, he sees the loading indicator (data loads here)
If there was no error, he now sees a view with all the data fetched earlier
He can press a button/pull to refresh the data
In that case I would suggest adding a showLoading boolean to ChatSuccess state. This way you display data and also a spinning indicator at the same time.
The flow with the states would be following:
ChatInProgress -> ChatSuccess(firstData, showLoading: false) -> (pull to refresh) -> ChatSuccess(firstData, showLoading: true) -> ChatSuccess(secondData, showLoading: false)

How to clear data from Streams of blocpattern?

My this function isn't clearing the cart.
clearCart(){
_listController.close();
}
Am I supposed to call some other property or implement some other approach in clear cart function?
Here is my CartListBloc code:
class CartListBloc extends BlocBase {
CartListBloc();
var _listController = BehaviorSubject<List<FoodItem>>.seeded([]);
//provider class
CartProvider provider = CartProvider();
//output
Stream<List<FoodItem>> get listStream => _listController.stream;
//input
Sink<List<FoodItem>> get listSink => _listController.sink;
addToList(FoodItem foodItem) {
listSink.add(provider.addToList(foodItem));
}
removeFromList(FoodItem foodItem) {
listSink.add(provider.removeFromList(foodItem));
}
clearCart(){
// What should I put here to clear the bloc of Streams from cart
}
//dispose will be called automatically by closing its streams
#override
void dispose() {
_listController.close();
super.dispose();
}
}
The Stream class has a drain method, which removes all data from a Stream. However, you seem to be trying to clear a BehaviorSubject so you can't use drain (It doesn't actually clear the subject). Instead, you should probably simply add an empty List or null (in which case you need to deal with this null in your UI) to _listController, which will give you a new event with no items.
Edit:
Example:
_listController.add([]); //Now your listeners will receive
//new event with empty list of items

Where and when should I store values in BLoC itself or its state

Context
I come from Redux and I am learning the BLoC pattern for global state management. I am having troubles defining where should I store values as properties inside the BLoC class, and when I should store values inside States.
Use case
I have a home page where I show some Ads, depending on its category (category should never be null). I implemented an AdsBloc like this:
class AdsBloc extends Bloc<AdsEvent, AdsState> {
final AdsRepository repository;
AdsBloc({#required this.repository})
: super(AdsInitial(category: Category.DOGS));
#override
Stream<AdsState> mapEventToState(
AdsEvent event,
) async* {
// my code
}
}
And these are my AdsState:
abstract class AdsState {
final Category category;
AdsState({this.category});
}
class AdsInitial extends AdsState {
AdsInitial({category}) : super(category: category);
}
class AdsSuccess extends AdsState {
final PaginatedAds paginatedAds;
AdsSuccess({this.paginatedAds, category}) : super(category: category);
}
class AdsFailure extends AdsState {
AdsFailure({category}) : super(category: category);
}
Problem
Because of the way I implemented the pattern, I need to pass the category every time I change the state.
Solution?
So I was thinking if I can consider the category property to be of AdsBloc and remove it from state, this way I can get a better control of this property.
Implement "category" as a parameter for the event that is triggering ad loading process. This way, for example, you will tell BLoC to "LoadAds" with "category: Category.DOGS".
class LoadAds extends AdsEvent {
final Category category;
LoadAds({
#required this.category,
});
#override
List<Object> get props => [category];
}
This way you will be able to use single bloc instance to load different ad types when needed. Load DOGS first, 2 minutes later load CATS instead of dogs.
If you do not need this ability - then defining category inside bloc itself is perfectly fine.