Can layout and logic separated in flutter? - flutter

I understand presence Bloc and Scoped Model in flutter.
But that isn't separate like a layout file in java's SpringBoot.

You can actually separate layout and logic in flutter. I have an example.
In my LoginForm I have a function
_attemptLogin() {
BlocProvider.of<LoginBloc>(context).add(
LoginButtonPressed(
context: context,
email: _tecEmail.text,
password: _tecPassword.text,
),
);
}
called by
RaisedButton(
color: Colors.blue,
child: const Text(
'Login',
style: TextStyle(
color: Colors.white,
),
),
onPressed: (state is LoginProcessing)
? null
: _attemptLogin,
),
and in my LoginBloc, I have the ff code inside mapEventToState
#override
Stream<LoginState> mapEventToState(LoginEvent event) async* {
if (event is LoginButtonPressed) {
yield LoginProcessing();
await Future.delayed(const Duration(milliseconds: 250));
try {
var loginResponse =
await _attemptLogin(userRepository, event.email, event.password);
/// Get Firebase Token
final firebaseToken =
await Provider.of<FirebaseMessagingProvider>(context).getToken();
if (loginResponse['data'] != null && firebaseToken != null) {
User user =
_setUserFromJsonMap(context, loginResponse['data']['user']);
IdentityToken identityToken = _setIdentityTokenFromJsonMap(
context, loginResponse['data']['token']);
/// Request Firebase Token Update
var jsonCreateUserFirebaseTokenResponse =
await _attemptFirebaseTokenUpdate(context, firebaseToken);
if (jsonCreateUserFirebaseTokenResponse != null) {
authBloc.add(LoggedIn(identityToken: identityToken));
yield LoginInitial();
}
} else {
yield LoginFailure(message: 'Login failed.');
}
} catch (error, stackTrace) {
print(error);
print(stackTrace);
await Future.delayed(const Duration(seconds: 1));
yield LoginFailure(
message: 'Login failed. Please check your internet connection.');
}
}
}
I didn't include all the other functions/classes as I have already deleted several lines of code to make it look readable, since it contains a ton of code already; which is unnecessary for only trying to prove a point that you can actually separate code for your view and logic.

Related

Flutter_Bloc 8 and Firebase Verify Email -- Clear Prior State?

I am trying to utilize FirebaseAuth's verify email functionality in my Flutter app. I'm using Flutter_BLoC 8. The code works, but when I verify the email address by clicking the link in the generated email, the prior state is apparently still buffered in my app and I need to click TWICE on the 'Continue' button to proceed to the Home Screen.
Here's my code:
ElevatedButton(
onPressed: () async {
context.read<EmailVerificationBloc>().add(const IsEmailVerified());
final isVerified = context.read<EmailVerificationBloc>().state;
if (isVerified == const EmailVerificationState.verified()) {
context.router.replace(const HomeRoute());
} else {
showErrorFlash(context, 'Email Is Not Verified');
}
},
child: const Center(
child: Text('Continue'),
),
),
Here's the BLoC event:
Future<void> _onIsEmailVerified(IsEmailVerified event, Emitter emit) async {
final successOrFailure = await _authRepository.isUserEmailVerified();
successOrFailure.fold(
(failure) {
emit(EmailVerificationState.error(failure));
},
(success) {
if (success) emit(const EmailVerificationState.verified());
},
);
}
And lastly, here's the repository method that's being called:
Future<Either<AuthFailure, bool>> isUserEmailVerified() async {
try {
await _currentUser!.reload(); // We will only call on authenticated users, so we don't expect null
return right(_currentUser!.emailVerified);
} on FirebaseAuthException catch (e) {
final error = e.code;
switch (error) {
case 'network-request-failed':
return left(const AuthFailure.noNetworkConnection());
default:
return left(const AuthFailure.unexpectedError());
}
} on PlatformException {
return left(const AuthFailure.unexpectedError());
}
}
Any ideas on how I can, for lack of a better term, flush the prior state? I'm sort of new to Flutter and BLoC, so I'm hopeful it's a relatively easy thing I'm just overlooking.
Thanks in advance.

How to send a verification email on registerUsingEmailPassword() in flutter

I wan't when a user clicks sign up button an email verification is sent. So far with my code on signup an email verification is sent but user can't navigate to the next page (CircularProgressIndicator keeps on loading)
Here is my code
onPressed: () async {
if (_regFormKey.currentState!.validate()) {
setState(() {
_isProcessing = true;
});
User? user = await FireAuth.registerUsingEmailPassword(
name: nameController,
email: _emailController.text,
password: _passwordController.text,
);
if (user != null) {
bool EmailSent = user.sendEmailVerification() as bool;
//I think something is wrong here
if (EmailSent) {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => ProfilePage(user: user),
),
ModalRoute.withName('/'),
); }
} else{
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(' Account exists or Network problems'),
backgroundColor: Colors.red,
));}
setState(() {
_isProcessing = false;
});
}}
sendEmailVerification() returns a Future<void> so EmailSent is not going to get set. You should await the verification call in a try...catch to handle the response.
More like this:
if (user != null) {
try {
await user.sendEmailVerification();
/// sent successfully
// TODO: put your navigation here
} catch (e) {
/// error sending verification
// TODO: show snackbar
// TODO: set _isProcessing to false
}
}

