How to check if user is SignedIn in Flutter Firebase - flutter

Hy here everyone. I am new to flutter and i want to check if User is SignedIn. If so the user navigate to HomeScreen else SplashScreen.
Here is my main.dart
void main() async{
runApp(MyApp());
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Instant Tasker',
theme: theme(),
initialRoute: SplashScreen.routeName,
routes: routes,
);
}
}
Here is Splash Screen
class SplashScreen extends StatefulWidget {
static String routeName = "/splash";
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
startTime() async {
var _duration = new Duration(seconds: 2);
return new Timer(_duration, navigationPage);
}
void navigationPage() {
var auth = FirebaseAuth.instance;
// ignore: deprecated_member_use
auth.onAuthStateChanged.listen((user) {
if (user != null) {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => MainScreen()),
(Route<dynamic> route) => false);
} else {}
});
}
#override
void initState() {
super.initState();
startTime();
}
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Scaffold(
body: Body()
);
}
}
However i achieved to check user at splash screen but it stays at splash screen to check user then move to HomeScreen which doesn't seems to be good.
Or can anybody suggest how to show CircularProgressIndicator instead of Splash Screen body when it is checking for user

You can achieve it using StreamProvder
Implementation
Steps
Create a CustomUser Data model.
class CustomUser {
final String userId;
CustomUser({this.userId});
}
Create a class named FirebaseAuthService and create a stream to listen to Firebase AuthStateChanges
import 'package:firebase_auth/firebase_auth.dart';
class FirebaseAuthService {
final FirebaseAuth auth = FirebaseAuth.instance;
// create user obj based on firebase user
CustomUser _userFromFirebaseUser(User user) {
return user != null ? CustomUser(userId: user.uid) : null;
}
// auth change user stream
//Required stream
Stream<CustomUser> get user {
return auth.authStateChanges().map(_userFromFirebaseUser);
}
}
}
Add a StreamProvider on top of the widget tree where you want to check for the AuthState.
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider<CustomUser>.value(
value: FirebaseAuthService().user,
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Instant Tasker',
theme: theme(),
initialRoute: SplashScreen.routeName,
routes: routes,
)
);
}
}
Create a Wrapper and return SplashScreen or HomeScreen based on AuthState.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Wrapper extends StatefulWidget {
#override
_WrapperState createState() => _WrapperState();
}
class _WrapperState extends State<Wrapper> {
#override
Widget build(BuildContext context) {
final user = Provider.of<CustomUser>(context);
if (user == null) {
return SplashScreen();
}
return HomeScreen();
}
}
Now you can use final user = Provider.of<CustomUser>(context);
in the widget tree to check if the user is null.
https://www.youtube.com/watch?v=z05m8nlPRxk&list=PL4cUxeGkcC9j--TKIdkb3ISfRbJeJYQwC&index=3

Related

Flutter login control in splash screen

I want to make a small login application. When entering the application, I want to inquire whether the user has a token code or not on the splash screen. How can do this? thank you for help.
main.dart file
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SplashScreen(),
);
}
}
My splash screen.
I want to know if the user has a token or not
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
loginControl();
}
// ignore: missing_return
Future<bool> loginControl() async {
bool status = AuthController.isLoginUser() as bool;
print(status);
if (status) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (BuildContext context) => HomeScreen()));
} else {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (BuildContext context) => LoginScreen()));
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('welcome my app'),
),
);
}
}
my auth controller like this;
class AuthController {
static Future<bool> isLoginUser() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
String token = sharedPreferences.getString("token");
if (token == null) {
return false;
} else {
return true;
}
}
}
Your isLoginUser is actually returning a Future<bool> means that it returns a Future that will later resolve to a bool value.
So, when you use it like this in your loginControl,
bool status = AuthController.isLoginUser() as bool;
AuthController.isLoginUser() return Future<bool> and it can't be directly converted to a bool using as bool.
Instead you should await that Future to resolve, like this.
bool status = await AuthController.isLoginUser(); // This will work.
Now, your code will pause at this line, until it gets a return value from isLoginUser and then resume to next line with status being an actual bool value. i.e., true or false.

StreamProvider with RiverPod not working (try to migrate from Provider)

