I’m using Firebase and Provider on my SignIn/Signup part of my mobile app.
Please take a look at the snipets to get a better idea.
main.dart
…
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider<FirebaseBridge>(
create: (_) => FirebaseBridge(),),
StreamProvider(
create: (context) =>
context.read<FirebaseBridge>().monitorAuthStateChanges,
initialData: null),
],
child: MaterialApp(
home: const SplashScreen(),
...
),);}
…
Splash redirects to AuthCheck.
auth_check.dart
…
class AuthCheck extends StatelessWidget {
const AuthCheck({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final userModel = Provider.of<UserModel?>(context);
if (userModel == null) {
return const WelcomeScreen();
} else {
return const Home();
}}}
…
Welcome has buttons to SignIn and SignUp.
SignIn calls FirebaseAuth signInUserWithEmailAndPassword and everything works fine.
SignIn Screen
void doLogIn(String email, String password) {
context
.read<FirebaseBridge>()
.logIn(
emailController.text.trim(),
passwordController.text.trim())
.then(
(value) {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (_) => const Home()));
emailController.clear();
passwordController.clear();
},
onError: (error) {
DialogBoxes.showErrorBox(context,
message:
'Ops! Some message and show the n($error)');
},
);
}
…
And the class that interacts with Firebase.
…
class FirebaseBridge {
final FirebaseAuth _fireAuth = FirebaseAuth.instance;
Stream<UserModel?> get monitorAuthStateChanges {
return _fireAuth.authStateChanges().map(_userModelFromFirebase);
}
UserModel? _userModelFromFirebase(User? user) {
if (user != null) {
return UserModel(uid: user.uid);
} else {
return null;
}
}
Future<void> logIn(String pEmail, String pPassword) async {
try {
await _fireAuth.signInWithEmailAndPassword(
email: pEmail, password: pPassword);
} on FirebaseAuthException catch (e) {
final ex = AuthExceptions.code(e.code);
return Future.error('${ex.message} - ${e.code}');
} on SocketException catch (e) {
return Future.error('Web Socketn($e)n(${e.osError})');
} on PlatformException catch (e) {
return Future.error('Platformn($e)n(${e.message})');
} catch (e) {
return Future.error('Unexpectedn($e)n(${e.toString()})');
}
}
…
My question is: Do I have to check if the user is logged in on everyscreen of the app? let’s say at the top of every screen call some method and ask Provider if the user is still logged in? Any comments on this or about the code would be really appreciated.
Thanks.
2
Answers
My strategy is to have a high-level widget (very close to my
main(runApp...)
watch the authchanges stream, and select a subwidget for either "logged in" or "logged out". Within each widget, I then start my MaterialApp (holding a router) and various Scaffolds below that.This way, I’m guaranteed to only be in half of my app at a time… the parts that work while I’m logged out, and the parts that work while I’m logged in. Even if I screw up routing, I won’t end up in disallowed pages.
Create a stream of User
getAuthChanges
in FirebaseAuthClass:Return a StreamBuilder in
auth_check.dart
: