Flutter Either fold is skipped - flutter

I'm working on a small app with GoogleSignIn-Auth. and stumbled upon a bug I cannot wrap my head around.
It seems like the fold of an Either seems to be skipped. It used to work before, when I had a complicated pile of blocs. Since I started reorganizing my widgets it started to this.
Future<Either<Failure, SignUpSuccess>> signInWithGoogle() async {
try {
final signUpSuccess = await googleRemoteDataSource.signInWithGoogle();
signUpSuccess.fold(
(failure) => () {
print("Got failure!");
return Left(GeneralFailure());
},
(success) => () {
return Right(signUpSuccess);
});
print("I skipped the fold!");
} catch (e) {
print("Caught exception!");
return Left(GeneralFailure());
}
print("Instant fail!");
return Left(GeneralFailure());
}
I have a widget that's listening to a SignInBloc emitting the states:
class SignUpRoot extends StatelessWidget {
SignUpRoot({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => sl<SignInBloc>(),
child: BlocListener<SignInBloc, SignInState>(
listener: (context, state) {
if (state is SignInWithGoogleLoaded) {
// Navigate to SignInNamePage
print("This seems to work!");
} else if (state is SignInWithGoogleLoading) {
// Navigate to loading page
print("Loading Google...");
} else if (state is SignInError) {
// Navigate to error page
print("An error occured while signing in!");
}
},
child: const SignUpMainPage(),
)));
}
And last but not least my bloc:
class SignInBloc extends Bloc<SignInEvent, SignInState> {
final SignUpUseCases useCases;
SignInBloc({required this.useCases}) : super(SignInInitial()) {
on<SignInWithGooglePressed>((event, emit) async {
// Show Loading indicator
emit(SignInWithGoogleLoading());
// wait for sign in response
Either<Failure, SignUpSuccess> successOrFailure =
await useCases.signInWithGoogle();
// emit corresponding state
successOrFailure.fold(
(failure) => emit(SignInError()),
(success) => () {
// emit sign in loaded state
emit(SignInWithGoogleLoaded());
// create new (local) user
// assign user data e.g. display name
});
});
}
}
Thanks for any help!

The problem is that the fold method returns the value of the left or right functions.
https://pub.dev/documentation/dartz/latest/dartz/Either/fold.html
B fold<B>(
B ifLeft(
L l
),
B ifRight(
R r
)
)
Your code should be corrected to:
Future<Either<Failure, SignUpSuccess>> signInWithGoogle() async {
try {
final signUpSuccess = await googleRemoteDataSource.signInWithGoogle();
return signUpSuccess.fold(
(failure) => () {
return Left(GeneralFailure());
},
(success) => () {
return Right(signUpSuccess);
});
} catch (e) {
return Left(GeneralFailure());
}
return Left(GeneralFailure());
}
I just added the return at the start of the fold, now the value returned from left or right will be returned by your function.

You just have to remove the arrow in the success part of the fold.
Future<Either<Failure, SignUpSuccess>> signInWithGoogle() async {
try {
final signUpSuccess = await googleRemoteDataSource.signInWithGoogle();
signUpSuccess.fold(
(failure) => () {
print("Got failure!");
return Left(GeneralFailure());
},
(success){
return Right(signUpSuccess);
});
print("I skipped the fold!");
} catch (e) {
print("Caught exception!");
return Left(GeneralFailure());
}
print("Instant fail!");
return Left(GeneralFailure());
}
class SignInBloc extends Bloc<SignInEvent, SignInState> {
final SignUpUseCases useCases;
SignInBloc({required this.useCases}) : super(SignInInitial()) {
on<SignInWithGooglePressed>((event, emit) async {
// Show Loading indicator
emit(SignInWithGoogleLoading());
// wait for sign in response
Either<Failure, SignUpSuccess> successOrFailure =
await useCases.signInWithGoogle();
// emit corresponding state
successOrFailure.fold(
(failure) => emit(SignInError()),
(success) {
emit(SignInWithGoogleLoaded());
});
});
}
}

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.

Flutter bloc emit() and SchedulerBinding

I use a SavingState during which I update my User object.
For this I use the SchedulerBindings.
However when creating a user: my object is not update. It updates too late. Why ?
Update : Strangely the same code for user update works
In my form :
Widget build(BuildContext context) {
isUpdateMode = widget.isUpdateMode;
return BlocBuilder<UserBloc, UserState>(builder: (context, state) {
if (state is ItemLoadedState || state is IsSavedState) {
SchedulerBinding.instance.addPostFrameCallback(
(_) => _data = generateItems(_buildPanelList()));
return _buildForm();
}
if (state is SavingState) {
SchedulerBinding.instance.addPostFrameCallback((_) => _savedForm());
print('SavingState: ${item.toJson()}');
return _buildForm();
}
if (state is FailState) return ErrorUI(message: state.message);
if (state is LoadingState) return buildLoading();
return ErrorUI(message: l10n.noDataText);
});
}
In my bloc
Future<void> _onCreateUser(
CreateUserEvent event, Emitter<UserState> emit) async {
try {
emit(SavingState());
print('bloc: ${event.item!.toJson()}');
final item = await repository.create(event.item!);
emit(IsSavedState(user: item));
} on HttpException catch (error) {
emit(
FailState(
message: 'Failed to create user ${event.item!.pseudo}.\n\n$error'),
);
} catch (e) {
emit(
FailState(message: 'Failed to create user ${event.item!.pseudo}.'),
);
}
}
Future<void> _onUpdateUser(
UpdateUserEvent event, Emitter<UserState> emit) async {
try {
emit(SavingState());
final item = await repository.update(event.item!);
emit(IsSavedState(user: item));
} catch (e) {
emit(
FailState(message: 'Failed to update user ${event.item!.pseudo}.'),
);
}
}
In my print I have : bloc before SavingState.
Thanks,

