Flutter bloc event not getting called - flutter

Event is getting called on button onPressed
BlocProvider.of<ProfileBloc>(context).add(FetchProfile());
I am very new to bloc state management, please help me with this issue.
version flutter_bloc: ^8.0.1, just wanna try if its possible with the newer version.
class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
AuthRepo authRepo;
#override
ProfileBloc(ProfileState initialState, {required this.authRepo})
: super(ProfileLoading()) {
on<FetchProfile>((event, emit) async {
return await fetchProfileEvent(event, emit);
});
}
Future<void> fetchProfileEvent(
FetchProfile event, Emitter<ProfileState> emit) async {
log("$event", name: "eventToState");
emit(ProfileLoading());
try {
await authRepo.getProfileCall().then(
(value) {
log("$value", name: 'FetchProfile');
if (value != 'failed') {
emit(ProfileLoaded(userData: userProfileModelFromJson(value)));
} else {
emit(ProfileLoaded(userData: null));
}
},
);
} catch (e) {
log("$e", name: "ProfileBloc : FetchProfile");
emit(ProfileError());
}
}
}

Try it like this:
class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
AuthRepo authRepo;
#override
ProfileBloc({required this.authRepo})
: super(ProfileLoading()) {
on<FetchProfile>(fetchProfileEvent);
}
Future<void> fetchProfileEvent(
FetchProfile event, Emitter<ProfileState> emit) async {
log("$event", name: "eventToState");
emit(ProfileLoading());
try {
final value = await authRepo.getProfileCall();
log("$value", name: 'FetchProfile');
if (value != 'failed') {
emit(ProfileLoaded(userData: userProfileModelFromJson(value)));
} else {
emit(ProfileLoaded(userData: null));
}
} catch (e) {
log("$e", name: "ProfileBloc : FetchProfile");
emit(ProfileError());
}
}
}

Related

I can't list database data in Flutter

I have a problem, my database has data, but I can't list this data in the application, can you see where the problem is?
Here the database query is being implemented
#override
Stream<Either<TodoFailures, List<Todo>>> watchAll() async* {
//yield left(const InsufficientPermissions());
// users/{user ID}/notes/{todo ID}
final userDoc = await firestore.userDocument();
yield* userDoc.todoCollection
.snapshots()
.map((snapshot) => right<TodoFailures, List<Todo>>(snapshot.docs
.map((doc) => TodoModel.fromFirestore(doc).toDomain()).toList()))
.handleError((e) {
if (e is FirebaseException) {
if (e.code.contains('permission-denied') || e.code.contains("PERMISSION_DENIED")) {
return left(InsufficientPermisssons());
} else {
return left(UnexpectedFailure());
}
} else {
// ? check for the unauthenticated error
// ! log.e(e.toString()); // we can log unexpected exceptions
return left(UnexpectedFailure());
}
});
}
Below is where I capture the integrated query through the BloC
#injectable
class ObserverBloc extends Bloc<ObserverEvent, ObserverState> {
final TodoRepository todoRepository;
StreamSubscription<Either<TodoFailures, List<Todo>>>? todoStreamSubscription;
ObserverBloc({required this.todoRepository}) : super(ObserverInitial()) {
on<ObserverEvent>((event, emit) async {
emit(ObserverLoading());
await todoStreamSubscription?.cancel();
todoStreamSubscription = todoRepository
.watchAll()
.listen((failureOrTodos) => add(TodosUpdatedEvent(failureOrTodos: failureOrTodos)));
});
on<TodosUpdatedEvent>((event, emit) {
event.failureOrTodos.fold((failures) => emit(ObserverFailure(todoFailure: failures)),
(todos) => emit(ObserverSuccess(todos: todos)));
});
}
#override
Future<void> close() async {
await todoStreamSubscription?.cancel();
return super.close();
}
}
Even containing data in the database it comes empty, I need help to know where the problem is.

How to fix LateInitializationError about non-nullable variables?