I'm trying to understand RiverPod by migrating my simple FireStore auth Provider example to RiverPod.
This is my AuthenticationService:
import 'package:firebase_auth/firebase_auth.dart';
class AuthenticationService {
final FirebaseAuth _firebaseAuth;
AuthenticationService(this._firebaseAuth);
// with StreamProvider we listen to these changes
Stream<User> get authStateChanges => _firebaseAuth.authStateChanges();
Future<String> signIn({String email, String password}) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
return 'Signed in';
} on FirebaseAuthException catch (e) {
return e.message;
}
}
Future<String> signUp({String email, String password}) async {
try {
await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
return 'Signed up ';
} on FirebaseAuthException catch (e) {
return e.message;
}
}
Future<void> signOut() async {
await _firebaseAuth.signOut();
}
}
In main.dart I made 2 providers so I can use the service and listen to the property inside of the AuthenticationService
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:meditatie_app/authentication_service.dart';
import 'package:meditatie_app/home_page.dart';
import 'package:meditatie_app/signin_page.dart';
import 'package:provider/provider.dart';
Future<void> main() async {
// initalize Firebase and before that we need to initialize the widgets.
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
// Normal provider to serve the AuthenticationService in the widgettree
// so the login form can use this provider to use .singIn()
Provider<AuthenticationService>(
create: (_) => AuthenticationService(FirebaseAuth.instance),
),
// also a StreamProvider that serves the AuthenticationSerivce authStateChanges
// this stream is updated by the FirebaseAuth package when users signin or out
// this provider use context.read<AuthenticationService> to find the
// provider dived here above
StreamProvider(
create: (context) =>
context.read<AuthenticationService>().authStateChanges,
)
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: AuthenticationWrapper(),
),
);
}
}
class AuthenticationWrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final firebaseUser = context.watch<User>();
if (firebaseUser != null) {
return HomePage();
}
return SignInPage();
}
}
Here the SingIn page:
import 'package:flutter/material.dart';
import 'package:meditatie_app/authentication_service.dart';
import 'package:provider/provider.dart';
class SignInPage extends StatelessWidget {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
...
RaisedButton(
onPressed: () {
// Sign in code
context.read<AuthenticationService>().signIn(
email: emailController.text.trim(),
password: passwordController.text.trim(),
);
},
...
This works fine with normal Provider, but I can't get it to work with RiverPod
What I did was:
These providers I made global in providers.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_riverpod/all.dart';
import 'authentication_service.dart';
final authenticationSeriviceProvider =
Provider((ref) => AuthenticationService(FirebaseAuth.instance));
final authStateChangeProvider = StreamProvider.autoDispose<User>((ref) {
return ref
.watch(authenticationSeriviceProvider)
.authStateChanges;
});
Is this correct? The authStateChangeProvider is using the authenticationSeriviceProvider
When is use it like:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:meditatie_app/home_page.dart';
import 'package:meditatie_app/signin_page.dart';
import 'package:flutter_riverpod/all.dart';
import 'providers.dart';
Future<void> main() async {
// initialize Firebase and before that we need to initialize the widgets.
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
// riverpod needs at toplevel a Provider container
// for storing state of different providers.
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: AuthenticationWrapper(),
);
}
}
// Riverpods ConsumerWidget
// which can consume a provider
// rebuild if the value of the provider changes
class AuthenticationWrapper extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
final firebaseUser = watch(authStateChangeProvider);
if (firebaseUser != null) {
return HomePage();
}
return SignInPage();
}
}
My 'firebaseUser' is not a User anymore, but an AsyncValue
When I change it to:
class AuthenticationWrapper extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
final User firebaseUser = watch(authStateChangeProvider).data?.value;
if (firebaseUser != null) {
return HomePage();
}
return SignInPage();
}
}
It is working, but what am I doing wrong that I now work with AsyncValue
Expanding the previous answer AsyncValue<T> is a sealed class, think of it as StreamBuilder in Flutter having AsyncSnapshot<T> which wraps the value returned from the stream and gives you options to check if its connecting, waiting, withError or withData. In your case
class AuthenticationWrapper extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
return watch(authStateChangeProvider).when(
data: (user) => user == null ? SignInPage() : HomePage(),
loading: () => CircularProgressIndicator(),
error: (err, stack) => SignInPage(),
);
}
}
should handle all the options, now when loading it will show a progress indicator, if there is an error (connection, bad result, etc) it will display the SignInPage, and finally when there is a value you still will need to check if the value returned from the Stream is null (As far as I understand Firebase returns null when there is no user signed in, it doesn't mean the stream is empty) and display the right widget if its null or not.
Just like Provider, after retrieving the user you still have to do the logic with that
See the documentation.
You should use AsyncValue's exposed states to decide what to render. Your code could look something like the following:
class AuthenticationWrapper extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
return watch(authStateChangeProvider).when(
data: (user) => user == null ? SignInPage() : HomePage(),
loading: () => CircularProgressIndicator(),
error: (err, stack) => SignInPage(),
);
}
}
So adjust your return logic to what you'd like for the data, loading, and error states, but this should give you a general idea on how to use AsyncValue.
Another way I found was to use it the way this tutorial did, but with the new riverpod changes:
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_shopping_list/repositories/auth_repository.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
final authControllerProvider = StateNotifierProvider<AuthController, User?>(
(ref) => AuthController(ref.read)..appStarted(),
);
class AuthController extends StateNotifier<User?> {
final Reader _read;
StreamSubscription<User?>? _authStateChangesSubscription;
AuthController(this._read) : super(null) {
_authStateChangesSubscription?.cancel();
_authStateChangesSubscription = _read(authRepositoryProvider)
.authStateChanges
.listen((user) => state = user);
}
#override
void dispose() {
_authStateChangesSubscription?.cancel();
super.dispose();
}
void appStarted() async {
final user = _read(authRepositoryProvider).getCurrentUser();
if (user == null) {
await _read(authRepositoryProvider).signInAnonymously();
}
}
}
And then I used it like this:
#override
Widget build(BuildContext context, WidgetRef ref) {
User? user = ref.watch<User?>(authControllerProvider);
return user != null
? MaterialApp(
title: 'My App',
builder: (context, child) => _Unfocus(child: child!),
home: MainNavigation(),
debugShowCheckedModeBanner: false,
)
: const MaterialApp(
title: 'My App,
home: LoginPage(),
debugShowCheckedModeBanner: false,
);
}

