How to keep login status with Flutter - firebase

Assumptions/what you want to achieve
Currently, I am learning about application development using Flutter+Firebase.
I want to implement the following functions as a part of the login function.
Transition to the login page and login only when logging in for the first time
Unless you log out, opening the app after exiting the app does not bring you to the login page
I did some research on my own and tried them, but they didn't work.
As a concrete method, how can the above functions be implemented?
Also, I am currently implementing a function that uses Firebase's Email and Password.
Is it a common method to implement the above functions even for Google login and Facebook login?
Since I am a new student of Flutter, I have many things that I do not understand, but I hope you can teach me.
Currently implemented
Login function using Firebase Auth
Database using Firebase Cloud Store
What I tried
Check the login status using Current User of Firebase Auth
Additional information (FW/tool ​​version, etc.)
Used
FirebaseAuth
FirebaseCloudStore
class MyApp extends StatelessWidget {
// This widget is the root of your application.
final UserState user = UserState();
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<UserState>.value(
value: user,
child: MaterialApp(
// Hide debug label
debugShowCheckedModeBanner: false,
title:'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: LoginCheck(),
initialRoute: "/",
routes:<String, WidgetBuilder>{
// "/": (BuildContext context) => LoginPage(),
AddRecord.routeName: (BuildContext context) => AddRecord(),
"/login":(BuildContext context) => LoginPage(),
"/home":(BuildContext context) => PageManager()
},
)
);
}
}
class LoginCheck extends StatefulWidget{
LoginCheck({Key key}): super(key: key);
#override
_LoginCheckState createState() => _LoginCheckState();
}
class _LoginCheckState extends State<LoginCheck>{
#override
void initState(){
super.initState();
checkUser();
// TODO: implement initState
}
void checkUser() async{
final UserState userState = Provider.of<UserState>(context);
final currentUser = await FirebaseAuth.instance.currentUser();
print(currentUser);
if(currentUser == null){
Navigator.pushReplacementNamed(context,"/login");
}else{
userState.setUser(currentUser);
Navigator.pushReplacementNamed(context, "/home");
}
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: Center(
child: Container(
child: Text("Loading..."),
),
),
);
}
}

Related

Flutter Streambuilder not working as expected with Firebase

Not sure I am thinking about this right. According to my knowledge the Streambuilder is supposed to log you out if the user has been deleted from the backend which is Firebase.
The steps of what I am doing as of now -
loading the app
Signing in to the app
Loading firebase and deleting the signed in user from the backend.
I believe doing this would log me out from the app as well. Is that right?
Here is my code -
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MaterialApp(
theme: ThemeData(
accentColor: Colors.orange,
primarySwatch: Colors.blue,
),
home: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
print(FirebaseAuth.instance.authStateChanges());
if (snapshot.connectionState == ConnectionState.active) {
var user = snapshot.data;
if (user == null) {
return Welcome();
}
return Conversations("Logged in");
}
)
));
}
Firebase Authentication uses a combination of long-lived and short-lived tokens to manage login sessions, and it may take up to an hour before the short-lived token needs to be refresh and the client detects that the client is signed out.
If you waited for less time than an hour, that is probably the reason your authStateChanges() didn't fire with a new value: the token is still valid, so the client's auth state hasn't changed yet.
If you want to learn how to revoke the user's tokens, I recommend reading the documentation on that. Fair warning though: it is quite a bit more involved than simply signing in and out on the client.
If your goal is to be able to lock out users instantly, there are probably other ways to do that. For example, when using Firebase's Realtime Database or Firestore, it is quite common to keep a list of "blocked UIDs" in the database, and check against that in the security rules of your database.
When logging out by using signOut(), the state got updated right away, but it might not be the case when you delete the user.
The change might take a while to be notified to the stream at the front end. You can read more on that here: Firebase user deleted but still logged in on device
Firebase Authentication tokens are valid for one hour and cached on the user's device. It is automatically refreshed by the client SDK. Deleting the account doesn't proactively reach out to all the user's devices to delete the token.
You can try on this mini sign-in app with the provided signOut() method:
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: MyApp(),
);
}
}
class LandingPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<User>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
User user = snapshot.data;
if (user == null) {
return Welcome();
}
return Conversations();
} else {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
},
);
}
}
class Welcome extends StatelessWidget {
Future<void> _signInAnonymously() async {
try {
await FirebaseAuth.instance.signInAnonymously();
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Sign in')),
body: Center(
child: RaisedButton(
child: Text('Sign in anonymously'),
onPressed: _signInAnonymously,
),
),
);
}
}
class Conversations extends StatelessWidget {
Future<void> _signOut() async {
try {
await FirebaseAuth.instance.signOut();
} catch (e) {
print(e); // TODO: show dialog with error
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
actions: <Widget>[
FlatButton(
child: Text(
'Logout',
style: TextStyle(
fontSize: 18.0,
color: Colors.white,
),
),
onPressed: _signOut,
),
],
),
);
}
}

