How to use Freezed package with Bloc in flutter? - flutter

I want to create a simple bloc with freezed package. This is my bloc:
import 'package:bloc/bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:presentation/presentation_index.dart';
part 'auth_bloc_event.dart';
part 'auth_bloc_state.dart';
class AuthBlocBloc extends Bloc<AuthEvent, AuthState> {
final SignUpBuyerUseCase signUpBuyerUseCase;
AuthBlocBloc(this.signUpBuyerUseCase) : super(AuthState.initial());
#override
Stream<AuthState> mapEventToState(
AuthEvent event,
) async* {
yield* event.map();
}
}
and my event class :
part of 'auth_bloc.dart';
#freezed
abstract class AuthEvent with _$AuthEvent {
const factory AuthEvent.login(String username, String password) = Login;
const factory AuthEvent.signUpBuyer(BuyerEntity entity) = SignUpBuyer;
}
and state class :
part of 'auth_bloc.dart';
#freezed
abstract class AuthState with _$AuthState {
const factory AuthState.initial() = InitialAuthState;
const factory AuthState.signUpBuyerFail(String error) = SignUpBuyerFail;
const factory AuthState.signUpBuyerSuccess() = SignUpBuyerSuccess;
const factory AuthState.signUpBuyerLoading() = SignUpBuyerLoading;
}
The problem is that when i try to run
flutter pub run build_runner watch --deleteonflicting-outputs
Nothing happens and no classes are generated

In the bloc, you should try to include the freezed file.
part 'auth_bloc_event.dart';
part 'auth_bloc_state.dart';
part 'auth_bloc.freezed.dart';

Related

type 'Null' is not a subtype of type 'Future<Either<Failure, NumberTrivia>>'