Flutter Future<bool>, RX<bool>, and regular bool confusion and how to get the value of one from the other

I'm new to flutter and just learned about GetX so I'm trying to incorporate it into my app as much as possible. In this example I'm trying to get permissions for storage and have a widget reactive if it is or isnt granted to change text and color and such.
My function for calling storage permissions gives back a future like so...
Future<bool> requestStoragePermissions() async {
final storagePermissionStatus = await Permission.storage.request();
if (storagePermissionStatus == PermissionStatus.granted) {
debugPrint('Notification Permission Granted');
return true;
} else if (storagePermissionStatus == PermissionStatus.denied) {
debugPrint('Notification Permission Denied');
} else if (storagePermissionStatus == PermissionStatus.permanentlyDenied) {
debugPrint('Notification Permission Permanently Denied');
await openAppSettings();
}
if (storagePermissionStatus == PermissionStatus.granted) {
return true;
} else {
return false;
}
}
in my permissions screen I have a method to choose which permissions I want depending on the argument sent into the class...
Future<bool> checkPermissions() async {
print('Permission Type: ' + permissionType.toString());
bool statusGranted = false;
if (permissionType == Permission.camera) {
statusGranted = await _permissionController.requestCameraPermission();
} else if (permissionType == Permission.location) {
statusGranted = await _permissionController.requestLocationPermissions();
} else if (permissionType == Permission.notification) {
statusGranted = await _permissionController.requestNotificationPermissions();
} else if (permissionType == Permission.storage) {
statusGranted = await _permissionController.requestStoragePermissions();
}
return Future<bool>.value(statusGranted);
}
I change the return to future but I did have it as just statusGranted as I'm playing around with it. How do I use the value Future in a conditional statement? I keep getting error that I cant compare Future to a bool.
Here I am trying to conditionally set the button icon to be different depending on if permission is granted or not.
SizedBox(
width: 100,
child: _permissionGranted.value == false
? const Icon(
Icons.no_photography,
size: 40,
color: Colors.red,
)
: const Icon(
Icons.camera_alt,
size: 40,
color: Colors.green,
),
),
The _permissionsGranted.value I put in there cause I had checkPermission() == falsa there at first. Now I'm just trying random things and falling further from where I need to be which is why I've come here for help.
On a side note as the Title says. Now we have 3 different Bools to work with. Is there a proper way to use these interchangably?
EDIT:
The checkPermission is getting checked everytime this button gets pressed...
Obx(
() => ElevatedButton(
child: _permissionGranted.value == false ? const Text('Click Me') : const Text('Done'),
onPressed: _permissionGranted.value == false
? () async {
await checkPermissions();
}
: null,
I basically was creating this button inside my screen widget and decided to extract it into its own widget so I could use more of the same button to check different permission checks on the same page.
If you're confused about Futures then I suggest reading up on async programming in Dart as its an important concept to understand. It's a Future because it has to await the process checking the permission before the value of the bool is set.
A Future of any type just means that the value won't be returned until some other process occurs ie...requesting data from some external API or checking permissions etc...
RxBool vs bool is a stream of a boolean vs a regular boolean. Using Obx requires a stream inside of it because its job is to listen to the stream and rebuild when the value of the stream updates. And as you know to access the actual primitive bool you use .value.
In your case, you have a bunch of logic in your screen and that could all live in the GetX class and clean up your UI code. All the UI should care about is the value of controller.permissionGranted
Your Future<bool> checkPermissions() doesn't need to return a bool it just needs to update the value of permissionGranted. All the other functions can be private because the UI doesn't need to know about what checkPermissions() does.
// all of this lives in the GetX class
Future<void> checkPermissions() async {
if (permissionType == Permission.camera) {
permissionGranted.value = await _requestCameraPermission();
} else if (permissionType == Permission.location) {
permissionGranted.value = await _requestLocationPermissions();
} // continue with others...
}
And here's an example of how that could look in the UI
class TestPage extends StatelessWidget {
final controller = Get.find<PermissionController>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Obx(
() => ElevatedButton(
child: !controller.permissionGranted
.value // note the ! saves from having to write "== false"
? const Text('Click Me')
: const Text('Done'),
onPressed: !controller.permissionGranted.value
? () async {
await controller.checkPermissions();
}
: null),
),
),
);
}
}
After a few more hours beating my head against the wall trying to figure this out I came across a forum that educated me in the fact the I could use async in an onPressed: property. So I created an RX in my permissionsController to change for each permission check I did and used that as the ternary condition to change the widgets behavior. So I had to add this method which takes in the bool as the method finishes successfully
void permissionGranted() {
if (permissionType == Permission.camera) {
_permissionGranted.value = _permissionController.mediaGranted.value;
} else if (permissionType == Permission.location) {
_permissionGranted.value = _permissionController.locationGranted.value;
} else if (permissionType == Permission.notification) {
_permissionGranted.value = _permissionController.notificationsGranted.value;
} else if (permissionType == Permission.storage) {
_permissionGranted.value = _permissionController.storageGranted.value;
}
}
And as I showed in my post edit I now used the checkPermission method inside the ternary statement rather than trying to use is AS the ternary statement.
Obx(
() => ElevatedButton(
child: _permissionGranted.value == false ? const Text('Click Me') : const Text('Done'),
onPressed: _permissionGranted.value == false
? () async {
await checkPermissions();
}
: null,
and here is my updated checkPermission method...
bool statusGranted = false;
Future<bool> checkPermissions() async {
//print('Permission Type: ' + permissionType.toString());
if (permissionType == Permission.camera) {
statusGranted = await _permissionController.requestCameraPermission();
permissionGranted();
} else if (permissionType == Permission.location) {
statusGranted = await _permissionController.requestLocationPermissions();
permissionGranted();
} else if (permissionType == Permission.notification) {
statusGranted = await _permissionController.requestNotificationPermissions();
permissionGranted();
} else if (permissionType == Permission.storage) {
statusGranted = await _permissionController.requestStoragePermissions();
permissionGranted();
}
return statusGranted;
}
Still very much confused about the interchangeability of having 3 different Boolean types to mix and use though. the Future, the RX and the regular bool.

ImagePicker.platform shows warning - Flutter

I am using the following code to pick an image from user's gallery.
Future getImageFromGallery(BuildContext context) async {
await ImagePicker.platform()
.pickImage(source: ImageSource.gallery)
.then((image) {
if (image != null) {
_cropImage(image, context);
}
});
}
I am getting the following warning.
The member 'platform' can only be used within 'package:image_picker/image_picker.dart' or a test.
I'm not sure what the warning means. I tried looking it up but couldn't figure out the solution to resolve this warning.
Try below code hope its help to you
Declare File type form dart.io package
File? imagePicked;
Create Function for pick up the image
void gallaryImage() async {
final picker = ImagePicker();
final pickedImage = await picker.pickImage(
source: ImageSource.gallery,
);
final pickedImageFile = File(pickedImage!.path);
setState(() {
imagePicked = pickedImageFile;
});
}
Create your Widget
TextButton(
onPressed: gallaryImage,
child: Text(
'Gallery',
style: TextStyle(
color: Colors.black,
),
),
),
You can just change the code
ImagePicker.platform().pickImage(...)
to
ImagePicker().pickImage(...)
so
Future getImageFromGallery(BuildContext context) async {
await ImagePicker()
.pickImage(source: ImageSource.gallery)
.then((image) {
if (image != null) {
_cropImage(image, context);
}
});
}

How to do autologin with three diffrent userTypes in flutter and firebase?

I have this app that do login with firebase auth and firestore to get the userType, This code is written obviously in the login page, What I want to add is autologin ASA the app runs which firebase offers with the correct userType So the first proplem how to transfer the email value to the main.dart page as I search in the firestore with the email to get the userType, Second proplem is that When I tried to do auto login in the login page with three different userTypes It does login but not auto login
CODE :
#override
void initState() {
super.initState();
FirebaseAuth.instance.currentUser().then(
(result) {
if (result != null) {
if (userType == 'userType1') {
Navigator.pushReplacementNamed(context, '/userType1page');
}
if (userType == 'userType2') {
Navigator.pushReplacementNamed(context, '/userType2page');
}
if (userType == 'userType3') {
Navigator.pushReplacementNamed(context, '/userType3page');
}
}
So Here It gets the user But no auto login, what I observed that When U remove the other IFs inside the big if and do 1 Navigation It works So don't know what to do, Please Help me I asked three questions before and didn't get an answer.
PS : NEW TO FLUTTER :)
#FLUTTER_FOREVER
Getting user Data from firestore:
void getUserData() async {
try {
firestoreInstance
.collection('Users')
.document(usernameController.text)
.get()
.then((value) {
setState(() {
email = (value.data)['email'];
password = (value.data)['password'];
gender = (value.data)['gender'];
username = (value.data)['username'];
userType = (value.data)['userType'];
});
});
} catch (e) {
print(e.toString);
}
}
Logining in :
void login() async {
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
firebaseAuth
.signInWithEmailAndPassword(
email: emailController.text, password: passwordController.text)
.then((result) {
{
if (userType == 'Student') {
Navigator.pushReplacementNamed(context, '/StudentsPage');
} else if (userType == 'Teacher') {
Navigator.pushReplacementNamed(context, '/TeacherPage');
} else if (userType == 'Admin') {
Navigator.pushReplacementNamed(context, '/AdminPage');
} else {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Error'),
content: Text(
'Please make sure that you have an internet connection '),
actions: [
FlatButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
},
);
}
}
I found the answer I must identify the var userType inside the initState and It worked by using the getUserData() function but I had a problem I can't use usernameController because It's a var and I didn't defined it so any idea how to get the userType with the document reference usernameController Which I can't identify X-( 'R.I.P #omardeveloper'