I'm really confused of how to get the bloc to emit the initial state. bloc.state emits the latest state. And since there is no #override initialState available in the new bloc library, initialState was passed in to the super constructor. But still bloc does not emit the initialState, which in this case is Empty().
number_trivia_bloc.dart
import 'package:clean_architecture/core/error/failure.dart';
import 'package:clean_architecture/core/utils/input_converter.dart';
import 'package:clean_architecture/features/number_trivia/domain/usecases/get_concrete_number_trivia_repository.dart';
import 'package:clean_architecture/features/number_trivia/domain/usecases/get_random_number_trivia.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'number_trivia_state.dart';
import 'number_trivia_event.dart';
const SERVER_FAILURE = 'Server failure';
const CACHE_FAILURE = 'Cache failure';
const INVALID_INPUT_FAILURE =
'Invalid Input Failure - The input should not be a negative integer or zero';
class NumberTriviaBloc extends Bloc<NumberTriviaEvent, NumberTriviaState> {
final GetConcreteNumberTrivia getConcreteNumberTrivia;
final GetRandomNumberTrivia getRandomNumberTrivia;
final InputConverter inputConverter;
NumberTriviaBloc({
// Changed the name of the constructor parameter (cannot use 'this.')
#required GetConcreteNumberTrivia concrete,
#required GetRandomNumberTrivia random,
#required this.inputConverter,
// Asserts are how you can make sure that a passed in argument is not null.
// We omit this elsewhere for the sake of brevity.
}) : assert(concrete != null),
assert(random != null),
assert(inputConverter != null),
getConcreteNumberTrivia = concrete,
getRandomNumberTrivia = random,
super(Empty());
#override
Stream<NumberTriviaState> mapEventToState(
NumberTriviaEvent event,
) async* {
if (event is GetTriviaForConcreteNumber) {
final inputEither =
inputConverter.stringToUnsignedInteger(event.numberString);
yield* inputEither.fold(
(failure) async* {
yield Error(message: INVALID_INPUT_FAILURE);
},
(integer) async* {
yield Loading();
},
);
}
}
}
number_trivia_bloc_test.dart
import 'package:clean_architecture/core/utils/input_converter.dart';
import 'package:clean_architecture/features/number_trivia/data/models/number_trivia_model.dart';
import 'package:clean_architecture/features/number_trivia/domain/entities/number_trivia.dart';
import 'package:clean_architecture/features/number_trivia/domain/usecases/get_concrete_number_trivia_repository.dart';
import 'package:clean_architecture/features/number_trivia/domain/usecases/get_random_number_trivia.dart';
import 'package:clean_architecture/features/number_trivia/presentation/bloc/number_trivia_bloc.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:clean_architecture/features/number_trivia/presentation/bloc/number_trivia_state.dart';
import 'package:clean_architecture/features/number_trivia/presentation/bloc/number_trivia_event.dart';
import 'package:mockito/mockito.dart';
class MockGetConcreteNumberTrivia extends Mock
implements GetConcreteNumberTrivia {}
class MockGetRandomNumberTrivia extends Mock implements GetRandomNumberTrivia {}
class MockInputConverter extends Mock implements InputConverter {}
void main() {
NumberTriviaBloc bloc;
MockGetConcreteNumberTrivia mockGetConcreteNumberTrivia;
MockGetRandomNumberTrivia mockGetRandomNumberTrivia;
MockInputConverter mockInputConverter;
setUp(() {
mockGetConcreteNumberTrivia = MockGetConcreteNumberTrivia();
mockGetRandomNumberTrivia = MockGetRandomNumberTrivia();
mockInputConverter = MockInputConverter();
bloc = NumberTriviaBloc(
concrete: mockGetConcreteNumberTrivia,
random: mockGetRandomNumberTrivia,
inputConverter: mockInputConverter,
);
});
test('initialState should be Empty', () {
// assert
expect(bloc.state, equals(Empty()));
});
group('GetTriviaForNumber', () {
String str = '1';
int parsedStr = int.parse(str);
NumberTrivia tTrivia =
NumberTriviaModel(text: 'test text', number: parsedStr);
test('Should convert a string to an unsigned integer', () async {
//arrange
when(mockInputConverter.stringToUnsignedInteger(any))
.thenReturn(Right(parsedStr));
//act
bloc.add(GetTriviaForConcreteNumber(str));
await untilCalled(mockInputConverter.stringToUnsignedInteger(str));
//assert
verify(mockInputConverter.stringToUnsignedInteger(any));
});
test('Should return [Error] for InvalidInputFailure', () async {
//arrange
when(mockInputConverter.stringToUnsignedInteger(any))
.thenReturn(Left(InvalidInputFailure()));
//assert later
final expected = [
Empty(),
Error(message: INVALID_INPUT_FAILURE),
];
expectLater(bloc, emitsInOrder(expected));
//act
bloc.add(GetTriviaForConcreteNumber(str));
});
});
}
It should work but all I get is this error screaming at me. I tries bloc.cast(), bloc.asBroadcastStream(). Nothing seems to work.So, please, can someone help me figure this out. Thanks in advance.
There contains some change from flutter_bloc 6.0.0
You can check the initial state via
test('initial state is correct', () {
expect(bloc.state, Empty());
});
Mentioned here:
Regression: initial state is not emited anymore
Related
Problem
I'm developing a simple Todo app and trying to create test for my files before I develop the UI.
I created a repository which exposes a Stream<List<Todo>>> and I'm listening to it in the TodosOverviewStore. I tried to test if the Store.value would update when the repository stream emitted a new value, but I can't get it right.
Code
todo_repository_impl.dart
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../domain/entities/todo.dart';
import '../domain/repositories/todo_repository.dart';
class TodoRepositoryImpl implements TodoRepository {
final SharedPreferences _prefs;
#visibleForTesting
static const String todoKey = '__todo_key__';
TodoRepositoryImpl({required SharedPreferences prefs}) : _prefs = prefs {
_init();
}
final _controller = StreamController<List<Todo>>();
void _init() {
final result = _prefs.getString(todoKey);
if (result == null || result.isEmpty) return _controller.add([]);
final todos = jsonDecode(result).map((e) => Todo.fromMap(e)).toList();
return _controller.add(todos);
}
#override
Stream<List<Todo>> getTodos() => _controller.stream;
}
todos_overview_store.dart
import 'dart:async';
import 'package:flutter/material.dart';
import '../../domain/entities/todo.dart';
import '../../domain/repositories/todo_repository.dart';
class TodosOverviewStore extends ValueNotifier<List<Todo>> {
final TodoRepository _repository;
late final StreamSubscription<List<Todo>> _subscription;
TodosOverviewStore({required TodoRepository todoRepository})
: _repository = todoRepository,
super([]);
void subscriptionRequested() {
_subscription = _repository.getTodos().listen((event) => value = event);
}
#override
Future<void> dispose() async {
await _subscription.cancel();
super.dispose();
}
}
todos_overview_store_test.dart
import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:hiveleak/domain/entities/todo.dart';
import 'package:hiveleak/domain/repositories/todo_repository.dart';
import 'package:hiveleak/presentation/stores/todos_overview_store.dart';
import 'package:mocktail/mocktail.dart';
class TodoRepositoryMock extends Mock implements TodoRepository {}
main() {
final todoRepository = TodoRepositoryMock();
late TodosOverviewStore todosOverviewStore;
final todo = Todo(title: 'lorem ipsum', done: false);
group('subscriptionRequested', () {
when(() => todoRepository.getTodos())
.thenAnswer((_) => Stream.value([todo]));
setUp(() {
todosOverviewStore = TodosOverviewStore(todoRepository: todoRepository);
});
test('It should call todoRepository.getTodos', () {
todosOverviewStore.subscriptionRequested();
verify(() => todoRepository.getTodos()).called(1);
});
test('It should update its value when stream emits', () {
todosOverviewStore.subscriptionRequested();
expect(todosOverviewStore.value, equals([todo]));
});
});
}
Error
But when I run this test, I get the following result:
package:test_api expect
package:flutter_test/src/widget_tester.dart 460:16 expect
test\presentation\stores\todos_overview_store_test.dart 34:7 main.<fn>.<fn>
Expected: [Instance of 'Todo']
Actual: []
Which: at location [0] is [] which shorter than expected
What should I do to make this work ? Thanks in advance.
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);
},
);
}
I'm trying to run a test with mockito following an outdated tutorial for a messaging app in flutter. I'm trying to use the most updated versions of everything. The tutorial just implemented flutter bloc, and now I'm getting an error that I don't know how to fix.
This is the test:
import 'package:chat/chat.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:rethink_chat/states_management/message/message_bloc.dart';
import 'message_bloc_test.mocks.dart';
#GenerateMocks([IMessageService])
void main() {
late MessageBloc sut;
late MockIMessageService messageService; //changed to Mock
late User user;
setUp(() {
messageService = MockIMessageService(); // FakeMessageService();
user = User(
username: 'test',
photoUrl: '',
active: true,
lastseen: DateTime.now(),
);
sut = MessageBloc(messageService);
});
tearDown(() => sut.close());
test('should emit message sent state when message is sent', () {
final message = Message(
from: '123',
to: '456',
contents: 'test message',
timestamp: DateTime.now(),
);
when(messageService.send(message)).thenAnswer((_) async => true);
when(messageService.dispose()).thenAnswer((_) async => <Object?>[]);
sut.add(MessageEvent.onMessageSent(message));
expectLater(sut.stream, emits(MessageState.sent(message)));
});
}
This is the error:
Bad state: add(MessageSent) was called without a registered event handler.
Make sure to register a handler via on<MessageSent>((event, emit) {...})
This is message_event.dart:
part of 'message_bloc.dart';
abstract class MessageEvent extends Equatable {
const MessageEvent();
factory MessageEvent.onSubscribed(User user) => Subscribed(user);
factory MessageEvent.onMessageSent(Message message) => MessageSent(message);
#override
List<Object> get props => [];
}
class Subscribed extends MessageEvent {
final User user;
const Subscribed(this.user);
#override
List<Object> get props => [user];
}
class MessageSent extends MessageEvent {
final Message message;
const MessageSent(this.message);
#override
List<Object> get props => [message];
}
class _MessageReceived extends MessageEvent {
const _MessageReceived(this.message);
final Message message;
#override
List<Object> get props => [message];
}
And message_bloc.dart:
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:chat/chat.dart';
import 'package:equatable/equatable.dart';
//import 'package:rethink_chat/states_management/message/message_event.dart';
// import 'package:rethink_chat/states_management/message/message_state.dart';
//import 'package:chat/chat.dart';
part 'message_event.dart';
part 'message_state.dart';
class MessageBloc extends Bloc<MessageEvent, MessageState> {
final IMessageService _messageService;
StreamSubscription?
_subscription; // added a ? but not sure if it's neutral, helpful or harmful
MessageBloc(this._messageService) : super(MessageState.initial());
#override
Stream<MessageState> mapEventToState(MessageEvent event) async* {
if (event is Subscribed) {
await _subscription?.cancel();
_subscription = _messageService
.messages(activeUser: event.user)
.listen((message) => add(_MessageReceived(message)));
}
if (event is _MessageReceived) {
yield MessageState.received(event.message);
}
if (event is MessageSent) {
await _messageService.send(event.message);
yield MessageState.sent(event.message);
}
}
#override
Future<void> close() {
_subscription?.cancel();
_messageService.dispose();
return super.close();
}
}
I saw in another post that flutter bloc at some point removed mapEventToState, but I don't know how to fix this for my situation? I'm so new to this that I have trouble knowing what to fix for this particular code, based on seeing examples of other code.
Please let me know if any other information would be helpful and I'll update this post to include it. I'm a total beginner, so this is hopefully a very simple fix that I'm overlooking. I greatly appreciate any help.
Edit: I also get a similar error for a similar test:
test('should emit messages received from service', () {
final message = Message(
from: '123',
to: '456',
contents: 'test message',
timestamp: DateTime.now(),
);
when(messageService.messages(activeUser: anyNamed('activeUser')))
.thenAnswer((_) => Stream.fromIterable([message]));
when(messageService.dispose()).thenAnswer((_) async => <Object?>[]);
sut.add(MessageEvent.onSubscribed(user));
expectLater(sut.stream, emitsInOrder([MessageReceivedSuccess(message)]));
});
The error is
Bad state: add(Subscribed) was called without a registered event handler.
Make sure to register a handler via on<Subscribed>((event, emit) {...})
I assume the fix should be the same for both, right?
Edit2: so I'm thinking I need to put on<MessageSent>((event, emit) {...}) and on<Subscribed>((event, emit) {...}) somewhere in the code, but where?
Check out the migration guide https://bloclibrary.dev/#/migration
It looks like your code is still version 7.1 or lower. You first need to follow the migration from 7.1 to 7.2. This will show you how to migrate your mapEventToState to the new on<Event> format.
I'm following this tutorial: https://www.youtube.com/watch?v=dc3B_mMrZ-Q&t=11033s&ab_channel=ResoCoder
Unfortunately I have the latest version of FLutter and also installed th elatest versions of all th e dependencies, so the code has to change for some breaking changes.
I get this error:
The superclass 'Bloc<NumberTriviaEvent, NumberTriviaState>' doesn't have a zero argument constructor.
Try declaring a zero argument constructor in 'Bloc<NumberTriviaEvent, NumberTriviaState>', or explicitly invoking a different constructor in 'Bloc<NumberTriviaEvent, NumberTriviaState>'.
This is the relevant part of the code:
class NumberTriviaBloc extends Bloc<NumberTriviaEvent, NumberTriviaState> {
final GetConcreteNumberTrivia getConcreteNumberTrivia;
final GetRandomNumberTrivia getRandomNumberTrivia;
final InputConverter inputConverter;
NumberTriviaBloc({
#required GetConcreteNumberTrivia concrete,
#required GetRandomNumberTrivia random,
#required this.inputConverter,
}) : assert(concrete != null),
assert(random != null),
assert(inputConverter != null),
getConcreteNumberTrivia = concrete,
getRandomNumberTrivia = random;
NumberTriviaState get initialState => Empty();
#override
Stream<NumberTriviaState> mapEventToState(
NumberTriviaEvent event,
) async* {
if (event is GetTriviaForConcreteNumber) {
final inputEither =
inputConverter.stringToUnsignedInteger(event.numberString);
yield* inputEither.fold(
(failure) async* {
yield Error(message: INVALID_INPUT_FAILURE_MESSAGE);
},
(integer) async* {
yield Loading();
final failureOrTrivia =
await getConcreteNumberTrivia(Params(number: integer));
yield* _eitherLoadedOrerrorState(failureOrTrivia);
},
);
} else if (event is GetTriviaForRandomNumber) {
yield Loading();
final failureOrTrivia = await getRandomNumberTrivia(NoParams());
yield* _eitherLoadedOrerrorState(failureOrTrivia);
}
}
Stream<NumberTriviaState> _eitherLoadedOrerrorState(
Either<Failure, NumberTrivia> failureOrTrivia,
) async* {
yield failureOrTrivia.fold(
(failure) => Error(message: _mapFailureToMessage(failure)),
(trivia) => Loaded(trivia: trivia),
);
}
String _mapFailureToMessage(Failure failure) {
switch (failure.runtimeType) {
case ServerFailure:
return SERVER_FAILURE_MESSAGE;
case CacheFailure:
return CACHE_FAILURE_MESSAGE;
default:
return 'Unexpected error';
}
}
}
I have tried adding a call to super in like this:
NumberTriviaBloc({
#required GetConcreteNumberTrivia concrete,
#required GetRandomNumberTrivia random,
#required this.inputConverter,
}) : assert(concrete != null),
assert(random != null),
assert(inputConverter != null),
getConcreteNumberTrivia = concrete,
getRandomNumberTrivia = random,
super(Empty());
This fixes the compilation error but I don't know what I should pass in to the super call.
I'm very new to Flutter and Dart (as in this is the first time I've looked at them in any depth).
Thanks for any insight you can give me.
I don't know what flutter bloc you used.
There is a change at setting initial state from v5.0.0.
https://bloclibrary.dev/#/migration?id=%E2%9D%97initialstate-has-been-removed
So you need to pass initial state at super constructor.
TLDR you should pass the initialState of your bloc.
See the official docs the problem is that the initialState is now passed to the super instead of being an attribute you have to override.
See this example of their docs as the state is of type int the initial state is 0 and is passed to the superclass.
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
}
Also you could use Cubit for more simple use cases instead of a full bloc.
Hey I've been having the same problem following this tutorial the answer is pretty simple.
All you have to do is to pass InitialNumberTriviaState() to the super constructor of bloc -
import 'dart:async';
import 'package:bloc/bloc.dart';
import './bloc.dart';
class NumberTriviaBloc extends Bloc<NumberTriviaEvent, NumberTriviaState> {
NumberTriviaBloc() : super(InitialNumberTriviaState());
#override
Stream<NumberTriviaState> mapEventToState(
NumberTriviaEvent event,
) async* {
// TODO: Add Logic
}
}
Problem
I'm using riverpod and state_notifier.
The initState() and update() that StateNotifier has are called and No. The other member functions can be called successfully. However, other member functions can be called successfully.
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_todo_list/todo_list_notifier.dart';
import 'package:riverpod_todo_list/todo_list_state.dart';
void main() {
print('start~~');
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends HookWidget {
// ...
}
final todoListProvider = StateNotifierProvider((_) => TodoListNotifier());
class MyHomePage extends HookWidget {
final _controller = TextEditingController();
final todoListNotifier = useProvider(todoListProvider);
final TodoListState _todoListState =
useProvider(todoListProvider.state.select((value) => value));
//...
import 'package:riverpod_todo_list/todo.dart';
import 'package:riverpod_todo_list/todo_list_state.dart';
import 'package:state_notifier/state_notifier.dart';
import 'package:uuid/uuid.dart';
class TodoListNotifier extends StateNotifier<TodoListState> with LocatorMixin {
TodoListNotifier() : super(const TodoListState());
Uuid _uuid = Uuid();
// could not run.
#override
void initState() {
super.initState();
print('init state~~~');
}
// could not run.
#override
void update(Locator watch) {
super.update(watch);
print('update');
}
// could run.
void add(String title) {
Todo todo = Todo(id: _uuid.v4(), title: title);
List<Todo> todoList = []..addAll(state.todoList);
todoList.add(todo);
state = state.copyWith(todoList: todoList);
}
// could run.
void toggleStatus(int index) {
List<Todo> todoList = []..addAll(state.todoList);
todoList[index] = state.todoList[index]
.copyWith(completed: !state.todoList[index].completed);
state = state.copyWith(todoList: todoList);
print('changed toggle~~');
}
}
restarted logs
not put initState() and update() logs.
Performing hot restart...
Restarted application in 464ms.
flutter: start~~
The question is already answered on the Github.
LocatorMixin is not supported by Riverpod.
https://github.com/rrousselGit/river_pod/issues/75#issuecomment-671255330
And it's proposed to note it in the document.
In my opinion, LocatorMixin is not needed to use with Riverpod because of ProvidierReference.
final userRepositoryProvider = Provider((ref) => UserRepository());
final userControllerProvider = StateNotifierProvider((ref) {
return UserController(
// Read userRepositoryProvider and create a UserController from the result
repository: ref.watch(userRepositoryProvider),
);
});