How to pass user to named route with a stateful widget using Navigator in flutter?

My Setup
Login Route - asynchronously checks if a user is logged
Checkin Route - Consists of a main stateful widget and several stateless children widgets that depend on the information of the logged in user
My Idea
After the user login is identified on the login route, use Navigator to pass along the user object to the checkin route
children widgets can receive the user object through their constructor
My Problem
the user object is not 'arriving' in the stateful widget of the checkin route
Files
Here are the relevant code snippets to check (I added them from more complex files and hope I got all imports correct):
Main.dart
import 'package:flutter/material.dart';
import 'loginView.dart';
import 'checkinView.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => LoginView(),
'/checkin': (BuildContext context) => CheckinView(),
}
);
}
}
LoginView.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class LoginView extends StatefulWidget {
#override
LoginViewState createState() => LoginViewState();
}
class LoginViewState extends State<LoginView> {
#override
void initState() {
super.initState();
checkLoginAndRedirect();
}
void checkLoginAndRedirect() async {
FirebaseAuth.instance.currentUser().then((FirebaseUser currUser){
if(currUser != null){
print("[LoginView - user] $currUser");
Navigator.pushNamed(context, '/checkin', arguments: {currUser});
}
});
}
}
CheckinView.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'checkinList.dart';
class CheckinView extends StatefulWidget {
final FirebaseUser user;
CheckinView({this.user});
#override
CheckinViewState createState() {
print("[CheckinView - user] $user");
return CheckinViewState();
}
}
class CheckinViewState extends State<CheckinView> {
FirebaseUser _currentUser;
#override
void initState() {
super.initState();
setState((){
_currentUser = widget.user;
});
}
#override
Widget build(BuildContext context) {
print("[CheckinViewState - widget.user] " + widget.user.toString());
return Scaffold(
body: new Column(
children: <Widget>[
CheckinList(user: _currentUser)
]
)
);
}
}
CheckinList.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class CheckinList extends StatelessWidget{
FirebaseUser user = null;
CheckinList({this.user = null});
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(30.0),
child: Text(user['name'])
);
}
}
Solution
The arguments need extraction in the build method of the WidgetState (because only there we have the BuildContext)
Navigator is automagically handling the arguments correct, even with Stateful Widgets - THANKS FLUTTER TEAM!!!!
My adapted Files
Main.dart
import 'package:flutter/material.dart';
import 'loginView.dart';
import 'checkinView.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => LoginView(),
'/checkin': (BuildContext context) => CheckinView(),
}
);
}
}
LoginView.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class LoginView extends StatefulWidget {
#override
LoginViewState createState() => LoginViewState();
}
class LoginViewState extends State<LoginView> {
#override
void initState() {
super.initState();
checkLoginAndRedirect();
}
void checkLoginAndRedirect() async {
FirebaseAuth.instance.currentUser().then((FirebaseUser currUser){
if(currUser != null){
print("[LoginView - user] $currUser");
//// pass a map as 'arguments' to be ready to pass along more variables in the future
Navigator.pushNamed(context, '/checkin', arguments: {'user': currUser});
}
});
}
}
CheckinView.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'checkinList.dart';
class CheckinView extends StatefulWidget {
//// leave the stateful class as usual
#override
CheckinViewState createState() => CheckinViewState();
}
class CheckinViewState extends State<CheckinView> {
//// don't bother in the initState method as the BuildContext is missing here
#override
Widget build(BuildContext context) {
//// access the passed arguments
Map<String, dynamic> args = ModalRoute.of(context).settings.arguments;
print("[CheckinViewState - widget.user] " + widget.user.toString());
return Scaffold(
body: new Column(
children: <Widget>[
//// access the passed variables through 'args'
CheckinList(user: args.user)
]
)
);
}
}
CheckinList.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class CheckinList extends StatelessWidget{
FirebaseUser user = null;
//// assign the user in your constructor
CheckinList(FirebaseUser user){
this.user = user;
};
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(30.0),
child: Text(user['name'])
);
}
}
Read more in the cookbook about it:
https://flutter.dev/docs/cookbook/navigation/passing-data#create-a-detail-screen-to-extract-the-arguments
https://flutter.dev/docs/cookbook/navigation/navigate-with-arguments
You can try it. I hope it can help you
class LoginViewState extends State<LoginView> {
#override
void initState() {
super.initState();
checkLoginAndRedirect();
}
void checkLoginAndRedirect() async {
FirebaseAuth.instance.currentUser().then((FirebaseUser currUser){
if(currUser != null){
print("[LoginView - user] $currUser");
Navigator.pushNamed(context, '/checkin', arguments: currUser); /// <--- fixed here
}
});
}
}
class CheckinView extends StatefulWidget {
RouteSettings settings = ModalRoute.of(context).settings /// <--- fixed here
FirebaseUser user = settings.arguments; /// <--- fixed here
#override
CheckinViewState createState() {
print("[CheckinView - user] $user");
return CheckinViewState();
}
}
You need to access passed data using ModalRoute inside build method, below code will give you can idea how to do it,
class LevelViewChildScreen extends StatelessWidget {
UserModel user;
#override
Widget build(BuildContext context) {
user = ModalRoute.of(context).settings.arguments;
return LevelViewChild(user);
}
}
class LevelViewChild extends StatefulWidget {
UserModel user;
LevelViewChild(this.user);

Keeping user logged in, in the app with setState()

I am trying to keep the user of my app logged in until they logout. This is an app that accesses an api for authentication. I want to keep the user logged in using the setState method and not the token authentication method. I am able to successfully login but when I close and open the app again, I have to re-login.
Below are the files with the related code using the setState function. I am unable to find my error.
main.dart:
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: "Splash and Token Authentication",
routes: <String, WidgetBuilder>{
"/HomeScreen": (BuildContext context) => HomeScreen(),
"/LoginScreen": (BuildContext context) => LoginScreen(),
},
//state variable is in loginScreen.dart file
home: ((state == loginState.LOGGED_IN)? HomeScreen():LoginScreen())
);
}
#override
void initState() {
super.initState();
}
}
loginScreen.dart:
import ...
enum loginState{ LOGGED_IN, LOGGED_OUT}
loginState state;
const URL = "http://www.google.com";
class LoginScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new LoginScreenState();
}
}
class LoginScreenState extends State<LoginScreen> {
final TextEditingController _userNameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
String _welcomeString = "";
Future launchURL(String url) async {
if(await canLaunch(url)) {
await launch(url, forceSafariVC: true, forceWebView: true);
} else {
showDialogSingleButton(context, "Unable to reach your website.", "Currently unable to reach the website $URL. Please try again at a later time.", "OK");
}
}
#override
void initState() {
super.initState();
_saveCurrentRoute("/LoginScreen");
}
_saveCurrentRoute(String lastRoute) async {
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString('LastScreenRoute', lastRoute);
}
}
#override
Widget build(BuildContext context) {
//LoginScreen UI
}
}
homeScreen.dart:
import ...
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
void initState() {
super.initState();
_saveLoginState();
_saveCurrentRoute("/HomeScreen");
}
_saveCurrentRoute(String lastRoute) async {
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString('LastScreenRoute', lastRoute);
}
Future<void> _saveLoginState() async{
setState(() {
state = loginState.LOGGED_IN;
});
}
#override
Widget build(BuildContext context) {
//HomeScreen UI
}
use onGenerateRoute inside your MaterialApp
Here you can set initialRoute, after log in change SharedPreferences to your loginScreen route. and then you can get route when app is started.
#override
Widget build(BuildContext context) {
String initialRoute = '/';
return MaterialApp(
theme: _themeData,
onGenerateRoute: (s) => _getCurrentRoute(s),
initialRoute: initialRoute,
);
}
_getCurrentRoute(RouteSettings settings) {
print(settings.arguments);
switch (settings.name) {
case '/':
return MaterialPageRoute(
builder: (context) => PageRouter(
child: LogInPage(),
isSearchBar: false,
));
case '/home':
return MaterialPageRoute(
builder: (context) => PageRouter(
child: Home(),
isSearchBar: true,
));
case '/plp_page':
return MaterialPageRoute(
builder: (context) => PageRouter(
child: PlpPage(),
isSearchBar: false,
));
case '/pdp_page':
return MaterialPageRoute(builder: (context) => PdpPage());
case '/cart_page':
return MaterialPageRoute(
builder: (context) => PageRouter(
child: CartPage(),
isSearchBar: false,
));
}
}
You are looking for the shared_preferences package.
You need to Save your Data into Shared Preference when user logs in, and when user log out you need to clear all the Shared Preference.
While When user opens up app again you need to check if the Shared Preference has data of user or not.
Sample Code:
//Initialise SharedPreferences
SharedPreferences prefs = await SharedPreferences.getInstance();
//set some value in it.
//In your case your user data like some id, email or anything
await prefs.setInt('key', value);
//Getting data from preference.
final someName = prefs.getInt('key')

