i am Working on Flutter Chat Application and when i tried to implement the MessageBloc i found that mapEventToState is not Working anymore in Version 8.0 of Bloc
This is MessageEvent
import 'package:chat/chat.dart';
import 'package:equatable/equatable.dart';
abstract class MessageEvent extends Equatable {
const MessageEvent();
factory MessageEvent.onSubscribed(User user) => Subscribed(user);
factory MessageEvent.onMessageSent(List<Message> messages) =>
MessageSent(messages);
#override
List<Object?> get props => [];
}
class Subscribed extends MessageEvent {
final User user;
Subscribed(this.user);
#override
List<Object?> get props => [user];
}
class MessageSent extends MessageEvent {
final List<Message> messages;
MessageSent(this.messages);
#override
List<Object> get props => [messages];
}
class MessageReceived extends MessageEvent {
MessageReceived(this.message);
final Message message;
#override
List<Object> get props => [message];
}
And this is MessageState
import 'package:chat/chat.dart';
import 'package:equatable/equatable.dart';
abstract class MessageState extends Equatable {
const MessageState();
factory MessageState.initial() => MessageInitial();
factory MessageState.sent(Message message) => MessageSentSuccesfully(message);
factory MessageState.received(Message message) =>
MessageReceivedSuccesfully(message);
#override
List<Object> get props => [];
}
class MessageInitial extends MessageState {}
class MessageSentSuccesfully extends MessageState {
final Message message;
const MessageSentSuccesfully(this.message);
#override
List<Object> get props => [message];
}
class MessageReceivedSuccesfully extends MessageState {
final Message message;
const MessageReceivedSuccesfully(this.message);
#override
List<Object> get props => [message];
}
and finally this is MessageBloc
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:chat/chat.dart';
import 'package:flutter_newapp/src/blocs/message/message_event.dart';
import 'package:flutter_newapp/src/blocs/message/message_state.dart';
class MessageBloc extends Bloc<MessageEvent, MessageState> {
final IMessageService _messageService;
StreamSubscription? _subscription;
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) {
final message = await _messageService.send(event.messages);
yield MessageState.sent(message);
}
}
#override
Future<void> close() {
_subscription?.cancel();
_messageService.dispose();
return super.close();
}
}
Can anyone propose me a Solution to Rewrite the MessageBloc in the new Version of Bloc ?
i am trying to move from version 7.1 of Bloc to 8.1 or the Latest version of Bloc and try to learn the new implementation of Blocs in Flutter
Related
I have a bloc listening to chats. Through the debugger, I can see that the bloc is catching live updates. However, the ui is not rebuilding when these changes occur. To see a change, I have to leave and reload the screen.
My chat state:
part of 'chat_bloc.dart';
abstract class ChatState extends Equatable {
const ChatState();
#override
List<Object?> get props => [];
}
class ChatLoading extends ChatState {}
class ChatLoaded extends ChatState {
final List<Chat?>? compiledChats;
const ChatLoaded({required this.compiledChats});
#override
List<Object?> get props => [compiledChats];
}
My chat events:
part of 'chat_bloc.dart';
abstract class ChatEvent extends Equatable {
const ChatEvent();
#override
List<Object?> get props => [];
}
class LoadChat extends ChatEvent {
const LoadChat();
#override
List<Object> get props => [];
}
class CloseChat extends ChatEvent {
const CloseChat();
#override
List<Object?> get props => [];
}
class UpdateChat extends ChatEvent {
final List<List<Chat?>> chats;
const UpdateChat({required this.chats});
#override
List<Object> get props => [chats];
}
My actual bloc:
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:hero/blocs/auth/auth_bloc.dart';
import 'package:hero/models/chat_model.dart';
import 'package:hero/models/user_model.dart';
import 'package:hero/repository/firestore_repository.dart';
part 'chat_event.dart';
part 'chat_state.dart';
class ChatBloc extends Bloc<ChatEvent, ChatState> {
final FirestoreRepository _firestoreRepository;
late StreamSubscription _chatListener;
ChatBloc({
required FirestoreRepository firestoreRepository,
}) : _firestoreRepository = firestoreRepository,
super(ChatLoading()) {
on<LoadChat>(_onLoadChat);
on<UpdateChat>(_onUpdateChat);
on<CloseChat>(_onCloseChat);
}
void _onLoadChat(
LoadChat event,
Emitter<ChatState> emit,
) {
_chatListener = _firestoreRepository.chats.listen((chats) {
add(
UpdateChat(
chats: chats,
),
);
});
}
void _onUpdateChat(
UpdateChat event,
Emitter<ChatState> emit,
) {
//generate compiledChats from event.chats
List<Chat?>? compiledChats = [];
for (List<Chat?> chatList in event.chats) {
for (Chat? chat in chatList) {
if (chat != null) {
compiledChats.add(chat);
}
}
}
emit(ChatLoaded(compiledChats: compiledChats));
}
void _onCloseChat(
CloseChat event,
Emitter<ChatState> emit,
) {
_chatListener.cancel();
print('ChatBloc disposed');
emit(ChatLoading());
}
#override
Future<void> close() async {
super.close();
}
}
For the scope of this problem, all that really matters is just looking at the updateChat and LoadChat methods. Any ideas? Thanks!
How can I migrate to flutter_bloc latest version?
I am facing this error
════════ Exception caught by gesture ═══════════════════════════════════════════
Bad state: add(NextMonthCalendar) was called without a registered event handler.
Make sure to register a handler via on((event, emit) {...})
here is my code it was built on flutter_bloc before version 5.0
import 'package:equatable/equatable.dart';
import 'package:abushakir/abushakir.dart';
abstract class CalendarEvent extends Equatable {
const CalendarEvent();
#override
List<Object> get props => [];
}
class TodaysCalendar extends CalendarEvent {
final ETC currentMonth;
const TodaysCalendar(this.currentMonth);
#override
List<Object> get props => [currentMonth];
#override
String toString() => "Today's Calendar { Calendar: $currentMonth }";
}
class NextMonthCalendar extends CalendarEvent {
final ETC currentMonth;
const NextMonthCalendar(this.currentMonth);
#override
List<Object> get props => [currentMonth.nextMonth];
#override
String toString() =>
"Next Month's Calendar { Calendar: ${currentMonth.nextMonth} }";
}
class PrevMonthCalendar extends CalendarEvent {
final ETC currentMonth;
const PrevMonthCalendar(this.currentMonth);
#override
List<Object> get props => [currentMonth.prevMonth];
#override
String toString() =>
"Previous Month's Calendar { Calendar: ${currentMonth.prevMonth} }";
}
I want the selected theme to be saved when exiting the application and the next time the application is launched, the theme that is saved is selected there
Code:
theme_bloc:
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
ThemeBloc()
: super(ThemeState(false)) {
on<ThemeEvent>((event, emit) {
emit(ThemeState(event.isDark));
});
}
}
theme_state:
#immutable
class ThemeState extends Equatable {
CupertinoThemeData get themeData => isDark
? AppThemes.appThemeData[AppTheme.DarkAppTheme]!
: AppThemes.appThemeData[AppTheme.LightAppTheme]!;
ThemeState(this.isDark);
final bool isDark;
#override
List<Object?> get props => [isDark];
}
theme_event:
#immutable
class ThemeEvent extends Equatable {
final bool isDark;
ThemeEvent(this.isDark);
#override
List<Object?> get props => [isDark];
}
I'm trying to implement blocTesting for my flutter app starting with authentication feature. Below are the Authentication and login related files required for this. I'd really appreciate if someone could show me on how I can implement blocTesting based on my code because I've been facing problems in doing so. Below are the bloc, state and event files for the auth bloc.
Authbloc.dart
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
part 'authentication_event.dart';
part 'authentication_state.dart';
class AuthenticationBloc extends Bloc<AuthenticationEvent, AuthenticationState> {
final AuthenticationRepository authenticationRepository = AuthenticationRepository();
final SettingsRepository _settingsRepository = SettingsRepository();
AuthenticationBloc() : super(AuthenticationInitial()) {
// Register events here
on<AuthenticationStarted>(_onAuthenticationStarted);
on<AuthenticationLoggedIn>(_onAuthenticationLoggedIn);
on<AuthenticationLoggedOut>(_onAuthenticationLoggedOut);
}
Future<void> _onAuthenticationStarted(AuthenticationStarted event, Emitter<AuthenticationState> emit) async {
try {
final bool hasToken = await authenticationRepository.hasToken();
if (hasToken) {
final Settings _settings = await _settingsRepository.getSettings();
final SysConfig _sysConfig = await _settingsRepository.getSysconfig();
final CountriesModelList _countries = await _settingsRepository.getCountries();
final ReasonsModelList _reasons = await _settingsRepository.getReasons();
final NotificationOptionsList _notificationOptions = await _settingsRepository.getNotificationOptions();
emit(
AuthenticationLoadSuccess(
settings: _settings,
sysConfig: _sysConfig,
countries: _countries,
reasons: _reasons,
notificationOptions: _notificationOptions,
),
);
} else {
emit(AuthenticationUnauthenticated());
}
} catch (e) {
final MYException _exception = e as MYException;
emit(AuthenticationLoadFailure(exception: _exception));
}
}
Future<void> _onAuthenticationLoggedIn(AuthenticationLoggedIn event, Emitter<AuthenticationState> emit) async {
emit(AuthenticationLoadInProgress());
await authenticationRepository.persistToken(event.token);
final Settings _settings = await _settingsRepository.getSettings();
final SysConfig _sysConfig = await _settingsRepository.getSysconfig();
final CountriesModelList _countries = await _settingsRepository.getCountries();
final ReasonsModelList _reasons = await _settingsRepository.getReasons();
final NotificationOptionsList _notificationOptions = await _settingsRepository.getNotificationOptions();
emit(
AuthenticationLoadSuccess(
settings: _settings,
sysConfig: _sysConfig,
countries: _countries,
reasons: _reasons,
notificationOptions: _notificationOptions,
),
);
}
Future<void> _onAuthenticationLoggedOut(AuthenticationLoggedOut event, Emitter<AuthenticationState> emit) async {
await authenticationRepository.deleteToken();
await Future<dynamic>.delayed(const Duration(seconds: 2));
emit(AuthenticationUnauthenticated());
add(AuthenticationStarted());
}
}
Authstate.dart
part of 'authentication_bloc.dart';
abstract class AuthenticationEvent extends Equatable {
const AuthenticationEvent();
#override
List<Object> get props => <Object>[];
}
class AuthenticationStarted extends AuthenticationEvent {}
class AuthenticationLoggedIn extends AuthenticationEvent {
final String token;
const AuthenticationLoggedIn({required this.token});
#override
List<Object> get props => <Object>[token];
}
class AuthenticationLoggedOut extends AuthenticationEvent {}
AuthEvent.dart
part of 'authentication_bloc.dart';
abstract class AuthenticationState extends Equatable {
const AuthenticationState();
#override
List<Object> get props => <Object>[];
}
class AuthenticationInitial extends AuthenticationState {}
class AuthenticationUnauthenticated extends AuthenticationState {}
class AuthenticationLoadSuccess extends AuthenticationState {
final SysConfig sysConfig;
final Settings settings;
final CountriesModelList countries;
final ReasonsModelList reasons;
final NotificationOptionsList notificationOptions;
const AuthenticationLoadSuccess({required this.sysConfig, required this.settings, required this.countries, required this.reasons, required this.notificationOptions});
#override
List<Object> get props => <Object>[sysConfig, settings, countries, reasons, notificationOptions];
}
class AuthenticationLoadInProgress extends AuthenticationState {}
class AuthenticationLoadFailure extends AuthenticationState {
final MYException exception;
const AuthenticationLoadFailure({required this.exception});
#override
List<Object> get props => <Object>[exception];
}
you have to change a lot of thinks.
First of all you need to add the repository/ies to your bloc constructor to inject the mocks.
class AuthenticationBloc extends Bloc<AuthenticationEvent, AuthenticationState> {
late final AuthenticationRepository authenticationRepository;
final SettingsRepository _settingsRepository = SettingsRepository();
AuthenticationBloc({required this.authenticationRepository}) : super(AuthenticationInitial()) {
// Register events here
on<AuthenticationStarted>(_onAuthenticationStarted);
on<AuthenticationLoggedIn>(_onAuthenticationLoggedIn);
on<AuthenticationLoggedOut>(_onAuthenticationLoggedOut);
}
Then you can use the mock when creating the bloc in the setup method
setUp(() {
authenticationRepositoryMock = MockWeatherRepository();
authenticationBloc = AuthenticationBloc(authenticationRepository: authenticationRepositoryMock );
});
Then you have to return that bloc in the build function of your blocTest and also you have to setup the mock behavior there
build: () {
when(() => authenticationRepositoryMock .hasToken()).thenAnswer((_) async => true);
return bloc;
},
Then add an event to your bloc in the act function
act: (dynamic b) => b.add(AuthenticationStarted()),
And then you can check the result in the expect function. (i think the initial state will not be emitted here)
expect: () => [
AuthenticationLoadSuccess(...),
It also a good idea to mock the SettingsRepository.
I just started learning bloc and I'm trying to make a cart bloc but I'm getting a state error:
Error: Unhandled error Expected a value of type 'CartSuccess', but got one of type 'CartLoading' occurred in bloc Instance of 'CartBloc'.
Even if I delete the yield CartLoading(), the error change to:
Error: Unhandled error Expected a value of type 'CartSuccess', but got one of type 'CartInitial' occurred in bloc Instance of 'CartBloc'.
Cart State
abstract class CartState extends Equatable {
const CartState();
#override
List<Object> get props => [];
}
class CartInitial extends CartState {}
class CartLoading extends CartState {}
class CartSuccess extends CartState {
final List<ProductModel> carts;
const CartSuccess(this.carts);
#override
List<Object> get props => [carts];
}
class CartFailed extends CartState {}
Cart Event
abstract class CartEvent extends Equatable {
const CartEvent();
#override
List<Object> get props => [];
}
class FetchCart extends CartEvent {}
class AddToCart extends CartEvent {
final ProductModel product;
const AddToCart(this.product);
#override
List<Object> get props => [product];
}
class RemoveFromCart extends CartEvent {
final ProductModel product;
const RemoveFromCart(this.product);
#override
List<Object> get props => [product];
}
Cart Bloc
class CartBloc extends Bloc<CartEvent, CartState> {
CartBloc() : super(CartInitial());
#override
Stream<CartState> mapEventToState(CartEvent event) async* {
yield CartLoading();
if(event is FetchCart){
final List<ProductModel> carts = List.from((state as CartSuccess).carts);
yield CartSuccess(carts);
}
if(event is AddToCart){
try{
final List<ProductModel> carts = List.from((state as CartSuccess).carts)
..add(event.product);
yield CartSuccess(carts);
}catch(e){
yield CartFailed();
print(e);
}
}
if(event is RemoveFromCart){
try{
final carts = (state as CartSuccess)
.carts
.where((product) => product.id != event.product.id)
.toList();
yield CartSuccess(carts);
}catch(e){
yield CartFailed();
print(e);
}
}
}
}
I guess this comes from here: List.from((state as CartSuccess).carts). You yield yield CartLoading();before this, so your state is no longer CartSuccess but CartLoading. You previous CartSuccess state was lost with yielding CartLoading