I was studying with an AWS tutorial however, that tutorial is not null safety.
I'v tried to convert it, but is showing:
LateInitializationError: Field '_credentials#26120019' has not been initialized.
I think that "late" modifier is not initializing the variables when I try to get the values in verifyCode
Pleease, how can I fix the code below
enum AuthFlowStatus {login, signUp, verification, session}
class AuthState {
final AuthFlowStatus? authFlowStatus;
AuthState({this.authFlowStatus});
}
class AuthService {
final authStateController = StreamController<AuthState>();
late AuthCredentials _credentials;
void showSignUp() {
final state = AuthState(authFlowStatus: AuthFlowStatus.signUp);
authStateController.add(state);
}
void showLogin() {
final state = AuthState(authFlowStatus: AuthFlowStatus.login);
authStateController.add(state);
}
void loginWithCredentials(AuthCredentials credentials) async {
try {
final result = await Amplify.Auth.signIn(
username: credentials.username, password: credentials.password,
);
if (result.isSignedIn) {
final state = AuthState(authFlowStatus: AuthFlowStatus.session);
authStateController.add(state);
} else {
print('User could not be signed in');
}
} on AuthException catch (authError) {
print('Could not login - ${authError}');
}
}
void signUpWithCredentials(SignUpCredentials credentials) async {
try {
Map<CognitoUserAttributeKey, String> userAttributes = {
CognitoUserAttributeKey.email: credentials.email,
};
final result = await Amplify.Auth.signUp(
username: credentials.username,
password: credentials.password,
options: CognitoSignUpOptions(
userAttributes: userAttributes
),
);
if (result.isSignUpComplete) {
loginWithCredentials(credentials);
} else {
this._credentials = credentials;
}
final state = AuthState(authFlowStatus: AuthFlowStatus.verification);
authStateController.add(state);
} on AmplifyException catch (authError) {
print('Failed ro sign up - ${authError}');
}
}
void verifyCode(String verificationCode) async {
try {
final result = await Amplify.Auth.confirmSignUp(
username: _credentials.username,
confirmationCode: verificationCode,
);
if (result.isSignUpComplete) {
loginWithCredentials(_credentials);
} else {
//not implemented yet
}
} on AuthException catch (authError) {
print('Could not verify code - ${authError}');
}
}
}

flutter how to yield to a stream of bloc?