Flutter & Firebase - Use a Provider only locally for a single page

So, I am building an app with 3 screens so far: Login, Register and Home. Login and Register pages work just fine. I managed to use a Provider to listen to the user authentication state, and direct him either to the Login or Home page, depending on whether he is logged in or not.
Now for the Home Page: I basically want it to show a list of stores I have in my Firestore Database. To do this, I am wrapping the Scaffold with a StreamProvider<List>.value
But I I keep getting the following error message:
Error: Could not find the correct Provider<List> above this Home Widget
Now, if I understand this correctly, this is because the Provider for the stores is not declared in the main file, like I did with the Provider for the user authentication.
Is there any way of having the Provider for the stores declared just in this Home page, and not in the main file, since I do not need to access the database in the other pages?
main function:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MaterialApp(home: CondoApp()));
}
class CondoApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider<User>.value(
value: AuthService().user,
child: MaterialApp(
theme: MyThemes(context).mainTheme,
home: Wrapper(),
routes: myRoutes,
),
);
}
}
wrapper (decides whether user is logged in or not, and then show the correct page)
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
// return either StarPage or Home
if (user == null) {
return StartPage();
} else {
return Home();
}
}
}
home page
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
final stores = Provider.of<List<Store>>(context);
return StreamProvider<List<Store>>.value(
value: DatabaseService().stores,
child: Scaffold(
appBar: AppBar(
title: Text('Title'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(stores[0].name),
Text(stores[0].image),
],
),
),
),
);
}
}
database file
class DatabaseService {
final String uid;
DatabaseService({ this.uid });
// Collection reference
final CollectionReference storeCollection = FirebaseFirestore.instance.collection('stores');
// Make a store list from snapshot object
List<Store> _storeListfromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((doc){
return Store(
name: doc.data()['name'] ?? '',
image: doc.data()['image'] ?? ''
);
}).toList();
}
// Get stores stream
Stream<List<Store>> get stores {
return storeCollection.snapshots().map(_storeListfromSnapshot);
}
}

how to signout user from application when his data is deleted from Authentication tab in firebase console?

