After a user successfully logins in and saving the state, I want to hide the login screen and just load the home screen, but I end up with the error
The following assertion was thrown building
Navigator-[GlobalObjectKey
_WidgetsAppState#6686e](dirty, dependencies: [UnmanagedRestorationScope, HeroControllerScope], state:
NavigatorState#c7e9f(tickers: tracking 1 ticker)):
'package:flutter/src/widgets/navigator.dart': Failed assertion: line
5070 pos 12: '': is not true.
What is the right way of hiding the login screen when the token is still valid, and just load the home screen?
my code
Main.dart
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'What',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
scaffoldBackgroundColor: Palette.scaffold,
),
// home: SignIn(),
routes: {
//Homepage and being controled by PagesProvider
'/': (context) => SignIn(),
'nav': (context) => NavScreen(),
// add all routes with names here
},
);
}
}
my signin.dart
class SignIn extends StatefulWidget {
const SignIn({Key key}) : super(key: key);
#override
_SignInState createState() => _SignInState();
}
class _SignInState extends State<SignIn> {
ProgressDialog progressDialog;
MsalMobile msal;
bool isSignedIn = false;
bool isLoading = true;
#override
void initState() {
super.initState();
MsalMobile.create('assets/auth_config.json', authority).then((client) {
setState(() {
msal = client;
});
refreshSignedInStatus();
});
}
/// Updates the signed in state
refreshSignedInStatus() async {
bool loggedIn = await msal.getSignedIn();
if (loggedIn) {
isSignedIn = loggedIn;
if (isSignedIn) {
dynamic data = await handleGetAccount();
dynamic token = await handleGetTokenSilently();
dynamic result = token;
SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
sharedPreferences.get("username");
sharedPreferences.get("token");
print('access token (truncated): ${result.accessToken}');
Navigator.of(context).pop();
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => NavScreen(),
),
);
}
// Remaining code for navigation
}
}
/// Gets a token silently.
Future<dynamic> handleGetTokenSilently() async {
String authority = "https://login.microsoftonline.com/$TENANT_ID";
final result = await msal.acquireTokenSilent([SCOPE], authority);
if (result != null) {
// print('access token (truncated): ${result.accessToken}');
SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
sharedPreferences.setString("token", result.accessToken);
return result;
} else {
print('no access token');
return null;
}
}
/// Signs a user in
handleSignIn() async {
await msal.signIn(null, [SCOPE]).then((result) {
// ignore: unnecessary_statements
refreshSignedInStatus();
}).catchError((exception) {
if (exception is MsalMobileException) {
logMsalMobileError(exception);
} else {
final ex = exception as Exception;
print('exception occurred');
print(ex.toString());
}
});
}
logMsalMobileError(MsalMobileException exception) {
print('${exception.errorCode}: ${exception.message}');
if (exception.innerException != null) {
print(
'inner exception = ${exception.innerException.errorCode}: ${exception.innerException.message}');
}
}
/// Signs a user out.
handleSignOut() async {
try {
print('signing out');
await msal.signOut();
print('signout done');
refreshSignedInStatus();
} on MsalMobileException catch (exception) {
logMsalMobileError(exception);
}
}
/// Gets the current and prior accounts.
Future<dynamic> handleGetAccount() async {
// <-- Replace dynamic with type of currentAccount
final result = await msal.getAccount();
if (result.currentAccount != null) {
SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
sharedPreferences.setString("username", result.currentAccount.username);
//print(result.currentAccount.username);
return result.currentAccount;
} else {
print('no account found');
return null;
}
}
#override
Widget build(BuildContext context) {
progressDialog = ProgressDialog(context, type:ProgressDialogType.Normal, isDismissible: false, );
return MaterialApp(
home: new Scaffold(
body: Builder(
builder: (context) => Stack(
fit: StackFit.expand,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Image.asset('assets/landing.webp',
fit: BoxFit.fill,
color: Color.fromRGBO(255, 255, 255, 0.6),
colorBlendMode: BlendMode.modulate),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: 10.0),
Container(
width: 130.0,
child: Align(
alignment: Alignment.center,
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0)),
color: Color(0xffffffff),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Icon(
FontAwesomeIcons.microsoft,
color: Color(0xFF01A6F0),
),
// Visibility(
// visible: !isSignedIn,
SizedBox(width: 10.0),
Text(
'Sign in',
style: TextStyle(
color: Colors.black, fontSize: 18.0),
),
// child: RaisedButton(
// child: Text("Sign In"),
// onPressed: handleSignIn,
// ),
// ),
],
),
onPressed: () => {
progressDialog.show(),
handleSignIn(),
progressDialog.hide()
})),
)
],
),
],
),
),
));
}
}
you should Navigate to the homePage only if the login is successful
because Naviagation.Pop is Equal to the Back button and user can do it manually
here is a better approach :
in main.dart add this :
routes: {
Homepage and being controled by PagesProvider
'nav': (context) => NavScreen(),
'home': (context) => HomePage(),
// add all routes with names here
},
in your refreshSignedInStatus() :
remove this :
Navigator.of(context).pop();
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => NavScreen(),
),
);
add this :
Navigator.pushNamed(context, 'nav');
Related
I'm trying to make a login method via Facebook using Back4App
and I've been following this instructions and I did everything as provided, but when I try to test this, it throws an error Status Code: 101 Error: Facebook auth is invalid for this user. my code: (it's the same as the code provided in the example)
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
import 'package:parse_server_sdk/parse_server_sdk.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final keyApplicationId = 'WHv1tAs6CNFngWtEG6zgX6LrwFCB*******';
final keyClientKey = 'qn7DXL5FHSFJOYRzQLVJ0K4xl1fwS1*******';
final keyParseServerUrl = 'https://parseapi.back4app.com';
await Parse().initialize(keyApplicationId, keyParseServerUrl,
clientKey: keyClientKey, debug: true);
if (kIsWeb) {
// initialiaze the facebook javascript SDK
FacebookAuth.i.webInitialize(
appId: "22079360*******", //<-- YOUR APP_ID
cookie: true,
xfbml: true,
version: "v9.0",
);
}
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter - Sign In with Facebook',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool isLoggedIn = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter - Sign In with Facebook'),
),
body: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
height: 200,
child: Image.network(
'http://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
),
Center(
child: const Text('Flutter on Back4App',
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
),
SizedBox(
height: 100,
),
Container(
height: 50,
child: ElevatedButton(
child: const Text('Sign In with Facebook'),
onPressed: isLoggedIn ? null : () => doSignInFacebook(),
),
),
SizedBox(
height: 16,
),
Container(
height: 50,
child: OutlinedButton(
child: const Text('Logout'),
onPressed: !isLoggedIn ? null : () => doUserLogout(),
),
)
],
),
),
));
}
void showSuccess(String message) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Success!"),
content: Text(message),
actions: <Widget>[
new TextButton(
child: const Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
void showError(String errorMessage) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Error!"),
content: Text(errorMessage),
actions: <Widget>[
new TextButton(
child: const Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
void doSignInFacebook() async {
try {
//Check if the user is logged.
final AccessToken? currentAccessToken =
await FacebookAuth.instance.accessToken;
if (currentAccessToken != null) {
//Logout
await FacebookAuth.instance.logOut();
}
//Make a Login request
final LoginResult result = await FacebookAuth.instance.login();
if (result.status != LoginStatus.success) {
showError(result.message!);
return;
}
final AccessToken accessToken = result.accessToken!;
//https://docs.parseplatform.org/parse-server/guide/#facebook-authdata
//According to the documentation, we must send a Map with user authentication data.
final Map<String, dynamic> authData = <String, dynamic>{};
authData['id'] = accessToken.userId;
authData['token'] = accessToken.token;
authData['expiration_date'] = accessToken.expires.toString();
final userData = await FacebookAuth.instance.getUserData();
//Make sign in with Facebook
final parseResponse = await ParseUser.loginWith('facebook', authData);
if (parseResponse.success) {
final ParseUser parseUser = parseResponse.result as ParseUser;
//Additional Information in User
if (userData.containsKey('email')) {
parseUser.emailAddress = userData['email'];
}
if (userData.containsKey('name')) {
parseUser.set<String>('name', userData['name']);
}
if (userData["picture"]["data"]["url"] != null) {
parseUser.set<String>('photoURL', userData["picture"]["data"]["url"]);
}
await parseUser.save();
showSuccess("User was successfully with Sign In Facebook!");
setState(() {
isLoggedIn = true;
});
} else {
showError(parseResponse.error!.message);
}
} on Exception catch (e) {
print(e.toString());
showError(e.toString());
}
}
void doUserLogout() async {
final user = await ParseUser.currentUser() as ParseUser;
var response = await user.logout();
if (response.success) {
showSuccess("User was successfully logout!");
setState(() {
isLoggedIn = false;
});
} else {
showError(response.error!.message);
}
}
}
debug console:
I/flutter ( 9901):
I/flutter ( 9901): https://parseapi.back4app.com/users
I/flutter ( 9901): ╰--
I/flutter ( 9901): ╭-- Parse Response
I/flutter ( 9901): Class: _User
I/flutter ( 9901): Function: ParseApiRQ.loginWith
I/flutter ( 9901): Status Code: 101
I/flutter ( 9901): Type: ObjectNotFound
I/flutter ( 9901): Error: Facebook auth is invalid for this user.
I/flutter ( 9901): ╰--
flutter version : 2.2.2.
parse_server_sdk: ^3.1.0
flutter_facebook_auth: ^3.5.0
the code provided in back4app's documentation had a problem, you can find the right method for sending the information to the parse server in the package's documentation
before:
final parseResponse = await ParseUser.loginWith('facebook', authData);
after:
final parseResponse = await ParseUser.loginWith('facebook',
facebook(accessToken.token, accessToken.userId, accessToken.expires));
I am managing state using provider but ChangeNotifierProvider does not change the value of variable.
I want to show progress indicator when user is registering. But ChangeNotifierProvider does not provide me update value rather it always return me _isLoading = false;
My Code:
AuthServices.dart
class AuthServices with ChangeNotifier {
bool _isLoading = false;
bool get loading => _isLoading;
///Register User
Future registerUser(
{#required String email, #required String password}) async {
try {
_isLoading = true;
notifyListeners();
await http.post(
BackEndConfigs.baseUrl +
BackEndConfigs.version +
BackEndConfigs.auth +
EndPoints.register,
body: {
"email": email,
"password": password
}).then((http.Response response) {
_isLoading = false;
notifyListeners();
return RegisterUser.fromJson(json.decode(response.body));
});
} catch (e) {
print(e);
}
}
}
RegisterScreen.dart
class RegisterScreen extends StatefulWidget {
#override
_RegisterScreenState createState() => _RegisterScreenState();
}
class _RegisterScreenState extends State<RegisterScreen> {
AuthServices _authServices = AuthServices();
ProgressDialog pr;
#override
Widget build(BuildContext context) {
pr = ProgressDialog(context, isDismissible: true);
// print(status.loading);
return Scaffold(
appBar:
customAppBar(context, title: 'Register Yourself', onPressed: () {}),
body: _getUI(context),
);
}
Widget _getUI(BuildContext context) {
return LoadingOverlay(
isLoading: false,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
VerticalSpace(10.0),
GreyBoldText('Account Information'),
VerticalSpace(20.0),
IconsButtonRow([
Icon(
FontAwesomeIcons.linkedinIn,
color: Color(0xff0e76a8),
),
Icon(
FontAwesomeIcons.facebook,
color: Color(0xff3b5998),
),
Icon(
FontAwesomeIcons.google,
color: Color(0xff4285F4),
),
]),
VerticalSpace(10.0),
GreyNormalText('or sign up with Email'),
VerticalSpace(10.0),
BlackBoldText("Email"),
AppTextField(
label: 'Email',
),
BlackBoldText("Password"),
AppTextField(
label: 'Password',
),
BlackBoldText("Confirm Password"),
AppTextField(
label: 'Confirm Password',
),
VerticalSpace(20.0),
ChangeNotifierProvider(
create: (_) => AuthServices(),
child: Consumer(
builder: (context, AuthServices user, _) {
return Text(user.loading.toString());
},
),
),
AppButton(
buttonText: 'Next',
onPressed: () async {
// await pr.show();
_registerNewUser();
}),
VerticalSpace(30.0),
ToggleView(ToggleViewStatus.SignUpScreen),
VerticalSpace(20.0),
],
),
),
),
);
}
_registerNewUser() async {
_authServices.registerUser(
email: 'sdjfkldsdf#ssdfdsddfdgsffd.com', password: 'sldjsdfkls');
}
}
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
scaffoldBackgroundColor: Colors.white,
hintColor: FrontEndConfigs.hintColor,
cursorColor: FrontEndConfigs.hintColor,
fontFamily: 'Gilory'),
home: RegisterScreen(),
);
}
}
You created two different instances of AuthServices.
One at the start of your State class and one using the ChangeNotifierProvider.
When calling _registerNewUser you use the AuthServices created in your state class not the provided one.
When you call registerUser on the first AuthServices, the value does not change for the second AuthServices provided by the ChangeNotifierProvider down in the Widget tree.
Try deleting the AuthServices instance created by the state class and move your ChangeNotifierProvider up the widget tree so all your functions share the same instance of AuthServices.
Context:
I am building a simple login app using azure AD, after clicking the login button the app authenticates but doesn't not render the next screen but the login button disappears. I want to redirect to the next page after a successful login, but am not being able to. I have handled the navigation inside the initState method inside the StatefulWidget but for some reason it is not being called. How would I be able to solve this?
code
class _SignInState extends State<SignIn> {
static const String SCOPE ='';
static const String TENANT_ID = 'organizations';
static String authority = "";
MsalMobile msal;
bool isSignedIn = false;
#override
void initState() {
super.initState();
MsalMobile.create('assets/auth_config.json', authority).then((client) {
setState(() {
msal = client;
});
refreshSignedInStatus();
signInNavigation();
});
}
Future<void> signInNavigation() async {
isSignedIn = await handleSignIn();
if(isSignedIn) {
// Your navigation code
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => NavScreen()));
}
}
/// Updates the signed in state
refreshSignedInStatus() {
msal.getSignedIn().then((loggedIn) {
print('refreshing');
setState(() {
isSignedIn = loggedIn;
});
});
}
/// Signs a user in
handleSignIn() async {
await msal.signIn(null, [SCOPE]).then((result) {
refreshSignedInStatus();
}).catchError((exception) {
if (exception is MsalMobileException) {
logMsalMobileError(exception);
} else {
final ex = exception as Exception;
print('exception occurred');
print(ex.toString());
}
});
}
logMsalMobileError(MsalMobileException exception) {
print('${exception.errorCode}: ${exception.message}');
if (exception.innerException != null) {
print(
'inner exception = ${exception.innerException.errorCode}: ${exception.innerException.message}');
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: new Scaffold(
body: Builder(
builder: (context) => Stack(
fit: StackFit.expand,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Image.asset('assets/landing.webp',
fit: BoxFit.fill,
color: Color.fromRGBO(255, 255, 255, 0.6),
colorBlendMode: BlendMode.modulate),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: 10.0),
Container(
width: 130.0,
child: Align(
alignment: Alignment.center,
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0)),
color: Color(0xffffffff),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Visibility(
visible: !isSignedIn,
child: RaisedButton(
child: Text("Sign In"),
onPressed: handleSignIn,
),
),
],
),
)),
)
],
),
],
),
),
));
}
}
I don't know how to resolve this issue, am stuck
msal.sign method
Future<MsalMobileAuthenticationResultPayload> signIn(
String loginHint, List<String> scopes) async {
if (!initialized) {
throw MsalMobileException.fromErrorCode(
MsalMobileExceptionErrorCode.notInitialized);
}
final response = await _channel.invokeMethod(
'signIn',
<String, dynamic>{'loginHint': loginHint, 'scopes': scopes},
);
final result = response != null
? MsalMobileAuthenticationResult.fromJson(jsonDecode(response))
: null;
if (!result.isSuccess && result.exception != null) {
// check if the user is already signed in. That could be the cause of an invalid_parameter failure from MSAL
final signedIn = await this.getSignedIn();
if (signedIn) {
throw MsalMobileException.fromErrorCode(
MsalMobileExceptionErrorCode.alreadySignedIn);
}
throw MsalMobileException.copy(result.exception, result.innerException);
}
return result.payload;
}
/// Updates the signed in state
refreshSignedInStatus() {
msal.getSignedIn().then((loggedIn) {
print('refreshing');
setState(() {
isSignedIn = loggedIn;
});
if(isSignedIn) {
// Your navigation code
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => NavScreen()));
}
});
}
A good way to implement authentication is to display a CircularProgressIndicator while we check from the backend whether the user is signed in & once it's done, we can display the Login/Sign up or the Home screen depending upon the status.
Context:
I am building a simple login app with azure ad, the app logins just fine and gets the token and redirects to the next page just fine, but before it redirects it throws an error
Error:
The following _TypeError was thrown building Builder(dirty,
dependencies: [MediaQuery]): type 'Future' is not a subtype of
type 'Widget'
The relevant error-causing widget was: Builder
file:///D:/Projects/flutter/aims_mobile/lib/screens/authentication/signin.dart:101:17
When the exception was thrown, this was the stack:
#0 _SignInState.build. (package:aims_mobile/screens/authentication/signin.dart:133:25)
#1 Builder.build (package:flutter/src/widgets/basic.dart:7185:48)
#2 StatelessElement.build (package:flutter/src/widgets/framework.dart:4749:28)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4675:15)
#4 Element.rebuild (package:flutter/src/widgets/framework.dart:4369:5)
Code
class _SignInState extends State<SignIn> {
static const String SCOPE ='';
static const String TENANT_ID = 'organizations';
static String authority = "";
MsalMobile msal;
bool isSignedIn = false;
#override
void initState() {
super.initState();
MsalMobile.create('assets/auth_config.json', authority).then((client) {
setState(() {
msal = client;
});
refreshSignedInStatus();
});
}
/// Updates the signed in state
refreshSignedInStatus() {
msal.getSignedIn().then((loggedIn) {
print('refreshing');
setState(() {
isSignedIn = loggedIn;
});
});
}
/// Signs a user in
handleSignIn() async {
await msal.signIn(null, [SCOPE]).then((result) {
refreshSignedInStatus();
}).catchError((exception) {
if (exception is MsalMobileException) {
logMsalMobileError(exception);
} else {
final ex = exception as Exception;
print('exception occurred');
print(ex.toString());
}
});
}
logMsalMobileError(MsalMobileException exception) {
print('${exception.errorCode}: ${exception.message}');
if (exception.innerException != null) {
print(
'inner exception = ${exception.innerException.errorCode}: ${exception.innerException.message}');
}
}
/// Signs a user out.
handleSignOut() async {
try {
print('signing out');
await msal.signOut();
print('signout done');
refreshSignedInStatus();
} on MsalMobileException catch (exception) {
logMsalMobileError(exception);
}
}
/// Gets the current and prior accounts.
handleGetAccount() async {
await msal.getAccount().then((result) {
if (result.currentAccount != null) {
print('current account id: ${result.currentAccount.id}');
print('current account id: ${result.currentAccount.username}');
} else {
print('no account found');
}
}).catchError((exception) {
if (exception is MsalMobileException) {
logMsalMobileError(exception);
} else {
print('exception occurred');
}
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: new Scaffold(
body: Builder(
builder: (context) => Stack(
fit: StackFit.expand,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Image.asset('assets/landing.webp',
fit: BoxFit.fill,
color: Color.fromRGBO(255, 255, 255, 0.6),
colorBlendMode: BlendMode.modulate),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: 10.0),
Container(
width: 130.0,
child: Align(
alignment: Alignment.center,
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0)),
color: Color(0xffffffff),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
isSignedIn
? Future.delayed(Duration.zero, () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => NavScreen()));
})
: RaisedButton(
child: Text("Sign In"),
onPressed: handleSignIn,
),
],
),
)),
)
],
),
],
),
),
));
}
}
I don't know how to resolve this issue
You are passing a function Future instead of Widget which is causing this error.
isSignedIn
? Future.delayed(Duration.zero, () { // <---- This is not a widget
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => NavScreen()));
})
: RaisedButton(
child: Text("Sign In"),
onPressed: handleSignIn,
),
Instead of doing this, you can use the Visibility widget to hide/show your "Sign In" button & using the bool isSignedIn, you can handle the navigation code.
Visibility(
visible: !isSignedIn,
child: RaisedButton(
child: Text("Sign In"),
onPressed: handleSignIn,
),
),
You should handle the navigation inside the initState method of your StatefulWidget which would look something like this:
#override
void initState() {
super.initState();
signInNavigation();
}
Future<void> signInNavigation() async {
// isSignedIn = await yourMethodWhichReturnsSignInStatus(); <-- Replace this with your method
if(isSignedIn) {
// Your navigation code
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => NavScreen()));
}
}
I am building a simple AuthProvider, and based on whether the user is null or not, show either the MainApp or Gatekeeper.
Unfortunately, even though my AuthProvider prints an updated fbUser value, my Widget never updates. What am I doing wrong?
When the firebase authstate changes, I am updating the user value in AuthProvider:
class AuthProvider extends ChangeNotifier {
firebaseAuth.User fbUser;
StreamSubscription _userAuthSub;
AuthProvider() {
_userAuthSub = firebaseAuth.FirebaseAuth.instance
.authStateChanges()
.listen((firebaseAuth.User user) {
if (user == null) {
fbUser = null;
} else {
print('User is signed in!');
fbUser = user;
}
notifyListeners();
});
}
#override
void dispose() {
if (_userAuthSub != null) {
_userAuthSub.cancel();
_userAuthSub = null;
}
super.dispose();
}
Future<firebaseAuth.UserCredential> signInWithGoogle() async {
// Trigger the authentication flow
final GoogleSignInAccount googleUser = await GoogleSignIn().signIn();
// Obtain the auth details from the request
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
// Create a new credential
final firebaseAuth.GoogleAuthCredential credential =
firebaseAuth.GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
// Once signed in, return the UserCredential
return await firebaseAuth.FirebaseAuth.instance
.signInWithCredential(credential);
}
}
Here's my main.dart file, where I use the AuthProvider:
class MeApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<AuthProvider>(
create: (_) => AuthProvider(),
)
],
child: MaterialApp(
home: Scaffold(body: GatekeeperView())));
}
}
And finally here is Gatekeeper consuming this AuthProvider:
class GatekeeperView extends StatelessWidget {
Widget _logo(BuildContext context) {
return Text("LOGO",
style: Theme.of(context)
.textTheme
.headline2
.copyWith(fontWeight: FontWeight.bold));
}
Widget _gatekeeper(BuildContext context) {
final height = MediaQuery.of(context).size.height;
final width = MediaQuery.of(context).size.width;
return Scaffold(
body: Container(
height: height,
alignment: Alignment.center,
padding: EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_logo(context),
SizedBox(height: height * 0.2),
SizedBox(
width: width * 0.6,
child: FlatButton(
color: Colors.orange,
textColor: Colors.white,
padding: EdgeInsets.all(12),
onPressed: () {
Navigator.pushReplacement(
context,
PageRouteBuilder(
pageBuilder: (c, a1, a2) => LoginView(),
transitionsBuilder: (c, anim, a2, child) =>
FadeTransition(opacity: anim, child: child),
transitionDuration: Duration(milliseconds: 300),
));
},
child: Text("Log In")),
),
],
),
),
);
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Consumer<AuthProvider>(builder: (context, auth, child) {
if (auth.fbUser != null) {
print("OK"); // <--- this never prints even though the AuthProvider prints "User is signed in"
return Text("Welcome you are logged in");
}
return _gatekeeper(context);
}));
}
}