Hi I'm new to flutter and dart. I'm following a lesson on internet which is practicing to use bloc to control states. First lesson is after showing appStart animation, turn to a login page.
the lesson was using 'mapEventToState':
class AuthenticationBloc extends Bloc<AuthenticationEvent, AuthenticationState> {
final UserRepository? _userRepository;
AuthenticationBloc({UserRepository? userRepository})
: assert(userRepository != null),
_userRepository = userRepository, super(Uninitialized());
#override
Stream<AuthenticationState> mapEventToState(
AuthenticationEvent event,
) async* {
if (event is AppStarted) {
yield* _mapAppStartedToState();
} else if (event is LoggedIn) {
yield* _mapLoggedInToState();
} else if (event is LoggedOut) {
yield* _mapLoggedOutToState();
}
Stream<AuthenticationState> _mapAppStartedToState() async* {
log('_mapAppStartedToState is running.');
try {
final bool? isSigned = await _userRepository?.isSignedIn();
if (isSigned != null) {
if (isSigned) {
final String? name = await _userRepository?.getUser();
yield Authenticated(name);
}
else {
yield Unauthenticated();
}
}
} catch (_) {
yield Unauthenticated();
}
}
Stream<AuthenticationState> _mapLoggedInToState() async* {
log('_mapLoggedInToState is running.');
yield Authenticated(await _userRepository?.getUser());
}
Stream<AuthenticationState> _mapLoggedOutToState() async* {
log('_mapLoggedOutToState is running.');
yield Unauthenticated();
_userRepository?.signOut();
}
}
turns out 'mapEventToState' was removed.
According to this page(https://github.com/felangel/bloc/issues/2526), I try to use on< event > instead:
#override
AuthenticationBloc({UserRepository? userRepository})
: assert(userRepository != null, 'userRepository == null'),
_userRepository = userRepository,
super(Uninitialized()) {
log('AuthenticationBloc is running.');
on<AppStarted>(_appStarted);
on<LoggedIn>(_loggedIn);
on<LoggedOut>(_loggedOut);
}
Stream<AuthenticationState> _appStarted(AuthenticationEvent event, Emitter<AuthenticationState> emit) async* {
log('_appStarted is running.');
yield* _mapAppStartedToState();
}
But it didn't work. Even log('_appStarted is running.'); didn't show at console.
I tried to change type and aync*. It would show console log if _appStarted isn't aync.
void _appStarted(AuthenticationEvent event, Emitter<AuthenticationState> emit) {
log('_appStarted is running.');
// yield* _mapAppStartedToState();
}
However, it can't yield to stream as _appStarted isn't aync. Makes me confused.
Please let me know if I got some misunderstand about bloc and stream. Happy to see any solution or advise.
You no longer need your one function per event, because you already have it:
void _appStarted(AuthenticationEvent event, Emitter<AuthenticationState> emit) {
log('_appStarted is running.');
try {
final bool? isSigned = await _userRepository?.isSignedIn();
if (isSigned != null) {
if (isSigned) {
final String? name = await _userRepository?.getUser();
emit(Authenticated(name));
}
else {
emit(Unauthenticated());
}
}
} catch (_) {
emit(Unauthenticated());
}
}
If you want to delegate this to another function, just remove the stream return value and pass the emitter.

Succeeding Bloc are not working after latest Bloc migration

I am using a MultiBlocProvider which is working for all Bloc before I migrate it to v8.0.1. Now, only the first Bloc (SignInBloc) is working.
This is on my main.dart
return MultiBlocProvider(
providers: [
BlocProvider<SignInBloc>(
create: (context) => SignInBloc(
authenticationRepository: authenticationRepository,
userDataRepository: userDataRepository,
),
),
BlocProvider<SignUpBloc>(
create: (context) => SignUpBloc(
authenticationRepository: authenticationRepository,
userDataRepository: userDataRepository,
),
),
Edit: here is my SignInBloc
SignInBloc(
{required this.authenticationRepository,
required this.userDataRepository})
: super(SignInInitialState()) {
on<CheckIfSignedInEvent>(mapCheckIfSignedInEventToState);
}
Future<void> mapCheckIfSignedInEventToState(
CheckIfSignedInEvent event,
Emitter<SignInState> emit,
) async {
try {
bool isSignedIn = await authenticationRepository.checkIfSignedIn();
if (isSignedIn) {
emit(CheckIfSignedInEventCompletedState(true));
} else {
emit(CheckIfSignedInEventCompletedState(false));
}
} catch (e) {
print(e);
emit(CheckIfSignedInEventFailedState());
}
}
I am not sure what to show but here is my SignUpBloc which is similar to my SignInBloc
SignUpBloc(
{required this.authenticationRepository,
required this.userDataRepository})
: super(SignUpInitialState()) {
on<SignUpWithGoogle>(mapSignUpWithGoogleEventToState);
}
Stream<SignUpState> mapSignUpWithGoogleEventToState(
SignUpWithGoogle event,
Emitter<SignUpState> emit,
) async* {
emit(SignUpInProgressState());
try {
User? checkUser = await authenticationRepository.checkIfUserExists();
if (checkUser != null) {
emit(SignUpWithGoogleInitialExistState());
} else {
bool checkDup =
await authenticationRepository.checkIfUserDup(event.name);
if (checkDup == true) {
emit(SignUpWithNameExistState());
} else {
User firebaseUser = await authenticationRepository.signUpWithGoogle();
emit(SignUpWithGoogleInitialCompletedState(firebaseUser));
}
}
} catch (e) {
print(e);
emit(SignUpWithGoogleInitialFailedState());
}
}
My main.dart will call the splash screen which has the declaration of the bloc
late SignInBloc signInBloc;
late SignUpBloc signupBloc;
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
signInBloc = BlocProvider.of<SignInBloc>(context);
signupBloc = BlocProvider.of<SignUpBloc>(context);
What I tried to do it to put alot of Print statement in order to check which part is getting called but I don't get why the SignUpBloc is not getting called anymore. Please help. Thanks!
Edit: I tried to debug.
This will trigger my SignInBloc. I'm able to listen to my SignInBloc.
signInBloc.add(CheckIfSignedInEvent());
This should trigger my SignUpBloc. But it doesn't do anything similar to my SignInBloc.
signupBloc.add(SignUpWithGoogle(name: selectedName));
Here's both of my events for comparison:
class CheckIfSignedInEvent extends SignInEvent {
#override
String toString() => 'CheckIfSignedInEvent';
}
class SignUpWithGoogle extends SignUpEvent {
final String name;
SignUpWithGoogle({required this.name});
#override
String toString() => 'SignUpWithGoogleEvent';
}
This is the part where I listen to the states which is both in my splash screen. Only signInBloc is able to listen.
signupBloc.stream.listen((state) {
print('BLOC: signupBloc splash screen init : $state');
});
signInBloc.stream.listen((state) {
print('BLOC: signinBloc splash screen init : $state');
});
It turns out that changing the Stream to Future will fix my issue. async* should also be changed to async
Future<void> mapSignUpWithGoogleEventToState(
SignUpWithGoogle event,
Emitter<SignUpState> emit,
) async {
emit(SignUpInProgressState());
try {
User? checkUser = await authenticationRepository.checkIfUserExists();
if (checkUser != null) {
emit(SignUpWithGoogleInitialExistState());
} else {
bool checkDup =
await authenticationRepository.checkIfUserDup(event.name);
if (checkDup == true) {
emit(SignUpWithNameExistState());
} else {
User firebaseUser = await authenticationRepository.signUpWithGoogle();
emit(SignUpWithGoogleInitialCompletedState(firebaseUser));
}
}
} catch (e) {
print(e);
emit(SignUpWithGoogleInitialFailedState());
}
}

How can I write "Event1 'or' Event2" inside on<Event> method from flutter_bloc?

That's my code for PostsBloc:
class PostsBloc extends Bloc<PostsEvent, PostsState> {
final _dataService = DataService();
// Constructor
PostsBloc() : super(LoadingPostsState()) {
on<LoadPostsEvent>((event, emit) async {
emit(LoadingPostsState());
try {
final posts = await _dataService.getPosts();
emit(LoadedPostsState(posts: posts));
} catch (e) {
emit(FailedToLoadPostsState(error: e));
}
});
}
}
So, I want to use the same method with new event, just without emitting LoadingPostsState() like this:
PostsBloc() : super(LoadingPostsState()) {
on<LoadPostsEvent || PullToRefreshEvent>((event, emit) async {
if(event == LoadPostsEvent){
emit(LoadingPostsState());
}
try {
final posts = await _dataService.getPosts();
emit(LoadedPostsState(posts: posts));
} catch (e) {
emit(FailedToLoadPostsState(error: e));
}
});
}
What you want is the is operator:
if (event is LoadPostsEvent)
However you run into another problem:
on<LoadPostsEvent || PullToRefreshEvent>
this is not a thing. I believe you have two options:
Either make a new event X and have LoadPostsEvent and PullToRefreshEvent extend it, like this:
class LoadEvent extends PostsEvent { ... }
class LoadPostsEvent extends LoadEvent { ... }
class PullToRefreshEvent extends LoadEvent { ... }
on<LoadEvent>((event, emit) {
if (event is LoadPostsEvent)
});
or, in order to minimize code repetition, declare this event handler as a function
on<LoadPostsEvent>(_loadEvent);
on<PullToRefreshEvent>(_loadEvent);
...
void _loadEvent(PostsEvent event, Emitter<PostsState> emit) {
...
}