the whole purpose of this is that i want to signout the current user using app, immediately when i open firebase console and delete his account from Authentication tab.
i want the signout process to be done smoothly without any errors.
what i've tried so far:
in my main function():
runApp(MyApp());
and this is myApp class:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider<AuthService>(
create: (_) => AuthService(),
),
StreamProvider(
create: (context) => context.read<AuthService>().onAuthStateChanged,
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
routes: <String, WidgetBuilder>{
'/home': (BuildContext context) => HomeController(),
'/signUp': (BuildContext context) => SignUpView(
authFormType: AuthFormType.signUp,
),
'/signIn': (BuildContext context) => SignUpView(
authFormType: AuthFormType.signIn,
),
'/addGig': (BuildContext context) => Home(passedSelectedIndex: 1),
},
home: HomeController(),
));
}
}
and this is the HomeController():
class _HomeControllerState extends State<HomeController> {
AuthService authService = locator.get<AuthService>();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
final firebaseUserUid = context.watch<String>();
if (firebaseUserUid != null) {
return HomePage();
return MaterialApp(
debugShowCheckedModeBanner: false,
routes: <String, WidgetBuilder>{
'/home': (BuildContext context) => HomeController(),
'/signUp': (BuildContext context) => SignUpView(
authFormType: AuthFormType.signUp,
),
'/signIn': (BuildContext context) => SignUpView(
authFormType: AuthFormType.signIn,
),
'/addGig': (BuildContext context) => Home(passedSelectedIndex: 1),
},
theme: fyreworkTheme(),
builder: EasyLoading.init(),
home: Home(passedSelectedIndex: 0),
);
} else {
return StartPage();
}
}
}
in the build function of MyApp class, the provider is listening to this stream from the AuthService class:
Stream<String> get onAuthStateChanged => _firebaseAuth.onAuthStateChanged.map(
(FirebaseUser user) => user?.uid,
);
so far so good...when i start or restart the App...every thing works as intended...no probs.
what i wanted to achieve is that if i open the firebase console / Authentication tab and i know the identifier of a specific user i want to delete his account and i delete it.
i thought that would signout that user from the app and navigates him to StartPage()..as the whole app is listening to onAuthStateChanged stream from the Provider.
but that didn't achieve what i was trying to do.
how can i sign out a specific user from the App after i delete his data from Authentication tab in firebase console ?
i hope i've described the problem and the desired goal well....
any help would be much appreciated.
Whenever the users sends any request to the firebase, revalidate the user to ensure that the user exists in the database or not. You may only if the user exists in the tables or not (not check validity of the tokens). Incase it does not redirect the user to login page. If it does then the user must have authenticated successfully before. This is a very simple solution.
For a complex one, you could use triggers in firebase and send a push notification to the app on deletion of the record of the user. Such that whenever the app receives the push notification that tells it to reauthenticate, the app should assume that the authentication data has been deleted or moved and would redirect the user to the login screen.
As an alternative approach, you could use firebase functions to trigger on deleteUser to delete the data server-side... this has the benefit of smaller-client, plus reduces the risk of some 'stuck' conditions should either the data or user delete fail.
exports.onAuthUserDelete = functions.auth.user().onDelete(onAuthUserDelete);
async function onAuthUserDelete(user: admin.auth.UserRecord) {
await admin.firestore().doc(`/users/${user.uid}`).delete();
functions.logger.info(`User firestore record deleted: uid=${user.uid}, displayName=${user.displayName}`);
}

What is the right way of getting the current Firebase user in every screen?

I successfully setup an app with Flutter with both registration and login with Firebase Authentication.
After the sign-in I come back to the home-screen, where I get the user with:
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final FirebaseAuth _auth = FirebaseAuth.instance;
FirebaseUser user;
#override
void initState() {
super.initState();
initUser();
}
initUser() async {
user = await _auth.currentUser();
setState(() {});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
//appBar: AppBar(title: const Text("Run to Feel Better")),
body: Center(
child: Column(
children: <Widget>[
if (user == null)
RoundedButton(
buttonTitle: 'Crea un account',
color: kRTFBBlue,
onPressedFunction: () {
Navigator.pushNamed(context, '/account');
},
)
else
Text("${user?.email}"),
],
),
),
bottomNavigationBar: bottomBar(context, 0),
), // /widgets/home_scaffold.dart
);
}
}
So, everything works fine, but my question is: what is the right way to get the current user in other pages? Have I to recheck in every screen with FirebaseAuth.instance, or can I save it and share between pages (maybe with Provider package)?
Thank-you very much.
Of course you can do both, i.e. you can store your FirebaseAuth instance somewhere using state management, however, you can also just call FirebaseAuth.instance all the time.
The only valuable insight I could think of is that calling FirebaseAuth.instance is totally fine (not particularly expensive) - everything else is probably just entirely opinion based.

Firebase Login with Flutter using onAuthStateChanged

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

Resources