I want to be logged in as an admin but also wanna create user login credentials for other users. using createUserWithEmailAndPassword() method but it accidentally logging a newly created user.
enter image description hereI have tried the other methods but not working in my case. Please helps me with this.
SAMPLE CODE::
class _TestScreenState extends State<TestScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final FirebaseFirestore _fs = FirebaseFirestore.instance;
final FirebaseAuth _auth = FirebaseAuth.instance;
bool _isLoading = false;
final User _currentUser = FirebaseAuth.instance.currentUser;
Future<void> _createUser() async {
try {
setState(() {
_isLoading = true;
});
await _auth
.createUserWithEmailAndPassword(
email: _emailController.text,
password: _passwordController.text,
)
.then((UserCredential _user) async {
try {
await _fs.collection('users').doc(_user.user.uid).set({
'email': _emailController.text,
'password': _passwordController.text,
}).then((value) {
setState(() {
_isLoading = false;
});
});
} catch (err) {
_user.user.delete();
print("User Deleted");
setState(() {
_isLoading = false;
});
}
});
} catch (err) {
print("Cannot Sign Up: $err");
setState(() {
_isLoading = false;
});
}
}
#override
Widget build(BuildContext context) {
print("USER--" + _currentUser.uid);
return Scaffold(
appBar: AppBar(title: Text("Test Screen")),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
TextField(
controller: _emailController,
),
SizedBox(
height: 20,
),
TextField(
controller: _passwordController,
),
SizedBox(height: 30),
ElevatedButton(
onPressed: _createUser,
child: Text(_isLoading ? "Creating..." : "Create User"),
),
],
),
),
);
}
}
In Firebase Authentication there is a single signed-in user for each FirebaseAuth object. If you want to sign in multiple users, you will need multiple instances of the FirebaseAuth class, based on an explicit FirebaseApp object (instead of the default FirebaseAuth.instance).
Note that this is a common anti-pattern though. For example, wanting to have a administrative user in the app who can create other user accounts, is typically better handled by creating a custom back-end API (for example on Cloud Functions), where you can then the Admin SDK that is specifically made for such administrative functionality.
I am using the authStateChanges stream from Firebase with flutter. I have two views, one for mobile and the another one for a web application. I want to redirect the user to the SignIn screen if he is not connected, logged in or authenticated. At first it works well but then when i am logged in and refresh the browser i got the SignIn screen loaded for like 1 second and then the Web screen appears again. I checked with print what's going on and from what i saw, the authStateChanges Stream is null for that 1-2 seconds(when SignIn screen appears) and then has a value when the stream receives the connected user. Is there a way to check, or wait until this authentication is done before loading the SignIn screen when it must not load it ?
My main component contains the StreamBuilder as following:
Widget build(BuildContext context) {
final firebaseAuthService = Provider.of<FirebaseAuthService>(context);
return StreamBuilder<User>(
stream: firebaseAuthService.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
User user = snapshot.data;
if (user == null) {
//first time no connection
return SignIn();
}
if (kIsWeb) {
return WebMain(user: user);
}
// load mobile version
return MobileMain();
}
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
});
}
Here you can find my FirebaseAuth wrapper class which contains the methods from firebase:
class FirebaseAuthService {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
User _user;
bool get isAuthenticated {
return _user == null ? false : true;
}
User get user {
return _user;
}
Future<User> signInWithEmailAndPassword(
String userEmail, String userPassword) async {
return _user = await _firebaseAuth
.signInWithEmailAndPassword(email: userEmail, password: userPassword)
.then((userCredential) => userCredential.user);
}
Stream<User> authStateChanges() {
_user = _firebaseAuth.currentUser;
return _firebaseAuth.authStateChanges();
}
Future<void> signOut() async {
return _firebaseAuth.signOut();
}
}
While I am not sure why authStateChanges does not notify when the user sign in state is changed (usually a second later), a similar function does seem to work for your use case.
Try idTokenChanges()
FirebaseAuth.instance.idTokenChanges().listen((event) {
print("On Data: ${event}");
});
This event will return your Firebase User object. When refreshed, it might return 'null' initially, but within a second, returns your signed in User. You could potentially make the sign in page wait a couple of seconds to make sure a signed in user isn't being initialized.
EDIT:
While there may be better solutions, this is currently working for me.
final subscription = FirebaseAuth.instance.idTokenChanges().listen(null);
subscription.onData((event) async {
if(event != null) {
print("We have a user now");
isLoading = false;
print(FirebaseAuth.instance.currentUser);
subscription.cancel();
Navigator.push(
context,
MaterialPageRoute(builder: (context) => OverviewController())
);
} else {
print("No user yet..");
await Future.delayed(Duration(seconds: 2));
if(isLoading) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LoginController())
);
isLoading = false;
subscription.cancel();
}
}
});
For me, the below code seems to work fine. Although there is a warning in docs that says "You should not use this getter to determine the user's current state, instead use [authStateChanges], [idTokenChanges] or [userChanges] to subscribe to updates."
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Diary Book',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
primarySwatch: Colors.green,
),
home: (FirebaseAuth.instance.currentUser == null)
? LoginPage()
: MainPage(),
);
}
}
I haven't encountered any issues using the above code. I Will let you know if do. If someone can comment any future errors this may have that would be great
FirebaseAuth.instance.authStateChanges().listen(
(event) {
if (event == null) {
print('----user is currently signed out');
} else {
print('----user is signed in ');
}
runApp(
const MyApp()
);
},
);
**Want to Print Account Email ID on the Drawer **
I am building a Flutter application and I am facing issues to parse the user's Email ID from the Google Sign-In Method, my app Does Sign in Well using Google Sign in but I am unable to access the user data from user.email function I tried using different Future async Function but I am still stuck there.
Please Find a possible way to Guide my problem, Note: I have tried searching the Web and tried almost all the possible sources
class _ExtendedHomeState extends State<ExtendedHome> {
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final AuthResult authResult = await _auth.signInWithCredential(credential);
final FirebaseUser user = authResult.user;
assert(await user.getIdToken() != null);
final FirebaseUser currentUser = await _auth.currentUser();
email = user.email;
imageUrl = user.photoUrl;
return email;
}
printEmail() async
{
signInWithGoogle();
String email = await signInWithGoogle();
print(email);
return email;
}
#override
Widget build(BuildContext context)
{
return MaterialApp(
debugShowCheckedModeBanner: false,
home: DefaultTabController(length: 2,
child: Scaffold(
drawer: Drawer(
elevation: 16.0,
child: ListView(
children: <Widget>[
UserAccountsDrawerHeader(
accountEmail: Text('$printEmail,', style: TextStyle(fontSize: 12),),
),
],
),
),
Please suggest any other method if possible
Tried all methods Available but Was unsuccessful
You are printing an async function there, that's why it's showing you the type of that function. If you want to print the result getting back from an async function, you can use a FutureBuilder:
accountEmail: FutureBuilder<String>(
future: printEmail(),
builder: (context, snapShot) {
if (snapShot.hasData) {
return Text(snapShot.data, style: TextStyle(fontSize: 12),);
} else {
return Text('Loading');
}
}),
Also change your function signature to:
Future<String> printEmail() async {
// Your existing code
}
I am trying to implement a Home class which shows a Login screen for new users or auto-logins previously signed in users and brings them to directly to the app. However, when the code runs it shows login screens for signed in users.
From my understanding, the Future function is initially returning a null and the code finishes with "Login Page" being shown even though the Future later returns the current User.
class Home extends StatelessWidget {
FirebaseAuth _auth = FirebaseAuth.instance;
Future<FirebaseUser> getCurrentUser() async {
return _auth.currentUser();
}
Widget userLoggedIn() {
getCurrentUser().then((user) {
if (user != null) {
//User is auto-logged in = build main app
return new Scaffold(
body: Center(
child: Text("Main App"),
),
);
} else if (user == null) {
//New user = return null
return null;
});
}
#override
Widget build(BuildContext context) {
return userLoggedIn() ??
//New user = build login page
new Scaffold(
body: Center(
child: Text("Login Page"),
),
);
}
}
Could you please help me fix this?
You can start the auto-login progress in your LoginPage's initState,
LoginPage must be StatefulWidget to use initState
#override
void initState() {
FirebaseAuth.instance.currentUser().then((user) {
if (user != null) { //if there isn't any user currentUser function returns a null so we should check this case.
Navigator.pushAndRemoveUntil(
// we are making YourHomePage widget the root if there is a user.
context,
MaterialPageRoute(builder: (context) => YourHomePage()),
(Route<dynamic> route) => false);
}
});
super.initState();
}
signOut with that method:
FirebaseAuth.instance.signOut()
You need to wait until _auth.currentUser() returns the user.
Use await keyword and not .then().
Change your getCurrentUser() method.
Return a FirebaseUser and not a Future.
FirebaseUser currentUser = await authService.getCurrentUser();
Outside of Flutter, when I implement firebase authentication I always use the onAuthStateChanged listener provided by firebase to determine if the user is logged in or not and respond accordingly.
I am trying to do something similar using flutter, but I can find a way to access onAuthStateChanged of Firebase. I am using the firebase_auth, and google_signin Flutter plugins. I am working of example code that is included with the firebase_auth Flutter plugin. Below is the sample code. I can login successfully with google sign in, but the example is too simple, because I want to have an observer/listener to detect the user's signed in/out state.
Is there a way to detect via observer/listener using the firebase_auth/google_signin flutter plugins to determine the status of a user?
Ultimately I want the app to determine if the user is logged in (yes/no). If not then show a login screen, if yes then show my main app page.
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn _googleSignIn = new GoogleSignIn();
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Firebase Auth Demo',
home: new MyHomePage(title: 'Firebase Auth Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<String> _message = new Future<String>.value('');
Future<String> _testSignInAnonymously() async {
final FirebaseUser user = await _auth.signInAnonymously();
assert(user != null);
assert(user == _auth.currentUser);
assert(user.isAnonymous);
assert(!user.isEmailVerified);
assert(await user.getToken() != null);
if (Platform.isIOS) {
// Anonymous auth doesn't show up as a provider on iOS
assert(user.providerData.isEmpty);
} else if (Platform.isAndroid) {
// Anonymous auth does show up as a provider on Android
assert(user.providerData.length == 1);
assert(user.providerData[0].providerId == 'firebase');
assert(user.providerData[0].uid != null);
assert(user.providerData[0].displayName == null);
assert(user.providerData[0].photoUrl == null);
assert(user.providerData[0].email == null);
}
return 'signInAnonymously succeeded: $user';
}
Future<String> _testSignInWithGoogle() async {
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
final FirebaseUser user = await _auth.signInWithGoogle(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
assert(user.email != null);
assert(user.displayName != null);
assert(!user.isAnonymous);
assert(await user.getToken() != null);
return 'signInWithGoogle succeeded: $user';
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new MaterialButton(
child: const Text('Test signInAnonymously'),
onPressed: () {
setState(() {
_message = _testSignInAnonymously();
});
}),
new MaterialButton(
child: const Text('Test signInWithGoogle'),
onPressed: () {
setState(() {
_message = _testSignInWithGoogle();
});
}),
new FutureBuilder<String>(
future: _message,
builder: (_, AsyncSnapshot<String> snapshot) {
return new Text(snapshot.data ?? '',
style: const TextStyle(
color: const Color.fromARGB(255, 0, 155, 0)));
}),
],
),
);
}
}
Here are links to the flutter packages in question:
https://github.com/flutter/plugins/tree/master/packages/firebase_auth
https://github.com/flutter/plugins/tree/master/packages/google_sign_in
I know this question is pretty old, but here is the answer if anybody is still looking for it.
Firebase returns a Stream of FirebaseUser with it's onAuthStateChanged function. There are many ways to listen to the user's authentication state change. This is how I do it:
Solution 1
I return a StreamBuilder to my App's home page, and the StreamBuilder returns specific pages based on the auth status of the user.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Your App Name',
home: _getLandingPage()
);
}
Widget _getLandingPage() {
return StreamBuilder<FirebaseUser>(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.providerData.length == 1) { // logged in using email and password
return snapshot.data.isEmailVerified
? MainPage()
: VerifyEmailPage(user: snapshot.data);
} else { // logged in using other providers
return MainPage();
}
} else {
return LoginPage();
}
},
);
}
Solution 2
You can create a listener in your app's initState() function as well. Make sure the firebase app has been initialized before registering the listener.
#override
void initState() {
super.initState();
FirebaseAuth.instance.authStateChanges().listen((firebaseUser) {
// do whatever you want based on the firebaseUser state
});
}
Solution 3 (Update May 2021)
A simple approach with null-safety without using the provider package:
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(App());
}
class App extends StatefulWidget {
#override
_AppState createState() => _AppState();
}
/// State is persistent and not rebuilt, therefore [Future] is only created once.
/// If [StatelessWidget] is used, in the event where [App] is rebuilt, that
/// would re-initialize FlutterFire and makes our app re-enter the
/// loading state, which is undesired.
class _AppState extends State<App> {
final Future<FirebaseApp> _initFirebaseSdk = Firebase.initializeApp();
final _navigatorKey = new GlobalKey<NavigatorState>();
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
navigatorKey: _navigatorKey,
theme: theme(),
home: FutureBuilder(
future: _initFirebaseSdk,
builder: (_, snapshot) {
if (snapshot.hasError) return ErrorScreen();
if (snapshot.connectionState == ConnectionState.done) {
// Assign listener after the SDK is initialized successfully
FirebaseAuth.instance.authStateChanges().listen((User? user) {
if (user == null)
_navigatorKey.currentState!
.pushReplacementNamed(LoginScreen.routeName);
else
_navigatorKey.currentState!
.pushReplacementNamed(HomeScreen.routeName);
});
}
return LoadingScreen();
}),
routes: routes,
);
}
}
This approach guarantees that you only use Firebase authentication FirebaseAuth.instance.authStateChanges().listen() after the SDK completes initialization. The auth change listener will be first invoked on app launch and then automatically called again after logout and login.
.pushReplacementNamed() will move to a new screen without back (no back icon on the app bar)
Null safe code (without 3rd party packages)
Screenshot:
To check if the user is signed in from anywhere in the app, use
bool signedIn = Auth.instance.isSignedIn;
To sign in, use
await Auth.instance.signIn(email: 'email', password: 'password');
To sign out, use
await Auth.instance.signOut();
Full Code:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MaterialApp(
home: StreamBuilder<User?>(
stream: Auth.instance.authStateChange(),
builder: (_, snapshot) {
final isSignedIn = snapshot.data != null;
return isSignedIn ? HomePage() : LoginPage();
},
),
),
);
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('HomePage')),
body: Center(
child: ElevatedButton(
onPressed: () => Auth.instance.signOut(),
child: Text('Sign out'),
),
),
);
}
}
class LoginPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('LoginPage')),
body: Center(
child: ElevatedButton(
onPressed: () => Auth.instance.signIn(email: 'test#test.com', password: 'test1234'),
child: Text('Sign in'),
),
),
);
}
}
class Auth {
static final instance = Auth._();
Auth._();
final FirebaseAuth _auth = FirebaseAuth.instance;
bool get isSignedIn => _auth.currentUser != null;
Stream<User?> authStateChange() => _auth.authStateChanges();
Future<void> signIn({required String email, required String password}) => _auth.signInWithEmailAndPassword(email: email, password: password);
Future<void> signOut() => _auth.signOut();
}
Same code using provider package:
Check this answer:
You can create a stream as a getter for the onAuthStateChanged inside an AuthService class. To help you manage the state, you can use the Provider package. The AuthService class will extend the ChangeNotifier class.
class AuthService extends ChangeNotifier {
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn _googleSignIn = new GoogleSignIn();
// create a getter stream
Stream<FirebaseUser> get onAuthStateChanged => _auth.onAuthStateChanged;
//Sign in async functions here ..
}
Wrap your MaterialApp with ChangeNotifierProvider and return an instance of the AuthService class in create method like so:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => AuthService(),
child: new MaterialApp(
title: 'Firebase Auth Demo',
home: Landing(),
),
);
}
}
Now create landing page as a stateless widget. Use Provider.of(context) and a stream builder to listen to the auth changes and render the login page or home page as appropriate.
class Landing extends StatelessWidget {
#override
Widget build(BuildContext context) {
AuthService auth = Provider.of<AuthService>(context);
return StreamBuilder<FirebaseUser>(
stream: auth.onAuthStateChanged,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
FirebaseUser user = snapshot.data;
if (user == null) {
return LogIn();
}
return Home();
} else {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
},
);
}
}
You can read more about state management with provider from the official flutter documentation. Follow this link: https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple
The Firebase for Flutter Codelab has a much more in-depth example using Google sign in and Firebase auth.
After the final step you end up with this _ensureLoggedIn function that is used to check whether the user is signed in and if not, initiate a sign in flow.
Future<Null> _ensureLoggedIn() async {
GoogleSignInAccount user = googleSignIn.currentUser;
if (user == null)
user = await googleSignIn.signInSilently();
if (user == null) {
user = await googleSignIn.signIn();
analytics.logLogin();
}
if (auth.currentUser == null) {
GoogleSignInAuthentication credentials =
await googleSignIn.currentUser.authentication;
await auth.signInWithGoogle(
idToken: credentials.idToken,
accessToken: credentials.accessToken,
);
}
}
You could modify this to check these things when your app starts up and conditionally show different views to pre-auth and post-auth users with something like:
final auth = FirebaseAuth.instance;
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'MyApp',
home: (_checkLogin() == true ? new PostAuthScaffold() : new PreAuthScaffold())
);
}
}
bool _checkLogin() {
GoogleSignInAccount user = googleSignIn.currentUser;
return !(user == null && auth.currentUser == null);
}
Null safe code (using provider):
Full Code:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MaterialApp(
home: ChangeNotifierProvider(
create: (_) => AuthModel(),
child: Consumer<AuthModel>(
builder: (_, model, __) => model.isSignedIn ? HomePage() : LoginPage(),
),
),
),
);
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('HomePage')),
body: Center(
child: ElevatedButton(
onPressed: () async {
final model = context.read<AuthModel>();
await model.signOut();
},
child: Text('Sign out'),
),
),
);
}
}
class LoginPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('LoginPage')),
body: Center(
child: ElevatedButton(
onPressed: () async {
final model = context.read<AuthModel>();
await model.signIn(email: 'test#test.com', password: 'test1234');
},
child: Text('Sign in'),
),
),
);
}
}
class AuthModel extends ChangeNotifier {
final FirebaseAuth _auth = FirebaseAuth.instance;
bool get isSignedIn => _auth.currentUser != null;
Future<void> signIn({required String email, required String password}) async {
await _auth.signInWithEmailAndPassword(email: email, password: password);
notifyListeners();
}
Future<void> signOut() async {
await _auth.signOut();
notifyListeners();
}
}
I had the same query I used Shared preference to get the auth state changed information I also have built a project using Shared Prefrences with Firebase and flutter. Iif you wish to know you can read the blog written on the same by me :
Implementing FirebaseAuthStateListener using Shared Prefrences in Flutter.” https://medium.com/#vaibhavminiyar/implementing-firebaseauthstatelistener-using-shared-prefrences-in-flutter-b42e12f81eb2
since the last update of FlutterFire you need to do it like this
FirebaseAuth.instance.authStateChanges().listen((User user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
}
);
Firebase Auth enables you to subscribe in realtime to this state via a
Stream. Once called, the stream provides an immediate event of the
users current authentication state, and then provides subsequent
events whenever the authentication state changes.
To subscribe to these changes, call the authStateChanges() method on
your FirebaseAuth instance.
The stream returns a User class if the user is signed in, or null
if they are not. You can read more about managing your users below.
More information