currently, the way to check if a user is logged in Flutter Fire as per the documentation (https://firebase.flutter.dev/docs/auth/usage#authentication-state):
FirebaseAuth.instance
.authStateChanges()
.listen((User? user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});
the way to set up a route guard in Flutter Modular as per the documentation (https://modular.flutterando.com.br/docs/flutter_modular/navegation#route-guard)
class AuthGuard extends RouteGuard {
AuthGuard() : super(redirectTo: '/login');
#override
Future<bool> canActivate(String path, ModularRoute router) {
return Modular.get<AuthStore>().isLogged;
}
}
how do I use this FlutterFire code to create the route guard in Flutter modular? I have trouble coming up with code that will return a Future from the FlutterFire auth code
try use only this:
Future<bool> checkCurrentUser() async {
return FirebaseAuth.instance.currentUser != null;
}
Modular guard required one future of boolean.
class AuthGuard extends RouteGuard {
AuthGuard() : super(redirectTo: '/login/');
#override
Future<bool> canActivate(String path, ModularRoute route) async {
return await Modular.get<AuthStore>().checkCurrentUser;
}
}
resolve to me this.
I am new to flutter web. I have implemented firebase login functionality in my flutter web application. This functionality works correctly in local. But When i deploy the website on my own server, if i enter correct credentials, it works correctly on live, but whenever i enter wrong password, at that time it gives me exception,
Uncaught ReferenceError: Toastify is not defined
I have used fluttertoast library to dispaly toast messages, i m not sure what is causing the issue, is this error related to toast message or related to firebase. Please see attached screenshot of error
Is this issue related to fluttertoast library or related to firebase? How to resolve this issue, do we need to do any configuration related to domain in firebase?
I am using following code to signin User
Future<void> _signInWithEmailPassword() async {
UtilityHelper.showToast(message: "Login clicked");
_formkey.currentState?.save();
bool _isValid = _formkey.currentState?.validate() ?? false;
FocusScope.of(context).requestFocus(FocusNode());
if (_isValid) {
setState(() {
_loginType = LoginType.normal;
_isLoading = true;
});
final authProvider = Provider.of<AuthProvider>(context, listen: false);
final hasResponse = await authProvider.singInUser(loginReqModel);
redirectToHome(hasResponse, authProvider);
}
}
late UserModel _user;
UserModel get user => _user;
bool get isChangePasswordButtonShown =>
_authRepo.isUserLoggedInUsingPasssword;
String errorMsg = '';
Future<bool> singInUser(LoginReqModel reqModel) async {
try {
final response = await _authRepo.singInUser(reqModel);
if (response != null) {
_user = response;
notifyListeners();
return true;
}
notifyListeners();
return false;
} catch (error) {
print(error);
errorMsg = UtilityHelper.getErrorMessage(error);
return false;
}
}
Any help would be appreciated.
This issue was related to flutter toast library that i was using to display toast message, it did not worked with live domain.
So replacing that library with oktoast library solved the issue
I have already implemented firebase crashlytics to my Flutter project through dependency in pubspec.yaml and also in Gradle files and able to see the crashlytics dashboard in the firebase console.
Now my question is how can I initialize crashlytics in main.dart file and how to write log and catch error or crash for a particular page(say Home page).
I have tried from this link: https://pub.dev/packages/firebase_crashlytics/example
main.dart
final _kShouldTestAsyncErrorOnInit = false;
// Toggle this for testing Crashlytics in your app locally.
final _kTestingCrashlytics = true;
main() {
WidgetsFlutterBinding.ensureInitialized();
runZonedGuarded(() {
runApp(MyApp());
}, (error, stackTrace) {
print('runZonedGuarded: Caught error in my root zone.');
FirebaseCrashlytics.instance.recordError(error, stackTrace);
});
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "My App",
debugShowCheckedModeBanner: false,
home: MainPage(),
theme: ThemeData(
accentColor: Colors.blue
),
);
}
}
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Future<void> _initializeFlutterFireFuture;
Future<void> _testAsyncErrorOnInit() async {
Future<void>.delayed(const Duration(seconds: 2), () {
final List<int> list = <int>[];
print(list[100]);
});
}
// Define an async function to initialize FlutterFire
Future<void> _initializeFlutterFire() async {
// Wait for Firebase to initialize
await Firebase.initializeApp();
if (_kTestingCrashlytics) {
// Force enable crashlytics collection enabled if we're testing it.
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
} else {
// Else only enable it in non-debug builds.
// You could additionally extend this to allow users to opt-in.
await FirebaseCrashlytics.instance
.setCrashlyticsCollectionEnabled(!kDebugMode);
}
// Pass all uncaught errors to Crashlytics.
Function originalOnError = FlutterError.onError;
FlutterError.onError = (FlutterErrorDetails errorDetails) async {
await FirebaseCrashlytics.instance.recordFlutterError(errorDetails);
// Forward to original handler.
originalOnError(errorDetails);
};
if (_kShouldTestAsyncErrorOnInit) {
await _testAsyncErrorOnInit();
}
}
#override
void initState() {
super.initState();
_initializeFlutterFireFuture = _initializeFlutterFire();
Firebase.initializeApp().whenComplete(() {
print("completed");
setState(() {});
});
checkLoginStatus();
}
}
Is it correct or any otherway to initialize crashlytics in flutter?
If i have to check whether there is any crash in HomePage, then how can i get that crash from home page and will show it in firbase crashlytics?
Yes, your configuration of the crashlytics is ok.
If you are using Firebase Auth, you can add the following code in order to have the ability to track crashes specific to a user:
FirebaseAuth.instance.onAuthStateChanged.listen((firebaseUser) {
if (firebaseUser != null && firebaseUser?.email != null) {
Crashlytics.instance.setUserEmail(firebaseUser.email);
}
if (firebaseUser != null && firebaseUser?.uid != null) {
Crashlytics.instance.setUserIdentifier(firebaseUser.uid);
}
if (firebaseUser != null && firebaseUser?.displayName != null) {
Crashlytics.instance.setUserName(firebaseUser.displayName);
}
});
Also, don't forget to track specific exceptions in catch of the try-catch block like this:
try {
//some code here...
} catch (e, s) {
Crashlytics.instance.recordError(e, s, context: "an error occured: uid:$uid");
}
I want to load a image from my Firebase storage. I uploaded images such as profile photos for users with the name {users uid}.png. So when I go to users profile screen, I want to upload these images from firebase accordingly to the current user uid. What is the best way for that ?? I have a async method that sets my user properties such as final loggegInUser and I have a async method
void getCurrentUser() async {
try {
final user = await _auth.currentUser();
if (user != null) {
loggedInUser = user;
print(loggedInUser.email);
print(uid);
}
} catch (e) {
print(e);
}
}
This method sets my global loggedInUser property then I want to load the image from firebase storage like that
CircleAvatar(
backgroundColor: Colors.black,
backgroundImage: FirebaseImage(
'gs://homeparty-68792.appspot.com/user_profile_images/${loggedInUser.uid}.png')
,
radius: 100,
),
But when I load this screen I get
ERROR TYPE Exception has occurred. NoSuchMethodError (NoSuchMethodError: The getter 'uid' was called on null. Receiver: null Tried calling: uid)
error. getCurrentUser() methods work properly it prints the e mail and password but in the build Widget It returns null. Why this is happening I need some help ???
If you are calling getCurrentUser() in initState, then the problem is that the build() is getting called before retrieving the user. Therefore the best thing to do is to upgrade cloud_firestore to version 0.14.0 and to add firebase_core : 0.5.0:
dependencies:
flutter:
sdk: flutter
firebase_core : ^0.5.0
firebase_auth : ^0.18.0
firebase_storage:^4.0.0
# cloud_firestore: ^0.14.0 not sure if you are using
Then you can do the following, first initialize Firebase:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
and inside getCurrentUser():
void getCurrentUser() {
try {
final user = _auth.currentUser;
if (user != null) {
loggedInUser = user;
print(loggedInUser.email);
print(uid);
}
} catch (e) {
print(e);
}
}
In the new version, getting the currentUser isn't asynchronous anymore doesnt require async/await.
Some useful links:
No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp() in Flutter and Firebase
Undefined class 'FirebaseUser'
cloud_firestore 0.14.0 how to use the data method
The getter 'instance' isn't defined for the type 'Firestore'
My Idea:
I want to use the Firebase Auth Plugin in Flutter to register the users.
But before they can access the App, they have to verify their Email address.
Therefor I push the Firebase users after registration to a verification screen. This is just a loading screen which tells the user that he has to verify his email.
But now: How can I continuously listen, if the users email is verified or not and send him (when true) to the Homescreen?
I'm new to Flutter and I don't know if I have to use a Streams or Observables or a while Loop or setState() or something else for such a boolean check. And I also don't know how to setup a solution.
This is my basic code for register a user:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
final Firestore _db = Firestore.instance;
Future<FirebaseUser> get getUser => _auth.currentUser();
Stream<FirebaseUser> get user => _auth.onAuthStateChanged;
Future<FirebaseUser> edubslogin(String email, String password) async {
try {
final FirebaseUser user = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
await user.sendEmailVerification();
//email verification somewhere here
updateUserData(user);
return user;
} catch (error) {
print(error);
return null;
}
}
I've tried this:
if (user.isEmailVerified == true) {
//go to Homescreen
return true;
} else {
//show verification screen(loading spinner)
return false;
}
But I don't get a boolean value true out of isEmailVerified.
What do I have to do?
I faced the same situation in my app. My solution was to create a periodic timer into the initState method of a strategic route to hold the app until the e-mail is verified. It is not so elegant as using a listener but works fine.
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
class _AccountConfirmationState extends State<AccountConfirmation> {
late Timer _timer;
#override
void initState() {
super.initState();
_timer = Timer.periodic(const Duration(seconds: 5), (timer) async {
await FirebaseAuth.instance.currentUser?.reload();
final user = FirebaseAuth.instance.currentUser;
if (user?.emailVerified ?? false) {
timer.cancel();
Navigator.pop(context, true);
}
});
}
#override
void dispose() {
super.dispose();
_timer.cancel();
}
#override
Widget build(BuildContext context) {
//TODO: Implement your amazing waiting screen here
}
}
This verification isn't as straightforward as you'd hope. First, there is the problem of recognizing that the user has verified their email. Second, there is the issue that there isn't any sort of a notification you can listen to that will automatically trigger a change in your app.
Check this thread for info about emailVerified: https://github.com/flutter/flutter/issues/20390#issuecomment-514411392
I was only able to verify the user if I 1) Created their account, 2) Signed them in, 3) Then checked to make sure they verified their email.
final FirebaseAuth _auth = FirebaseAuth.instance;
var _authenticatedUser = await _auth.signInWithEmailAndPassword(email: _email, password: _password);
//where _email and _password were simply what the user typed in the textfields.
if (_authenticatedUser.isEmailVerified) {
//Verified
} else {
//Not verified
}
Part 2: How do you get your app to recognize that the user has confirmed their email? Find a way to trigger the function that checks confirmation. A button would be easy enough. If you want it to see "automatic" then I guess you could create a timer that checks for email verification every 10 seconds or so.
Well I created a stream to handle this. Not so elegant but works. Use a StreamProvider.value() to handle events.
Stream<userVerificationStatus> checkUserVerified() async* {
bool verified = false;
yield userVerificationStatus(status: Status.LOADING);
while (!verified) {
await Future.delayed(Duration(seconds: 5));
FirebaseUser user = await _auth.currentUser();
if(user!=null)await user.reload();
if (user == null) {
yield userVerificationStatus(status: Status.NULL);
} else {
print("isemailverified ${user.isEmailVerified}");
await user.reload();
verified = user.isEmailVerified;
if(verified)
yield userVerificationStatus(status: Status.VERIFIED);
else
yield userVerificationStatus(status: Status.NOT_VERIFIED);
}
}
}
True. None of the FirebaseAuth idTokenChanges() , authStateChanges() or userChanges() will send you an event if the user verifies their email. I'm using a combination of the methods to get an email verification update in my app and it seems to be working well.
First I check the status in the initState() method and start a timer if email is not verified
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
//Get Authenticated user
user = context.read<AuthenticationService>().currentUser();
_isEmailVerified = user.emailVerified;
if (!_isEmailVerified) _startEmailVerificationTimer();
}
I also listen for app background/foreground events in case the user happens to leave the app to confirm their email ( If you also do this, add WidgetsBindingObserver to your class)
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
user = context.read<AuthenticationService>().reloadCurrentUser();
if (user.emailVerified) {
setState(() {
_isEmailVerified = user.emailVerified;
});
timer?.cancel();
} else {
if (!timer.isActive) _startEmailVerificationTimer();
}
}
}
This is the _startEmailVerificationTimer() method
_startEmailVerificationTimer() {
timer = Timer.periodic(Duration(seconds: 5), (Timer _) {
user = context.read<AuthenticationService>().reloadCurrentUser();
if (user.emailVerified) {
setState(() {
_isEmailVerified = user.emailVerified;
});
timer.cancel();
}
});
}
Don't forget to dispose the timer
#override
void dispose() {
timer?.cancel();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
My Firebase User methods in case anyone is interested:
User currentUser() {
return _firebaseAuth.currentUser;
}
User reloadCurrentUser() {
User oldUser = _firebaseAuth.currentUser;
oldUser.reload();
User newUser = _firebaseAuth.currentUser;
return newUser;
}
In order for the app to recognise if the user has verified their email you can achieve this with a simple user.reload.
In order to test it yourself implement a button with onPressed code:
FlatButton(
child: Text("check"),
textColor: Colors.white,
onPressed: () async {
try {
FirebaseUser user = await _firebaseAuth.currentUser();
await user.reload();
user = await _firebaseAuth.currentUser();
print( user.isEmailVerified);
} catch (e) {
return e.message;
}
}),
I had the same problem with the latest version of firebase auth.
But I found out there is a function for reloading the current user which signed in
Future<bool> get userVerified async {
await FirebaseAuth.instance.currentUser.reload();
return FirebaseAuth.instance.currentUser.emailVerified;
}
referesh token after checking current user emailVerified is true
var user = FirebaseAuth.instance.currentUser;
await user?.reload();
if (user?.emailVerified == true) {
await FirebaseAuth.instance.currentUser?.getIdToken(true);
//rest code..
}
also please let me know if this a correct way of doing things.
I have found a way by updating firebase user profile and calling it in init() like below function.
void _checkEmailVerification() async {
await widget.auth.getCurrentUser().then((user) {
UserUpdateInfo userUpdateInfo = new UserUpdateInfo();
userUpdateInfo.displayName = user.displayName;
user.updateProfile(userUpdateInfo).then((onValue) {
setState(() {
_isEmailVerified = user.isEmailVerified;
});
});
});
}
Auth state change listener didn't work for me. Field isEmailVerified remains false even after user verifies his email.
My workaround:
Started from the assumption that user leaves the app to verify his email (which mean app is paused), and he returns to the app after verifying it (app resumes).
What I did was attach a WidgetsBinding to a relevant stateful widget where I wanted to display if email was verified (but can be done elsewhere). This involves two steps.
First step is to attach the binding:
#override
void initState() {
WidgetsBinding.instance.addObserver(this);
super.initState();
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
Second step is to override the didChangeAppLifecycleState to reload the user. I created a function that does the reload and sets a new firebaseUser object
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed && !firebaseUser.isEmailVerified)
refreshFirebaseUser().then((value) => setState(() {}));
super.didChangeAppLifecycleState(state);
}
Future<void> refreshFirebaseUser() async {
await firebaseUser.reload();
firebaseUser = FirebaseAuth.instance.currentUser;
}
So what this is basically doing is to reload firebase user object everytime the user returns to the app, while its email is not verified. I chose this solution over setting and cancelling a timer as it avoided setting a recurrent action through a timer which could be overkill for this particular problem.
Since authOnChanged only listens for sign in and sign out actions, in your sign in method, first sign out then try to sign in.
await _firebaseAuth.signOut();
authResult = await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
return authResult.user;
In the onAuthChanged, when you control if user.isEmailVerified, it will work since you have signed out and it will update the user even if you haven't signed in yet because sign out will trigger your onAuthChanged even if you haven't signed in.
It is like cheating but the only way that I have found without timeout is this.