I was learning how to do test drive development (tdd) as well as clean code architecture on flutter and I kept getting into unfamiliar problem again and again. That is - type 'Null' is not a subtype of type 'Future<Either<Failure, NumberTrivia>>'. This method is declared in an abstract class NumberTriviaRepository
The NumberTrivia is a simple entity class as follow
class NumberTrivia extends Equatable {
final int number;
final String text;
const NumberTrivia({
required this.number,
required this.text,
});
#override
// TODO: implement props
List<Object?> get props => [number, text];
}
The NumberTriviaRepository
abstract class NumberTriviaRepository {
Future<Either<Failure, NumberTrivia>> getConcreteNumberTrivia(int number);
Future<Either<Failure, NumberTrivia>> getRandomNumberTrivia();
}
The usecase - GetConcreteNumberTrivia
class GetConcreteNumberTrivia {
final NumberTriviaRepository repository;
GetConcreteNumberTrivia(this.repository);
Future<Either<Failure, NumberTrivia>> execute({required number}) async {
return await repository.getConcreteNumberTrivia(number);
}
}
Here is the main test file
class MockNumberTriviaRepository extends Mock
implements NumberTriviaRepository {}
void main() {
late MockNumberTriviaRepository mockNumberTriviaRepository;
late GetConcreteNumberTrivia usecase;
setUp(() {
mockNumberTriviaRepository = MockNumberTriviaRepository();
usecase = GetConcreteNumberTrivia(mockNumberTriviaRepository);
});
final testNumber = 1;
const testNumberTrivia = NumberTrivia(number: 1, text: 'test');
test('should get trivia for the number from the repository', () async {
when(mockNumberTriviaRepository.getConcreteNumberTrivia(testNumber))
.thenAnswer((_) async => const Right(testNumberTrivia));
final result = await usecase.execute(number: testNumber);
log('Result equals ${result}');
expect(result, equals(const Right(testNumberTrivia)));
verify(mockNumberTriviaRepository.getConcreteNumberTrivia(testNumber));
verifyNoMoreInteractions(mockNumberTriviaRepository);
});
}
I don't know where I'm making a mistake but I'm having trouble passing this test case. I believe both the results should be NumberTrivia object but it seems like one of them is null and I can't figure why that is the case.
I expect the object to be of the same type (NumberTrivia) in the expect function during the test
This might be caused if you are using an old Mockito version that does not support null-safety. Try upgrading your version to ^5.0.0.
This is frequently a pain if tutorials are from pre-null-safety or if some packages have significantly changed since then. Not sure if it was this or some bloc tutorial, one was causing me real headaches because it required significant changes to run in today's version.
If you use version to ^5.0.0.
To use Mockito's generated mock classes, add a build_runner dependency in your package's pubspec.yaml file, under dev_dependencies; something like build_runner: ^1.11.0.
flutter pub run build_runner build
# OR
dart run build_runner build
The resulting class is as follows, and you will pass the test
get_concrete_number_trivia_test.mocks.dart
// Mocks generated by Mockito 5.3.2 from annotations
// in flutter_go/test/features/number_trivia/domain/usecases/get_concrete_number_trivia_test.dart.
// Do not manually edit this file.
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i4;
import 'package:dartz/dartz.dart' as _i2;
import 'package:flutter_go/core/error/failures.dart' as _i5;
import 'package:flutter_go/features/number_trivia/domain/entities/number_trivia.dart'
as _i6;
import 'package:flutter_go/features/number_trivia/domain/repositories/number_trivia_repository.dart'
as _i3;
import 'package:mockito/mockito.dart' as _i1;
// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types
// ignore_for_file: subtype_of_sealed_class
class _FakeEither_0<L, R> extends _i1.SmartFake implements _i2.Either<L, R> {
_FakeEither_0(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}
/// A class which mocks [NumberTriviaRepository].
///
/// See the documentation for Mockito's code generation for more information.
class MockNumberTriviaRepository extends _i1.Mock
implements _i3.NumberTriviaRepository {
#override
_i4.Future<_i2.Either<_i5.Failure, _i6.NumberTrivia>> getConcreteNumberTrivia(
int? number) =>
(super.noSuchMethod(
Invocation.method(
#getConcreteNumberTrivia,
[number],
),
returnValue:
_i4.Future<_i2.Either<_i5.Failure, _i6.NumberTrivia>>.value(
_FakeEither_0<_i5.Failure, _i6.NumberTrivia>(
this,
Invocation.method(
#getConcreteNumberTrivia,
[number],
),
)),
returnValueForMissingStub:
_i4.Future<_i2.Either<_i5.Failure, _i6.NumberTrivia>>.value(
_FakeEither_0<_i5.Failure, _i6.NumberTrivia>(
this,
Invocation.method(
#getConcreteNumberTrivia,
[number],
),
)),
) as _i4.Future<_i2.Either<_i5.Failure, _i6.NumberTrivia>>);
#override
_i4.Future<_i2.Either<_i5.Failure, _i6.NumberTrivia>>
getRandomNumberTrivia() => (super.noSuchMethod(
Invocation.method(
#getRandomNumberTrivia,
[],
),
returnValue:
_i4.Future<_i2.Either<_i5.Failure, _i6.NumberTrivia>>.value(
_FakeEither_0<_i5.Failure, _i6.NumberTrivia>(
this,
Invocation.method(
#getRandomNumberTrivia,
[],
),
)),
returnValueForMissingStub:
_i4.Future<_i2.Either<_i5.Failure, _i6.NumberTrivia>>.value(
_FakeEither_0<_i5.Failure, _i6.NumberTrivia>(
this,
Invocation.method(
#getRandomNumberTrivia,
[],
),
)),
) as _i4.Future<_i2.Either<_i5.Failure, _i6.NumberTrivia>>);
}
Example
import 'package:dartz/dartz.dart';
import 'package:flutter_go/features/number_trivia/domain/entities/number_trivia.dart';
import 'package:flutter_go/features/number_trivia/domain/repositories/number_trivia_repository.dart';
import 'package:flutter_go/features/number_trivia/domain/usecases/get_concrete_number_trivia.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
// class MockNumberTriviaRepository extends Mock
// implements NumberTriviaRepository {}
#GenerateNiceMocks([MockSpec<NumberTriviaRepository>()])
// import generated mock classes
import './get_concrete_number_trivia_test.mocks.dart';
void main() {
late GetConcreteNumberTrivia usecase;
late MockNumberTriviaRepository mockNumberTriviaRepository;
setUp(() {
mockNumberTriviaRepository = MockNumberTriviaRepository();
usecase = GetConcreteNumberTrivia(mockNumberTriviaRepository);
});
final tNumber = 1;
final tNumberTrivia = NumberTrivia(number: 1, text: 'test');
test(
'should get trivia for the number from the repository',
() async {
// "On the fly" implementation of the Repository using the Mockito package.
// When getConcreteNumberTrivia is called with any argument, always answer with
// the Right "side" of Either containing a test NumberTrivia object.
when(mockNumberTriviaRepository.getConcreteNumberTrivia(any))
.thenAnswer((_) async => Right(tNumberTrivia));
// The "act" phase of the test. Call the not-yet-existent method.
final result = await usecase.execute(number: tNumber);
// UseCase should simply return whatever was returned from the Repository
expect(result, Right(tNumberTrivia));
// Verify that the method has been called on the Repository
verify(mockNumberTriviaRepository.getConcreteNumberTrivia(tNumber));
// Only the above method should be called and nothing more.
verifyNoMoreInteractions(mockNumberTriviaRepository);
},
);
}

Flutter Hive with ChangeNotifierProvider

So I wanted to use Hive for storing notes in a Calendar app but I am stuggling so much with implementing Hive and a ChangeNotifierProvider together . If anyone has an idea on what to do I would like to see it.
Here is my code until now :
#HiveType(typeId: 0)
class EventsBox extends HiveObject {
EventsBox({required this.date, required this.eventsList});
#HiveField(0)
DateTime date;
#HiveField(1)
List<CleanCalendarEvent> eventsList;
}
And here is the FutureProvider that is needed :
final hiveProvider = FutureProvider<HiveDB>((_) => HiveDB.create());
class HiveDB {
var _events;
HiveDB._create() {}
static Future<HiveDB> create() async {
final component = HiveDB._create();
await component._init();
return component;
}
_init() async {
Hive.registerAdapter(EventsBoxAdapter());
this._events = await Hive.openBox<EventsBox>('events');
}
storeEvent(EventsBox eventsMap) {
this._events.put('events', eventsMap);
}
EventsBox getEvents() {
return this._events.get('events');
}
}
I want to use ChangeNotifierProvider and not FutureProvider
The goal of the provider framework is to inject dependencies into your app without recreating them all the time. In your example, you have your HiveDB class which is what you want to inject into your app for various other widgets to use it.
IMO, the general approach of "providing" dependencies is:
Create dependency instances outside the app (Typically in the main() function)
Create a provider which injects this dependency object
Wrap your MaterialApp with the provider
Supply your dependency object into the provider wrapper
Use the Provider<DependencyClass>.of(context) to access your dependency wherever you need it in your app.
Let's see how this applies to your code:
1. Setup your Hive models and HiveDB
lib/models/event_box.dart
#HiveType(typeId: 0)
class EventsBox extends HiveObject {
#HiveField(0)
DateTime date;
#HiveField(1)
List<CleanCalendarEvent> eventsList;
EventsBox({
required this.date,
required this.eventsList,
});
}
lib/models/hive_db.dart
class HiveDB {
var _events;
HiveDB._create() {}
static Future<HiveDB> create() async {
final component = HiveDB._create();
await component._init();
return component;
}
_init() async {
Hive.registerAdapter(EventsBoxAdapter());
this._events = await Hive.openBox<EventsBox>('events');
}
storeEvent(EventsBox eventsMap) {
this._events.put('events', eventsMap);
}
EventsBox getEvents() {
return this._events.get('events');
}
}
In this case, you want to "provide" (or inject) an instance of HiveDB to your app.
2. Create a provider for your HiveDB instance
lib/models/hive_db_provider.dart
class HiveDbProvider extends StatelessWidget {
final HiveDB db;
final Widget child;
const HiveDbProvider({
Key? key,
required this.db,
required this.child,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<HiveDB>.value(
value: db,
child: Consumer<AppCache>(
builder: (context, db, _) {
return child;
},
),
);
}
}
3. Create HiveDB instance in main()
lib/main.dart
void main() async {
final HiveDB db = await HiveDB.create();
runApp(
MyApp(db: db)
);
}
...
4. Inject the HiveDB instance using the provider
lib/main.dart
...
class MyApp extends StatelessWidget {
final HiveDB db;
const PayBuddy({
Key? key,
required this.db,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return HiveDbProvider(
db: db,
child: CacheProvider(
cache: cache,
child: MaterialApp(
...
),
),
);
}
}
5. Access "provided" instance using Provider anywhere in your app
lib/pages/some_screen.dart
import "package:provider/provider.dart";
...
class SomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
// Access HiveDB instance
final hiveDb = Provider<HiveDB>.of(context);
...
}
}
Conclusion
The sample uses the Provider package which simplifies using provider usage in Flutter.

Flutter mocktail get error when put mock getx controller

I have this getx controller :
class CustomerPOAutocompleteState extends GetxController {
CustomerPOAutocompleteState();
...
Future<void> getCustomer(String title, String? propertyId) async {
var result = await RRGraphQL().artemis!...
_customers =...
update(['update']);
}
Now by Mock mocktail I created a mock class:
class MockController extends Mock implements CustomerPOAutocompleteState {}
When I try to put it I got error:
void main() {
late MockController mockController;
setUpAll(() {
mockController = MockController();
});
...
testWidgets('test', (WidgetTester tester) async {
Get.put<CustomerPOAutocompleteState>(mockController,tag: "");
error:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following _TypeError was thrown running a test:
type 'Null' is not a subtype of type 'InternalFinalCallback<void>'
UPDATED Answer
This error occurs since Get.put performs calls on your GetxController which have not been mocked out correctly.
You can either do this manually (you will have to check the implementation details of Get.put and GetxController). Or you can do, what the Get documentation suggests and let your mock extend GetxController and use Mock as a mixin.
class MockController extends GetxController
with Mock
implements CustomerPOAutocompleteState {}
Check out this full example:
import 'package:flutter_test/flutter_test.dart';
import 'package:get/get.dart';
import 'package:mocktail/mocktail.dart';
class DemoController extends GetxController {
var count = 0.obs;
increment() => count++;
}
class MockController extends GetxController
with Mock
implements DemoController {}
main() {
late MockController mockController;
setUpAll(() {
mockController = MockController();
});
test('GetX Test', () {
when(() => mockController.count).thenAnswer((_) => 12.obs);
Get.put<DemoController>(mockController, tag: "");
mockController.increment();
expect(mockController.count.value, 12);
});
}

How to use the BLoC library?

I'm trying to figure out the BLoC library, but it gives me headaches.
I'm trying to fetch hotel names from an API. I have a model and a service responsible for contacting the API and fetching the data. However, I don't know how to connect it to the BLoC library.
Once my app starts, I want BLoC to fetch the data from the API and then show it in the app.
Here's my code:
hotel_model.dart
class Hotels {
final List<Hotel> hotels;
Hotels({this.hotels});
factory Hotels.fromJson(Map<String, dynamic> json) {
return Hotels(
hotels: List<Hotel>.from(
json['hotels'].map(
(x) => Hotel.fromJson(x),
),
),
);
}
}
class Hotel {
final String hotelName;
Hotel({this.hotelName});
factory Hotel.fromJson(Map<String, dynamic> json) {
return Hotel(
hotelName: json['name'],
);
}
}
hotel_service.dart
import 'package:http/http.dart' as http;
abstract class DownloadService {
Future<http.Response> fetchHotels();
}
class HotelService extends DownloadService {
#override
Future<http.Response> fetchHotels() {
final Uri uri = Uri.https('services.lastminute.com', 'mobile/stubs/hotels');
return http.get(uri);
}
}
And here's what I did wit the BLoC lib.
hotel_event.dart
part of 'hotel_bloc.dart';
#immutable
abstract class HotelEvent {}
class OnAppStartEvent extends HotelEvent {}
hotel_bloc.dart
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:hotels/models/hotel/hotel_model.dart';
import 'package:hotels/services/hotel/hotel_service.dart';
import 'package:meta/meta.dart';
part 'hotel_event.dart';
part 'hotel_state.dart';
class HotelBloc extends Bloc<HotelEvent, HotelState> {
HotelBloc() : super(HotelFinal());
final HotelService hotelService = HotelService();
#override
Stream<HotelState> mapEventToState(
HotelEvent event,
) async* {
if (event is FetchEvent) {
final response = hotelService.fetchHotels();
yield
}
}
}
hotel_state.dart
part of 'hotel_bloc.dart';
#immutable
abstract class HotelState {
HotelState();
}
class HotelFinal extends HotelState {
final Hotel hotel;
HotelFinal(this.hotel);
Hotel getHotel() {
return hotel;
}
}
First of all add await to this line in your bloc
final response = await hotelService.fetchHotels();
return List<Hotel> from your fetchHotels function
you must have stateful class for your screen and in the initState
you can create your bloc object and call .add method on it
in your build method wrap your widget with BlocBuilder and on builder callback check your bloc state, if the state is HotelFinal return your ui with list of hotels in your state object.
It'll be useful to add another state for your HotelState for when your bloc is fetching the data, and even for when there's an error. e.g;
part of 'hotel_bloc.dart';
#immutable
abstract class HotelState {
HotelState();
}
class HotelFinal extends HotelState {
final Hotel hotel;
HotelFinal(this.hotel);
Hotel getHotel() {
return hotel;
}
}
class HotelLoading extends HotelState {
HotelLoading();
}
class HotelError extends HotelState {
final String error;
HotelError(this.error);
}
You would want to change your mapEventToState to something like this:
#override
Stream<HotelState> mapEventToState(
HotelEvent event,
) async* {
if (event is FetchEvent) {
yield HotelLoading();
try {
final response = await hotelService.fetchHotels();
// It seems like your service doesn't return an hotel directly, so you'll have to deal with this as it is not part of the question.
final hotel = getYourHotelHereWithTheResponse;
yield HotelFinal(hotel);
} catch (e) {
yield HotelError('something went wrong getting the hotel info');
}
}
}
Lastly, add a widget to your widget tree that adds FetchEvent to your bloc and add a BlocBuilder to react to the change of states. Note that this is very flexible and can be done in many ways, but it is out of the scope of your very broad question, I'm just showing you how to use the library at a minimal:
class MyStatefulWidget extends StatefulWidget {
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
HotelBloc hotelBloc;
#override
void initState() {
hotelBloc = HotelBloc..add(FetchEvent());
super.initState();
}
#override
void dispose() {
hotelBloc.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return BlocBuilder(builder: (context, state) {
if(state is HotelLoading) {
// return a widget to deal with loading
}
if(state is HotelFinal) {
// return a widget to deal with success
}
if(state is HotelError) {
// return a widget to deal with error
}
});
}
}

LocationListBloc doesnt extend Bloc<dynamic, LocationListState>

I am currently refactoring my code to the bloc pattern and have created a bloc for a screen that fetches a list of locations from a json file in assets. The event being the fetch, and the states being initial, loading and loaded.
On my UI screen, I want to use BlocBuilder but when I use the BlocBuilder widget it gives me the error :
LocationListBloc doesnt extend Bloc with the LocationListBloc underlined with a line
My code is structured in folders as as follows
lib/blocs/location_list_bloc
lib/blocs/location_list_event
lib/blocs/location_list_state
lib/blocs/blocs
UI / location_list
location_list_state
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gt_project_bloc/blocs/blocs.dart';
class Locations extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocBuilder<LocationListBloc, LocationListState>();
}
}
import 'package:equatable/equatable.dart';
import 'package:gt_project_bloc/models/models.dart';
import 'package:meta/meta.dart';
import 'package:equatable/equatable.dart';
abstract class LocationListState extends Equatable {
const LocationListState();
#override
List<Object> get props => [];
}
class LocationListinitial extends LocationListState {}
class LocationListLoading extends LocationListState {}
class LocationListLoaded extends LocationListState {
final List<SavedStations> locationlist;
LocationListLoaded(this.locationlist) : assert(locationlist != null);
}
location_list_event
import 'package:equatable/equatable.dart';
abstract class LocationListEvent extends Equatable {
#override
List<Object> get props => [];
}
class Fetch extends LocationListEvent {}
location_list_state
import 'package:equatable/equatable.dart';
import 'package:gt_project_bloc/models/models.dart';
import 'package:meta/meta.dart';
import 'package:equatable/equatable.dart';
abstract class LocationListState extends Equatable {
const LocationListState();
#override
List<Object> get props => [];
}
class LocationListinitial extends LocationListState {}
class LocationListLoading extends LocationListState {}
class LocationListLoaded extends LocationListState {
final List<SavedStations> locationlist;
LocationListLoaded(this.locationlist) : assert(locationlist != null);
}
I found my issue, i had a stateful widget on the same page called LocationList and it was confusing its state LocationListState with the same name of my blocs state