Flutter state management with Provider

I have a simple Provider class:
import 'package:flutter/foundation.dart';
class AppState with ChangeNotifier {
bool _isLoggedIn = false;
bool get isLoggedIn => _isLoggedIn;
set isLoggedIn(bool newValue) {
_isLoggedIn = newValue;
notifyListeners();
}
}
And in the login class I just set isLoggedIn to true if login is successful:
void _signInWithEmailAndPassword(appState) async {
try {
final FirebaseUser user = await _auth.signInWithEmailAndPassword(
...
);
if (user != null) {
appState.isLoggedIn = true;
appState.userData = user.providerData;
...
}
} catch (e) {
setState(() {
_errorMessage = e.message;
});
}
}
Pressing the back button on Android lets users go back to this page even after successfully logging in. So I wanted to know if Provider.of can be accessed before Widget build and redirect a user if isLoggedIn is true.
Now I have something like:
#override
Widget build(BuildContext context) {
final appState = Provider.of<AppState>(context);
...
This is only one use case for the login view, but I'm sure this functionality can be used in other cases.
If you are going to use the FirebaseUser or Logged in user throughout your app, i would suggest that you add the Provider on the highest level of your app. Example
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp();
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<FirebaseUser>.value(
stream: FirebaseAuth.instance.onAuthStateChanged, // Provider here
),
],
child: MaterialApp(
title: 'My App',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.green,
primarySwatch: Colors.green,
accentColor: Colors.yellow,
),
home: MainPage(),
),
);
}
}
class MainPage extends StatefulWidget {
MainPage({Key key, this.storage}) : super(key: key);
final FirebaseStorage storage;
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage>
with SingleTickerProviderStateMixin {
#override
Widget build(BuildContext context) {
final user = Provider.of<FirebaseUser>(context); // gets the firebase user
bool loggedIn = user != null;
return loggedIn ? HomePage() : LoginPage(); // Will check if the user is logged in. And will change anywhere in the app if the user logs in
}
}
References
Fireship 185 Provider
Great Youtube video explaining the code