Flutter call function from other class?

Hi I am building flutter function which able to capture qr code and after display the result after user scan QR Code. I need user to be able navigate to previous scanning screen by using button if they need rescan or scan new qr code. This is code for button which on Scanview class.
SizedBox(height: 40,),
CupertinoButton(
color: Color(0xFF88070B),
child:Text("Re-scan QR "),
onPressed: (){
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => **ScanFunction**(),
),
);
}
)
I want to call this _scanQR method from other class ScanFunction . What is proper way when user tap Re-scan QR button and call _scanQR method which is on other class? How to access method from other class? Thanks for help.
class ScanFunction extends StatefulWidget {
#override
ScanFunctionState createState() {
return ScanFunctionState();
}
}
class ScanFunctionState extends State<ScanFunction> {
String result = "Maklumat Inventori";
Future _scanQR() async {
try {
String qrResult = await BarcodeScanner.scan();
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Scanresultview(qrResult),
),
);
} on PlatformException catch (ex) {
if (ex.code == BarcodeScanner.CameraAccessDenied) {
setState(() {
result = "Kebenaran kamera telah ditolak";
});
} else {
setState(() {
result = "Ralat tidak diketahui$ex";
});
}
} on FormatException {
setState(() {
result = "Anda menekan butang belakang sebelum mengimbas apa-apa";
});
} catch (ex) {
setState(() {
result = "Ralat tidak diketahui $ex";
});
}
}
You can call this method from initState.
class ScanFunction extends StatefulWidget {
#override
ScanFunctionState createState() {
return ScanFunctionState();
}
}
class ScanFunctionState extends State<ScanFunction> {
#override
void initState() {
_scanQR();
super.initState();
}
String result = "Maklumat Inventori";
Future _scanQR() async {
try {
String qrResult = await BarcodeScanner.scan();
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Scanresultview(qrResult),
),
);
} on PlatformException catch (ex) {
if (ex.code == BarcodeScanner.CameraAccessDenied) {
setState(() {
result = "Kebenaran kamera telah ditolak";
});
} else {
setState(() {
result = "Ralat tidak diketahui$ex";
});
}
} on FormatException {
setState(() {
result = "Anda menekan butang belakang sebelum mengimbas apa-apa";
});
} catch (ex) {
setState(() {
result = "Ralat tidak diketahui $ex";
});
}
}

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());
}
}

StreamProvider and TCP Socket

I wrote an one-page desktop app to communicate with TCP Server.
In my code, I use Socket.listen() method to receive data and it is OK.
I used single subscription and it was enough for me.
I tried to convert it to StreamProvider[Riverpod] and I failed.
I used StreamController() then I get bad state.
I used StreamController.broadcast() and I couldn't get data from socket
Could you suggest me correct way?
For a side note: I'm not an experienced flutter developer, just try to learn :)
I added code blocks to below and also full code.
For the full code: https://gist.github.com/sphinxlikee/3cbfa47817a5187c7b67905028674041
UI:
Working code;
Future<void> createConnection() async {
try {
_socket = await Socket.connect(serverAddress, serverPort);
_changeConnectionState();
} catch (e) {
print('connection has an error and socket is null.');
print(e);
return;
}
listenSocket();
}
void listenSocket() {
_socket.listen(
(event) {
_getData(String.fromCharCodes(event));
print('received: $receivedData');
if (!_dataReceived) {
_changeDataReceivedState();
}
},
)
..onDone(
() {
_changeConnectionState();
_streamDone();
print('socket is closed');
},
)
..onError(
(error, stackTrace) {
print('$error');
},
);
}
Working code - UI side
class ReceivedData extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
final receivedData = watch(tcpClientProvider).receivedData;
return Text('Received data: $receivedData');
}
}
For the StreamProvider I tried,
Future<void> createConnection() async {
try {
_socket = await Socket.connect(serverAddress, serverPort);
streamController.sink.add(_socket.listen((event) => String.fromCharCodes(event)));
_changeConnectionState();
} catch (e) {
print('connection has an error and socket is null.');
print(e);
return;
}
}
StreamProvider - UI side
final streamProvider = StreamProvider.autoDispose(
(ref) async* {
await for (final value in ref.watch(tcpClientProvider).streamController.stream) {
yield value;
}
},
);
class ReceivedDataWithProvider extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
AsyncValue receivedData = watch(streamProvider);
return receivedData.when(
data: (data) => Text('Received data: $data'),
loading: () => const CircularProgressIndicator(),
error: (err, stack) => Text('error'),
);
}
}
Socket implements Stream, so you could just write:
final streamProvider = StreamProvider.autoDispose<Uint8List>((ref) {
return ref.watch(tcpClientProvider)._socket;
});
If you still want to add a listener, there's no harm in having one if you need:
final streamProvider = StreamProvider.autoDispose<Uint8List>((ref) {
final client = ref.watch(tcpClientProvider);
return client._socket
..listen(
(event) {},
).onDone(
() {
client
.._changeConnectionState()
.._streamDone();
print('socket is closed');
},
);
});