totally, I started a few time and I kind of lost on how to do some simples things with bloc, normally, things that suppost to happen in a multi-page app (real life, right?). I am coming from Xamarin, and in MVVM every thing seens so simple and connected rsrsrs
Since the previous way shown in the video no longer works for later Bloc versions, this is what worked for me on Flutter Bloc 8.1.1 test('should emit [Error] when input is invalid', () async* { when(mockInputConverter.stringToUnsignedInteger(any)) .thenReturn(Left(InvalidInputFailure())); final expected = [ Empty(), const Error(message: INVALID_INPUT_FAILURE_MESSAGE) ]; expectLater(bloc.stream.asBroadcastStream(), emitsInOrder(expected)); bloc.add(const GetTriviaForConcreteNumber(tNumberString)); }); My NumberTriviaBloc class looks like this; class NumberTriviaBloc extends Bloc { final GetConcreteNumberTrivia getConcreteNumberTrivia; final GetRandomNumberTrivia getRandomNumberTrivia; final InputConverter inputConverter; NumberTriviaBloc({ required GetConcreteNumberTrivia concrete, required GetRandomNumberTrivia random, required this.inputConverter }) : getConcreteNumberTrivia = concrete, getRandomNumberTrivia = random, super(Empty()) { on (numberTriviaEventObserver); } FutureOr numberTriviaEventObserver(event, emit) async* { if (event is GetTriviaForConcreteNumber) { final inputEither = inputConverter.stringToUnsignedInteger(event.numberString); yield* inputEither.fold( (failure) async* { yield const Error(message: INVALID_INPUT_FAILURE_MESSAGE); }, (integer) => throw UnimplementedError()); } } }
if your test still fails then change to the following : test('should emit [Error] when the input is invalid', () async { // arrange when(mockInputConverter.stringtoUnsignedInt(any)) .thenReturn(Left(InvalidInputFailure())); // assert later final expected = [ //Empty(), Error(message: INVALID_INPUT_FAILURE_MESSAGE), ]; expectLater(bloc.stream, emitsInOrder(expected)); // act bloc.add(GetTriviaForConcreteNumber(tNumberString)); });
if your following this tutorial later after the bloc update. bloc.state does not return a list of states, but the latest state. To add a stream type. expectLater( bloc.asBroadcastStream(), emitsInOrder(expected), );
I have solved in that way: expectLater( bloc.asBroadcastStream(), emits(Error(message: INVALID_INPUT_FAILURE_MESSAGE)), ); i don't know why he doesn't emit the first state
I got an error while testing the Error. I believe it was because of Bloc 1.0.0 new release. Basically bloc.state returns the current state and the test failed reporting 'ERROR: Expected: should do the following in order: * emit an event that Empty: * emit an event that Error: Actual: Empty: Which: was not a Stream or a StreamQueue' I solved it by changing bloc.state to bloc.cast()
With the new flutter_bloc release you just need to use bloc not bloc.state or bloc.cast() ResoCoder just made a good primer video for the bloc_test package that touches on these points and as always is great!
For a better readability I renamed classes ans objects instance: GetRandomNumberTrivia to GetRandomNumberTriviaUseCase and randomNumberTriviaUseCase GetConcreteNumberTrivia to GetConcreteNumberTriviaUseCase and concreteNumberTriviaUseCase
I don't know if it's just me, but I feel like this approach is really excellent for a one man team. If you have to add members to that team, you'd really have to sit them down and teach them about this approach thereby losing production time, I don't know...great series though.
That's a reality that exists in most development contexts. Now there is this video series that you can tell them to go through, so everyone can be on the same page.
Hi 'Sensei' thanks for wonderful work !! , by the way , when you add bloc.dispatch , currently it isn't on bloc, because In v1.0.0 of the bloc library, all blocs are now Sinks. This means we can start using the traditional add sink api in order notify the bloc of a new event instead of dispatch.
or you can do some changes like this : test('should emit [Error] when the input is invalid', () async { // arrange when(mockInputConverter.stringtoUnsignedInt(any)) .thenReturn(Left(InvalidInputFailure())); // assert later final expected = [ //Empty(), Error(message: INVALID_INPUT_FAILURE_MESSAGE), ]; expectLater(bloc.stream, emitsInOrder(expected)); // act bloc.add(GetTriviaForConcreteNumber(tNumberString)); }); Important: First of you are still facing error is because you have to implement right Side of the fold also: if (event is GetTriviaForConcreteNumber) { final inputorerror = inputConverter.stringtoUnsignedInt(event.numberString); yield* inputorerror.fold((l) async* { yield Error(message: INVALID_INPUT_FAILURE); }, (r) async* { concreteNumberTrivia(Params(number: r)); }); }
you should remake this tutorial with the current version of bloc. I saw your github project for this tutorial the bloc dependecy still at v5.0.0 compared to current bloc v6.0.1
Hi thanks for this tutorial. I was wondering why did you pass usecases dependencies one by one for the bloc implementation and not directly the repository Interface ? Can't really understand the role of usescases.... Seems like another layer of abstraction but what for ? Thanks again !
For the last test, since I try to find minimum changes needed in order for the test to work. just change async to async* in number_trivia_bloc_test.dart for that test. async => gives you a Future async* => gives you a Stream
Excellent video tutorial and my only feedback might be that the flutter_bloc library has changed so rapidly that a few parts just didn't work. such as bloc.dispatch is no longer a bloc method. Since the making of this video, less than 2 months ago, flutter_bloc has gone from beta to v1.0.0 to now v2.0.0. Perhaps a single video update on this library alone might be enough.
the new code for the updated version of bloc test('should emit [error] when the input is invalid', () { when(() => mockInputConverter.stringToUnsignedInt(any())) .thenReturn(Left(InvalidInputFailure())); final expected = [ // Empty(), Error(message: INVALID_INPUT_FAILURE_MESSAGE), ]; expectLater(bloc.stream.asBroadcastStream(), emitsInOrder(expected)); bloc.add(GetTriviaForConcreteNumber(tNumberString)); });
I'd like to know if it is possible to make the state a built_value object and still get the equatable feature with it! Because I really loved the built_value of the state and the immutabality of it, Thank you so much
Code in Repo does not work for this test. I am using null safe flutter_bloc version 7.0.1. This test code doesn't work as provided. The stream is not emitting the Empty state. It's only emitting the Error state. Plus, you need to wire the the expectLater call to bloc.stream.asBroadcastStream() To get the test to work using null safe flutter_bloc. The below code works for this test: group('NumberTriviaBloc 2', () { test('should emit [NumberTriviaErrorState] when the input is invalid', () async { // arrange when(mockInputConverter.stringToUnsignedInteger(any)) .thenReturn(Left(InvalidInputFailure())); // assert later final expected = [ // the initial state is always emitted first // EmptyNumberTriviaState(), ErrorNumberTriviaState(message: INVALID_INPUT_FAILURE_MESSAGE) ]; expectLater(bloc.stream.asBroadcastStream(), emitsInOrder(expected)); // act bloc.add(GetTriviaForConcreteNumberEvent(numberString: 'xyz')); }); });
Hello resocoder thanks for your greats tutorials, I used bloc+ flutter_bloc in my app, i noticed that I need at least one bloc per page, is it normal or I didn't well-understood bloc pattern with flutter_bloc? Is there a way to share more than one type of data per bloc? for example i ve implemented shop app with favorite item feature, i had to create 1 bloc for favorite item, 1 for home page item and one for item itself, could i encapsulate all this in a "shopBloc" ?
You need one bloc per one big enough functionality. You can have 1 and you can have even more blocs you use in your app page. Some of the Blocs might be shared across the pages, because of blocs cover functionality, not pages. If you using general BLoC conventions, you might have one bloc for the whole shop app, but because you are, probably using, the package:bloc (from Felix Angelov) implementation, which only have 1 way in, 1 way out, you cannot, for example, create an effective solution for having the whole shop logic in one bloc. But this is a good thing because we want to split our functionality and states per functionality provide us better testability. You can find more information about how Bloc (package:bloc) works at bloclibrary.dev/#/
Can't watch the tutorial today... One of my hdds crashed and I decided to reinstall my system from scratch. When manjaro and win 10 work again I will go for it 🤘🙈
@@ResoCoder I used Ubuntu at first like most Linux newbies. Though I'm still one. But package management is far better in arch distros in my opinion. It just runs smooth no bugs on installation e.g. on my Raspberry Pi 4. The only problem I had so far was getting blizzard games to run... Because of Nvidia Drivers... I guess Pop OS is there in a better shape 😅
I suppose it is because of the package:bloc since v1.0.0 implements Stream and Sink API, therefore you only need to use `myBloc` to get the stream of events, not `myBloc.state`. Take a look at the changelog github.com/felangel/bloc/blob/master/packages/bloc/CHANGELOG.md
Bro you should make one more playlist, in which make a multi-page app using TDD and bloc. It will help us a lot. Always love your work #resocoder
totally, I started a few time and I kind of lost on how to do some simples things with bloc, normally, things that suppost to happen in a multi-page app (real life, right?). I am coming from Xamarin, and in MVVM every thing seens so simple and connected rsrsrs
Since the previous way shown in the video no longer works for later Bloc versions, this is what worked for me on Flutter Bloc 8.1.1
test('should emit [Error] when input is invalid', () async* {
when(mockInputConverter.stringToUnsignedInteger(any))
.thenReturn(Left(InvalidInputFailure()));
final expected = [
Empty(),
const Error(message: INVALID_INPUT_FAILURE_MESSAGE)
];
expectLater(bloc.stream.asBroadcastStream(), emitsInOrder(expected));
bloc.add(const GetTriviaForConcreteNumber(tNumberString));
});
My NumberTriviaBloc class looks like this;
class NumberTriviaBloc extends Bloc {
final GetConcreteNumberTrivia getConcreteNumberTrivia;
final GetRandomNumberTrivia getRandomNumberTrivia;
final InputConverter inputConverter;
NumberTriviaBloc({
required GetConcreteNumberTrivia concrete,
required GetRandomNumberTrivia random,
required this.inputConverter
}) : getConcreteNumberTrivia = concrete,
getRandomNumberTrivia = random,
super(Empty()) {
on (numberTriviaEventObserver);
}
FutureOr numberTriviaEventObserver(event, emit) async* {
if (event is GetTriviaForConcreteNumber) {
final inputEither = inputConverter.stringToUnsignedInteger(event.numberString);
yield* inputEither.fold(
(failure) async* {
yield const Error(message: INVALID_INPUT_FAILURE_MESSAGE);
},
(integer) => throw UnimplementedError());
}
}
}
bloc.dispatch should be bloc.add with flutter_bloc ^2.0
send a pr :)
if your test still fails then change to the following :
test('should emit [Error] when the input is invalid', () async {
// arrange
when(mockInputConverter.stringtoUnsignedInt(any))
.thenReturn(Left(InvalidInputFailure()));
// assert later
final expected = [
//Empty(),
Error(message: INVALID_INPUT_FAILURE_MESSAGE),
];
expectLater(bloc.stream, emitsInOrder(expected));
// act
bloc.add(GetTriviaForConcreteNumber(tNumberString));
});
@@gogalcloud Thanks a lot!
if your following this tutorial later after the bloc update. bloc.state does not return a list of states, but the latest state. To add a stream type.
expectLater(
bloc.asBroadcastStream(),
emitsInOrder(expected),
);
Saved me a lot of time. Thank you.
doesn't work for me :( both tests doesn't work... should call inputConverter and should emit error
I have solved in that way:
expectLater(
bloc.asBroadcastStream(),
emits(Error(message: INVALID_INPUT_FAILURE_MESSAGE)),
);
i don't know why he doesn't emit the first state
Resocoder should have atleast updated the bloc code for the latest version into their repositories at gethub @@raffos92
thanks homie
I got an error while testing the Error. I believe it was because of Bloc 1.0.0 new release. Basically bloc.state returns the current state and the test failed reporting
'ERROR: Expected: should do the following in order:
* emit an event that Empty:
* emit an event that Error:
Actual: Empty:
Which: was not a Stream or a StreamQueue'
I solved it by changing bloc.state to bloc.cast()
Thanks works for me. :)
Works for me too! Thanks! Do you know the difference between cast() and state? Could you help me Reso Coder?
With the new flutter_bloc release you just need to use bloc not bloc.state or bloc.cast() ResoCoder just made a good primer video for the bloc_test package that touches on these points and as always is great!
Update your code following this video: th-cam.com/video/S6jFBiiP0Mc/w-d-xo.html
I was eagerly waiting for this video.
Currently implanting all these techniques in my Flutter project.
Thanks for the awesome work!
Thanks a lot friend this is reaally the most pro tutoriels out there .
3:10 "optional but also required"... reminds me of that "Well yes but actually no" meme hahaha
For a better readability I renamed classes ans objects instance:
GetRandomNumberTrivia to GetRandomNumberTriviaUseCase
and randomNumberTriviaUseCase
GetConcreteNumberTrivia to GetConcreteNumberTriviaUseCase
and concreteNumberTriviaUseCase
Great! You are really good at explaining!
I finally understand async* and yield*.
blocTest(
'emit [NumberTriviaError] when the input is invalid',
build: () {
when(mockInputConverter.stringToUnsignedInteger(any))
.thenReturn(Left(InvalidInputFailure()));
return numberTriviaBloc;
},
act: (bloc) => bloc.add(const GetTriviaForConcreteNumber(tNumberString)),
expect: () => [isA()],
);
All the videos related to Clean architecture are awesome. You have very well explained the stuffs. Love your channel. #resocoder
I really love you for your work. Thanks
Thanks for watching!
I don't know if it's just me, but I feel like this approach is really excellent for a one man team. If you have to add members to that team, you'd really have to sit them down and teach them about this approach thereby losing production time, I don't know...great series though.
That's a reality that exists in most development contexts. Now there is this video series that you can tell them to go through, so everyone can be on the same page.
Hi 'Sensei' thanks for wonderful work !! , by the way , when you add bloc.dispatch , currently it isn't on bloc, because In v1.0.0 of the bloc library, all blocs are now Sinks. This means we can start using the traditional add sink api in order notify the bloc of a new event instead of dispatch.
Thanks my friend. Keep it up!
For the last test use bloctest instead
blocTest(
'should emit [Error] when the input is invalid',
build: () {
when(mockInputConverter.stringToUnsignedInteger(any))
.thenReturn(Left(InvalidInputFailure()));
return bloc;
},
act: (bloc) => bloc.add(GetTriviaForConcreteNumber(tNumberString)),
expect: () => [Error(message: INVALID_INPUT_FAILURE_MESSAGE)],
);
});
and
yield* inputEither.fold(
(failure) async* {
yield Error(message: INVALID_INPUT_FAILURE_MESSAGE);
},
(number) async* {
print(number);
},
);
or you can do some changes like this :
test('should emit [Error] when the input is invalid', () async {
// arrange
when(mockInputConverter.stringtoUnsignedInt(any))
.thenReturn(Left(InvalidInputFailure()));
// assert later
final expected = [
//Empty(),
Error(message: INVALID_INPUT_FAILURE_MESSAGE),
];
expectLater(bloc.stream, emitsInOrder(expected));
// act
bloc.add(GetTriviaForConcreteNumber(tNumberString));
});
Important: First of you are still facing error is because you have to implement right Side of the fold also:
if (event is GetTriviaForConcreteNumber) {
final inputorerror =
inputConverter.stringtoUnsignedInt(event.numberString);
yield* inputorerror.fold((l) async* {
yield Error(message: INVALID_INPUT_FAILURE);
}, (r) async* {
concreteNumberTrivia(Params(number: r));
});
}
you should remake this tutorial with the current version of bloc. I saw your github project for this tutorial the bloc dependecy still at v5.0.0 compared to current bloc v6.0.1
Great video!!!
watch out for this line *expectLater(bloc, emitsInOrder(expected))*
great!
Hi thanks for this tutorial. I was wondering why did you pass usecases dependencies one by one for the bloc implementation and not directly the repository Interface ? Can't really understand the role of usescases.... Seems like another layer of abstraction but what for ? Thanks again !
For the last test, since I try to find minimum changes needed in order for the test to work.
just change async to async* in number_trivia_bloc_test.dart for that test.
async => gives you a Future
async* => gives you a Stream
DO NOT USE async*!!! using async* will not show error when the expected is wrong.
Instead in expectLater change [bloc.state] to [bloc.stream]
Excellent video tutorial and my only feedback might be that the flutter_bloc library has changed so rapidly that a few parts just didn't work. such as bloc.dispatch is no longer a bloc method. Since the making of this video, less than 2 months ago, flutter_bloc has gone from beta to v1.0.0 to now v2.0.0. Perhaps a single video update on this library alone might be enough.
I think the best thing to do is to pull the repo and do the updates then send a PR.
I have updated the repo a month ago.
the new code for the updated version of bloc
test('should emit [error] when the input is invalid', () {
when(() => mockInputConverter.stringToUnsignedInt(any()))
.thenReturn(Left(InvalidInputFailure()));
final expected = [
// Empty(),
Error(message: INVALID_INPUT_FAILURE_MESSAGE),
];
expectLater(bloc.stream.asBroadcastStream(), emitsInOrder(expected));
bloc.add(GetTriviaForConcreteNumber(tNumberString));
});
I'd like to know if it is possible to make the state a built_value object and still get the equatable feature with it! Because I really loved the built_value of the state and the immutabality of it, Thank you so much
Because when changing the state I'd like to preserve the old data of the state if not changed, so it possible to do that without built_value or not?
Amazing tutorial.
Where would you execute flutter auth plugins (like flutter_facebook_login) from?
Thanks
Thanks again keep going :) !
Isn't the state a cold observable? It should wait for you to expect it, shouldn't it?
I am getting this error in test.
Unhandled error UnimplementedError occurred in Instance of 'NumberTriviaBloc'.
Code in Repo does not work for this test.
I am using null safe flutter_bloc version 7.0.1. This test code doesn't work as provided. The stream is not emitting the Empty state. It's only emitting the Error state. Plus, you need to wire the the expectLater call to bloc.stream.asBroadcastStream() To get the test to work using null safe flutter_bloc.
The below code works for this test:
group('NumberTriviaBloc 2', () {
test('should emit [NumberTriviaErrorState] when the input is invalid', () async {
// arrange
when(mockInputConverter.stringToUnsignedInteger(any))
.thenReturn(Left(InvalidInputFailure()));
// assert later
final expected = [
// the initial state is always emitted first
// EmptyNumberTriviaState(),
ErrorNumberTriviaState(message: INVALID_INPUT_FAILURE_MESSAGE)
];
expectLater(bloc.stream.asBroadcastStream(), emitsInOrder(expected));
// act
bloc.add(GetTriviaForConcreteNumberEvent(numberString: 'xyz'));
});
});
when is this Number Trivia project going to finish..?
In 2 weeks PLUS the full code is already on GitHub.
Hello resocoder thanks for your greats tutorials, I used bloc+ flutter_bloc in my app, i noticed that I need at least one bloc per page, is it normal or I didn't well-understood bloc pattern with flutter_bloc? Is there a way to share more than one type of data per bloc? for example i ve implemented shop app with favorite item feature, i had to create 1 bloc for favorite item, 1 for home page item and one for item itself, could i encapsulate all this in a "shopBloc" ?
You need one bloc per one big enough functionality. You can have 1 and you can have even more blocs you use in your app page. Some of the Blocs might be shared across the pages, because of blocs cover functionality, not pages.
If you using general BLoC conventions, you might have one bloc for the whole shop app, but because you are, probably using, the package:bloc (from Felix Angelov) implementation, which only have 1 way in, 1 way out, you cannot, for example, create an effective solution for having the whole shop logic in one bloc. But this is a good thing because we want to split our functionality and states per functionality provide us better testability.
You can find more information about how Bloc (package:bloc) works at bloclibrary.dev/#/
You the outro song is so danceable
Can't watch the tutorial today... One of my hdds crashed and I decided to reinstall my system from scratch. When manjaro and win 10 work again I will go for it 🤘🙈
Oh my! How do you find Manjaro compared to Debian-based distros? I'm on PopOS (basically Ubuntu).
@@ResoCoder I used Ubuntu at first like most Linux newbies. Though I'm still one. But package management is far better in arch distros in my opinion. It just runs smooth no bugs on installation e.g. on my Raspberry Pi 4. The only problem I had so far was getting blizzard games to run... Because of Nvidia Drivers... I guess Pop OS is there in a better shape 😅
hi, I updated bloc to 1.0.0 version and get fail of the last test. Can you find out what is wrong, what was broke?
I suppose it is because of the package:bloc since v1.0.0 implements Stream and Sink API, therefore you only need to use `myBloc` to get the stream of events, not `myBloc.state`. Take a look at the changelog github.com/felangel/bloc/blob/master/packages/bloc/CHANGELOG.md
The tests don't pass in flutter 1.20. So downgrade to flutter 1.17.5