Flutter pressing back button pops up previous snackBar from Login page again - flutter

I have a LoginPage in Flutter. After login, it shows a small snackbar with "success" or "failure.." if password is wrong, then it navigates to the todo list.
When I now press the "back" button on an Android device, it navigates back to the login screen. However, there is still the snackbar popping up and saying "Login successful, redirecting..", and also, my textfields are not emptied and still have the values from the first login, why? That should not happen, but I cannot figure out why that is... here is my code:
import 'package:flutter/material.dart';
import 'package:todoey_flutter/components/rounded_button.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:todoey_flutter/util/file_handler.dart';
import 'package:provider/provider.dart';
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
String username;
String password;
String hashedPW;
// Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
var _nameController = TextEditingController();
var _pwController = TextEditingController();
#override
Widget build(BuildContext context) {
CryptOid cy = Provider.of<CryptOid>(context, listen: true);
FileHandler fh = Provider.of<FileHandler>(context, listen: true);
return Scaffold(
backgroundColor: Colors.white,
body: Builder(
builder: (BuildContext scaffoldBuildContext) {
return Container(
//inAsyncCall: isSpinning,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 34.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
/*
Flexible(
child: Hero(
tag: 'logo',
child: Container(
height: 200.0,
child: Image.asset('images/logo.png'),
),
),
),*/
SizedBox(
height: 48.0,
),
TextField(
controller: _nameController,
style: TextStyle(color: Colors.black54),
onChanged: (value) {
//Do something with the user input.
username = value.toLowerCase();
},
decoration: InputDecoration(
hintText: 'Enter your username',
),
),
SizedBox(
height: 8.0,
),
TextField(
controller: _pwController,
obscureText: true,
style: TextStyle(color: Colors.black54),
onChanged: (value) {
//Do something with the user input.
password = value;
},
decoration: InputDecoration(
hintText: 'Enter your password',
),
),
SizedBox(
height: 24.0,
),
RoundedButton(
title: 'Login',
colour: Colors.lightBlueAccent,
onPressed: () async {
Scaffold.of(scaffoldBuildContext).removeCurrentSnackBar();
print("user: $username, pw: $password");
if ((username != '' && username != null) && (password != '' && password != null)) {
SharedPreferences prefs = await SharedPreferences.getInstance();
// cy.test();
if ((username != '' && username != null) && prefs.containsKey(username)) {
hashedPW = prefs.getString(username);
bool decryptPW = await cy.deHash(hashedPW, password);
if (decryptPW) {
cy.setUsername(username);
fh.setUser(username);
prefs.setString('activeUser', username);
Scaffold.of(scaffoldBuildContext).showSnackBar(
SnackBar(
content: Text("Login successful! redirecting.."),
),
);
Navigator.pushNamed(context, 'taskScreen');
} else {
Scaffold.of(scaffoldBuildContext).showSnackBar(
SnackBar(
content: Text("Wrong password for user $username!"),
),
);
}
} else {
String hashedPW = await cy.hashPW(password);
prefs.setString('activeUser', username);
prefs.setString(username, hashedPW);
cy.setUsername(username);
fh.setUser(username);
Scaffold.of(scaffoldBuildContext).showSnackBar(
SnackBar(
content: Text("User created successful! redirecting.."),
),
);
Navigator.pushNamed(context, 'taskScreen');
//prefs.setString(username, hashedPW);
}
_nameController.clear();
_pwController.clear();
} else {
Scaffold.of(scaffoldBuildContext).showSnackBar(
SnackBar(
content: Text("User and password may not be empty.."),
),
);
_nameController.clear();
_pwController.clear();
return;
}
},
),
],
),
),
);
},
),
);
}
}

You should create a ScaffoldState GlobalKey then assign the to the scaffold.
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: Container());
}
The use the key to showSnackBar
void _showInSnackBar(String value) {
_scaffoldKey.currentState
.showSnackBar(new SnackBar(content: new Text(value)));
}
So your full code would look like this:
import 'package:flutter/material.dart';
import 'package:todoey_flutter/components/rounded_button.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:todoey_flutter/util/file_handler.dart';
import 'package:provider/provider.dart';
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
String username;
String password;
String hashedPW;
// Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
var _nameController = TextEditingController();
var _pwController = TextEditingController();
#override
Widget build(BuildContext context) {
CryptOid cy = Provider.of<CryptOid>(context, listen: true);
FileHandler fh = Provider.of<FileHandler>(context, listen: true);
return Scaffold(
key: _scaffoldKey,
backgroundColor: Colors.white,
body: Builder(
builder: (BuildContext scaffoldBuildContext) {
return Container(
//inAsyncCall: isSpinning,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 34.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
/*
Flexible(
child: Hero(
tag: 'logo',
child: Container(
height: 200.0,
child: Image.asset('images/logo.png'),
),
),
),*/
SizedBox(
height: 48.0,
),
TextField(
controller: _nameController,
style: TextStyle(color: Colors.black54),
onChanged: (value) {
//Do something with the user input.
username = value.toLowerCase();
},
decoration: InputDecoration(
hintText: 'Enter your username',
),
),
SizedBox(
height: 8.0,
),
TextField(
controller: _pwController,
obscureText: true,
style: TextStyle(color: Colors.black54),
onChanged: (value) {
//Do something with the user input.
password = value;
},
decoration: InputDecoration(
hintText: 'Enter your password',
),
),
SizedBox(
height: 24.0,
),
RoundedButton(
title: 'Login',
colour: Colors.lightBlueAccent,
onPressed: () async {
_scaffoldKey.currentState.removeCurrentSnackBar();
print("user: $username, pw: $password");
if ((username != '' && username != null) &&
(password != '' && password != null)) {
SharedPreferences prefs =
await SharedPreferences.getInstance();
// cy.test();
if ((username != '' && username != null) &&
prefs.containsKey(username)) {
hashedPW = prefs.getString(username);
bool decryptPW = await cy.deHash(hashedPW, password);
if (decryptPW) {
cy.setUsername(username);
fh.setUser(username);
prefs.setString('activeUser', username);
_showInSnackBar("Login successful! redirecting..");
Navigator.pushNamed(context, 'taskScreen');
} else {
_showInSnackBar(
"Wrong password for user $username!");
}
} else {
String hashedPW = await cy.hashPW(password);
prefs.setString('activeUser', username);
prefs.setString(username, hashedPW);
cy.setUsername(username);
fh.setUser(username);
_showInSnackBar(
"User created successful! redirecting..");
Navigator.pushNamed(context, 'taskScreen');
//prefs.setString(username, hashedPW);
}
_nameController.clear();
_pwController.clear();
} else {
_showInSnackBar("User and password may not be empty..");
_nameController.clear();
_pwController.clear();
return;
}
},
),
],
),
),
);
},
),
);
}
void _showInSnackBar(String value) {
_scaffoldKey.currentState
.showSnackBar(new SnackBar(content: new Text(value)));
}
}

Related

Error: NoSuchMethodError: 'then' Dynamic call of null. Receiver: null

Does anyone know the cause of this error? I have tried many ways but still don't know where the problem is.
Database.dart
import 'package:fitness_app/Login/login_data.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DatabaseService {
static final DatabaseService _databaseService = DatabaseService._internal();
factory DatabaseService() => _databaseService;
DatabaseService._internal();
static Database? _database;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
final databasePath = await getDatabasesPath();
final path = join(databasePath, 'conference.database');
return await openDatabase(
path,
onCreate: _onCreate,
version: 1,
onConfigure: (db) async => await db.execute('PRAGMA foreign_keys = ON'),
);
}
Future<void> _onCreate(Database db, int version) async {
await db.execute(
'CREATE TABLE login(id INTEGER PRIMARY KEY, name TEXT, username TEXT, password TEXT)',
);
}
verifyuser(String user, String pass) {}
insertLoginData(Logindata logindata) {}
}
Login.dart
import 'package:fitness_app/Login/signup.dart';
import 'package:flutter/material.dart';
import 'login_data.dart';
import 'package:fitness_app/Database/database.dart';
import 'package:fitness_app/home_page.dart';
class Login extends StatefulWidget {
const Login({Key? key, this.login}) : super(key: key);
final Logindata? login;
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
String email = "a";
String pass = "a";
TextEditingController emails = TextEditingController();
TextEditingController password = TextEditingController();
final _formKey = GlobalKey<FormState>();
static final List<Logindata> _login = [];
final DatabaseService _databaseService = DatabaseService();
Future<List<Logindata>> _getLogin() async {
await _databaseService.verifyuser(email, pass).then((value) {
if (value) {
AlertDialog alert = AlertDialog(
title: const Text('Login successful!'),
content: const Text('Welcome!'),
actions: <Widget>[
TextButton(
onPressed: () => (Navigator.push(
context,
MaterialPageRoute(builder: (context) => const HomePage()),
)),
child: const Text('OK'),
),
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
} else {
AlertDialog alert = AlertDialog(
title: const Text('Error!'),
content: const Text('Wrong Email or Password'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
});
return _login;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Column(
children: [
Container(
padding: EdgeInsets.all(10),
child: Form(
key: _formKey,
child: Column(
children: [
Text(
'BeFit:Fitness Activity Tracker Progress\n\n',
style: TextStyle(fontSize: 24),
textAlign: TextAlign.start,
),
Text(
'Welcome',
style: TextStyle(fontSize: 40),
textAlign: TextAlign.center,
),
SizedBox(
height: 30,
),
Container(
child: TextFormField(
controller: emails,
decoration: InputDecoration(
border: UnderlineInputBorder(
borderSide:
BorderSide(width: 1, color: Colors.grey)),
labelText: 'Email'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
return null;
},
),
),
SizedBox(
height: 20,
),
Container(
child: TextFormField(
controller: password,
obscureText: true,
decoration: InputDecoration(
border: UnderlineInputBorder(
borderSide:
BorderSide(width: 1, color: Colors.grey)),
labelText: 'Password'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
return null;
},
),
),
SizedBox(
height: 20,
),
Container(
width: MediaQuery.of(context).size.width,
height: 50,
child: FlatButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
email = emails.text;
pass = password.text;
_getLogin();
print(email);
print(pass);
print('success');
}
},
child: Text("Login"),
textColor: Colors.white,
color: Colors.deepPurple[400],
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(10)),
),
),
SizedBox(
height: 20,
),
Container(
child: Row(
children: <Widget>[
Text('Does not have account?'),
FlatButton(
textColor: Colors.deepPurpleAccent[100],
child: Text(
'Sign up',
style: TextStyle(fontSize: 16),
),
onPressed: () {
//signup screen
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SignUp()),
);
},
)
],
mainAxisAlignment: MainAxisAlignment.center,
))
],
),
),
)
],
),
),
),
);
}
}
Here is the error
enter image description here
I'm using this flutter to complete my project but there are some errors that I can't solve it. Sorry if my coding looks not right because still lacks in coding
Your verifyuser function does not return anything so your value returns null. You either need to return something or check for null values in your then statement.
Future<String> verifyuser(String user, String pass)async {
return user + pass;
}

State not triggering while clicking login button

Every thing is working fine except that the page is not navigating while I press login button. The user is logged in and The screen is changing once I reload it. But the screen has to change when I click the login button. I used the setState fuction But still not working.Please help me solve this. Thanks in advance.
This is my root page
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:login/main.dart';
import 'package:login/services/auth_service.dart';
class Root extends StatefulWidget {
const Root({Key? key}) : super(key: key);
#override
State<Root> createState() => _RootState();
}
enum AuthState { signedIn, signedOut }
class _RootState extends State<Root> {
AuthState _authState = AuthState.signedOut;
AuthService auth = AuthService();
String authuser = "ghnfgjy";
void signOut() async {
await auth.signOut();
setState(() {
_authState = AuthState.signedOut;
});
}
void signIn() async {
setState(() {
_authState = AuthState.signedIn;
});
}
#override
void initState() {
super.initState();
auth.currentUser().then((user) {
print("check check $user ");
setState(() {
_authState = user == null ? AuthState.signedOut : AuthState.signedIn;
});
});
}
#override
Widget build(BuildContext context) {
if (_authState == AuthState.signedOut) {
return Login();
} else {
return Scaffold(
body: Center(
child: Column(
children: [
SizedBox(
height: 69,
),
Text("Helllloooooooo $authuser"),
SizedBox(
height: 45,
),
FlatButton(
onPressed: () {
signOut();
},
child: Text("Sign out"))
],
),
));
}
}
}
This is my login.dart page
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:login/main.dart';
import 'package:login/services/auth_service.dart';
import 'package:login/share/constant.dart';
class Login extends StatefulWidget {
const Login({Key? key}) : super(key: key);
#override
_LoginState createState() => _LoginState();
}
enum FormType {
login,
register,
}
class _LoginState extends State<Login> {
dynamic _email;
dynamic _password;
final formkey = GlobalKey<FormState>();
FormType _formType = FormType.login;
dynamic result;
AuthService auth = AuthService();
bool validateandsave() {
final form = formkey.currentState;
if (form!.validate()) {
form.save();
print('form is valid');
print('$_email $_password');
return true;
} else {
print('form is not valid');
return false;
}
}
Future validateandsumit() async {
if (validateandsave()) {
if (_formType == FormType.register) {
auth.register(_email, _password);
setState(() {
_formType = FormType.login;
});
} else {
auth.signIn(_email, _password);
}
}
}
void moveToRegister() {
formkey.currentState!.reset();
setState(() {
_formType = FormType.register;
});
}
void moveToLogin() {
formkey.currentState!.reset();
setState(() {
_formType = FormType.login;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Colors.blueAccent,
Colors.purple,
],
),
),
padding: EdgeInsets.all(16),
child: Form(
key: formkey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: openText() + inputFields() + buttons(),
),
),
),
);
}
List<Widget> openText() {
if (_formType == FormType.register) {
return [
Text(
'Please Register to continue',
style: TextStyle(
fontSize: 35, color: Colors.white, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
SizedBox(
height: 40,
),
];
} else {
return [
Text('Please Login to continue',
style: TextStyle(
fontSize: 35, color: Colors.white, fontWeight: FontWeight.bold),
textAlign: TextAlign.center),
SizedBox(
height: 40,
),
];
}
}
List<Widget> inputFields() {
return [
TextFormField(
decoration: inputDecoration.copyWith(
labelText: 'Email Address',
),
style: TextStyle(color: Colors.black),
validator: (val) => val!.isEmpty ? 'Enter the email address' : null,
onSaved: (val) => _email = val),
SizedBox(
height: 20,
),
TextFormField(
obscureText: true,
decoration: inputDecoration.copyWith(
labelText: 'Password',
),
style: TextStyle(color: Colors.black),
validator: (val) => val!.isEmpty ? 'Enter the password' : null,
onSaved: (val) => _password = val),
SizedBox(
height: 60,
),
];
}
List<Widget> buttons() {
if (_formType == FormType.register) {
return [
FlatButton(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 60),
onPressed: validateandsumit,
child: Text(
'Register',
style: TextStyle(fontSize: 20, color: Colors.white),
),
color: Colors.pinkAccent,
),
SizedBox(
height: 20,
),
FlatButton(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 60),
onPressed: moveToLogin,
child: Text(
'Have an account? Login',
style: TextStyle(fontSize: 20, color: Colors.white),
),
color: Colors.pinkAccent,
),
];
} else {
return [
FlatButton(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 60),
onPressed: validateandsumit,
child: Text(
'Login',
style: TextStyle(fontSize: 20, color: Colors.white),
),
color: Colors.pinkAccent,
),
SizedBox(
height: 20,
),
FlatButton(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 60),
onPressed: moveToRegister,
child: Text(
'Register',
style: TextStyle(fontSize: 20, color: Colors.white),
),
color: Colors.pinkAccent,
),
];
}
}
}
This is my authservice file
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class AuthService {
FirebaseAuth _auth = FirebaseAuth.instance;
//Stream <User>? get authStateChanges => _auth.authStateChanges();
Stream<User?> get authStateChanges => _auth.authStateChanges();
CollectionReference users = FirebaseFirestore.instance.collection('users');
late User user;
//register in with email and password
Future register(email, password) async {
try {
UserCredential userCredential = await _auth
.createUserWithEmailAndPassword(email: email, password: password);
user = userCredential.user!;
print('Registered ${user.uid}');
var userdata = {
'email': email,
'password': password,
'role': 'user',
};
users.doc(user.uid).get().then((doc) {
if (doc.exists) {
doc.reference.update(userdata);
} else {
users.doc(user.uid).set(userdata);
}
});
} catch (e) {
print(e);
}
}
//sign in with email and password
Future signIn(email, password) async {
try {
UserCredential userCredential = await _auth.signInWithEmailAndPassword(
email: email, password: password);
user = userCredential.user!;
print('logged in ${user.uid}');
var userdata = {
'email': email,
'password': password,
'role': 'user',
};
users.doc(user.uid).get().then((doc) {
if (doc.exists) {
doc.reference.update(userdata);
} else {
users.doc(user.uid).set(userdata);
}
});
} catch (e) {
print(e);
}
}
Future currentUser() async {
try {
User user = await _auth.currentUser!;
return user.uid;
} catch (e) {
print(e);
}
}
//sign out
Future signOut() async {
try {
await _auth.signOut();
} catch (e) {
print(e);
}
}
}
I think the problem is that the build function in _RootState does not rebuild when you login.
One possible solution is to use StreamBuilder to rebuild whenever auth changes (login/logout).
Add this function to your AuthService class:
class AuthService {
final _auth = FirebaseAuth.instance;
...
Stream<User?> authStream() => _auth.authStateChanges(); // add this line
}
Then write your build function (in _RootState) as below:
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: auth.authStream(), // auth here is AuthService.
builder: (BuildContext context, AsyncSnapshot<User?> snapshot) {
// StreamBuilder will automatically rebuild anytime you log in or log
// logout. No need for your initState or signout & signin functions.
if (snapshot.hasError) return const Text('Something went wrong');
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.data == null) return Login();
// snapshot.data is User.
// print(snapshot.data?.email); print(snapshot.data?.displayName);
return Scaffold(
body: Center(
child: Column(
children: [
const SizedBox(height: 69),
Text('Hello ${snapshot.data?.email}'),
const SizedBox(height: 45),
FlatButton(
onPressed: () async {
// no need to call sign out here.
await auth.signOut();
},
child: const Text('Sign out'),
)
],
),
),
);
},
);
// you can delete your initState, signin and signout functions in _RootState

Null value comes from singleton structure

I am developing an application with flutter. I set up a singleton structure to access the data in shared preferences. I made a password change area in the settings section of the application. And I wrote the service with node js, everything works fine, but the email value is null.
how can i fix the problem
data_store (singleton)
class DataStore {
static DataStore? _dataStore;
static DataStore? getInstance() {
if (_dataStore == null) {
// keep local instance till it is fully initialized.
var data = DataStore._();
_dataStore = data;
}
return _dataStore;
}
DataStore._();
UserModel user = UserModel();
bool? putUser(UserModel user) {
this.user = user;
return null;
}
UserModel? getUser() {
return user;
}
}
change password page
import 'package:flutter/material.dart';
import 'package:meetdy/Constants/Utils/app_shared_preferences.dart';
import 'package:meetdy/Constants/colors.dart';
import 'package:meetdy/Constants/utils/data_store.dart';
import 'package:meetdy/Models/auth_models/change_passsword_models/change_password_req_model.dart';
import 'package:meetdy/Models/auth_models/user_model.dart';
import 'package:meetdy/Services/auth_services/change_password_services/change_password_services.dart';
import 'package:provider/provider.dart';
import 'package:sizer/sizer.dart';
class ChangePasswordScreen extends StatefulWidget {
const ChangePasswordScreen({Key? key}) : super(key: key);
#override
State<ChangePasswordScreen> createState() => _ChangePasswordScreenState();
}
class _ChangePasswordScreenState extends State<ChangePasswordScreen> {
late Size size = MediaQuery.of(context).size;
bool _isObscure = true;
bool isLoading = false;
final _formKey = GlobalKey<FormState>();
final _passwordController = TextEditingController();
final DataStore? _dataStore = DataStore.getInstance();
UserModel? user;
#override
void initState() {
user = _dataStore?.getUser();
super.initState();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: GestureDetector(
onTap: () {
FocusManager.instance.primaryFocus?.unfocus();
},
child: Scaffold(
appBar: buildAppBar(context),
body: Padding(
padding: EdgeInsets.symmetric(horizontal: 3.h, vertical: 1.h),
child: Column(
children: [
SizedBox(height: 2.h),
// Login TextFields
Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Form(
key: _formKey,
child: TextFormField(
obscureText: _isObscure,
controller: _passwordController,
cursorColor: AppColors.lightBlack,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(
_isObscure
? Icons.visibility
: Icons.visibility_off,
color: Colors.grey,
),
onPressed: () {
setState(() {
_isObscure = !_isObscure;
});
}),
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
disabledBorder: InputBorder.none,
labelText: 'Password',
labelStyle: TextStyle(
color: AppColors.lightBlack,
),
),
validator: (value) {
if (value!.isEmpty) {
return 'Required';
} else if (value.length < 6) {
return 'Password must be at least 6 characters';
}
return null;
},
onSaved: (value) {
_passwordController.text = value!;
},
),
),
SizedBox(height: 4.h),
Consumer<ChangePasswordService>(
builder: (context, provider, child) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
"Update",
style: TextStyle(
fontSize: 22.sp,
color: AppColors.black,
fontWeight: FontWeight.w300),
),
Padding(
padding:
EdgeInsets.symmetric(horizontal: 1.0.h)),
isLoading
? CircularProgressIndicator(
color: AppColors.orange)
: FloatingActionButton(
backgroundColor: AppColors.orange,
onPressed: () async {
final form = _formKey.currentState;
if (!form!.validate()) {
return;
}
setState(() {
isLoading = true;
});
String password =
_passwordController.text.trim();
String? email = user!.email;
ChangePasswordReqModel passwordData =
ChangePasswordReqModel(
email: email,
password: password);
var newPassword = await provider
.changePassword(passwordData);
setState(() {
isLoading = false;
});
if (newPassword?.error != null) {
var snackBar = SnackBar(
content: Text(
newPassword?.error?.message ??
""),
duration: Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20)),
clipBehavior: Clip.hardEdge,
);
// ignore: use_build_context_synchronously
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
} else {
var snackBar = SnackBar(
content: Text(
"Your password has been successfully changed"),
duration: Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20)),
clipBehavior: Clip.hardEdge,
);
// ignore: use_build_context_synchronously
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
}
_passwordController.clear();
},
child: Icon(
Icons.keyboard_arrow_right_sharp))
]);
})
],
),
),
],
),
),
),
),
);
}
AppBar buildAppBar(BuildContext context) {
return AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
centerTitle: true,
title: Text(
"Change Password",
style: TextStyle(
color: AppColors.lightBlack,
),
),
leading: IconButton(
icon: Icon(Icons.arrow_back, color: AppColors.lightBlack),
onPressed: () => Navigator.of(context).pop(),
),
);
}
}

Issue with Flutter retrieving data from Firestore (Authenticating different users types from collection)

I am having an issue with trying to route different users to different views in flutter.
I will attach my code below. I have authentication working and routing to the main view. I am now trying to query a Users table in my database (screenshot attached). I want to check by email that the user isStudent and route to menu page. if not a student then route to a separate page.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_tex/flutter_tex.dart';
import 'package:maths_for_computing/components/roundedButton.dart';
import 'package:maths_for_computing/screens/chat_screen.dart';
import 'package:flutter/material.dart';
import 'package:maths_for_computing/components/roundedButton.dart';
import 'package:maths_for_computing/constants.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:maths_for_computing/screens/latex_example.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import 'chat_screen.dart';
import 'menu_student.dart';
final GlobalKey<ScaffoldState> _key = GlobalKey(); // Create a key
final _firestore = FirebaseFirestore.instance;
class LoginScreen extends StatefulWidget {
static const String id = 'login_screen';
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
bool showSpinner = false;
final _auth = FirebaseAuth.instance;
late String email;
late String password;
Future getUsers(String email) async {
final users = await _firestore
.collection('Users')
.where('email', isEqualTo: email)
.get();
for (var user in users.docs) {
print(user.get("email"));
//return user.get("isStudent") as String;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Flexible(
child: Hero(
tag: 'logo',
child: Container(
height: 200.0,
child: Image.asset('images/logo.png'),
),
),
),
const SizedBox(
height: 48.0,
),
TextField(
keyboardType: TextInputType.emailAddress,
textAlign: TextAlign.center,
onChanged: (value) {
//Do something with the user input.
email = value;
},
decoration:
kTextFieldDecoration.copyWith(hintText: 'Enter your email'),
),
const SizedBox(
height: 8.0,
),
TextField(
obscureText: true,
textAlign: TextAlign.center,
onChanged: (value) {
//Do something with the user input.
password = value;
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Enter your password'),
),
const SizedBox(
height: 24.0,
),
RoundedButton(
title: 'Log In',
colour: Colors.lightBlueAccent,
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
final user = await _auth.signInWithEmailAndPassword(
email: email, password: password);
String userType = getUsers(email) ;
// getUsers(email);
if (user != null) {
if (isStudent == 'N') {
Navigator.pushNamed(context, ChatScreen.id);
} else {
Navigator.pushNamed(context, MenuScreen.id);
}
}
} catch (e) {
print(e);
}
setState(() {
showSpinner = false;
});
},
),
],
),
),
),
);
}
}
Any help or advice on this or best practice would be much appreciated. This is a small app so trying to stay away from Custom claims as I am very new to flutter and don't want to overcomplicate things.
Many thanks
E
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_tex/flutter_tex.dart';
import 'package:maths_for_computing/components/roundedButton.dart';
import 'package:maths_for_computing/screens/chat_screen.dart';
import 'package:flutter/material.dart';
import 'package:maths_for_computing/components/roundedButton.dart';
import 'package:maths_for_computing/constants.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:maths_for_computing/screens/latex_example.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import 'chat_screen.dart';
import 'menu_student.dart';
final GlobalKey<ScaffoldState> _key = GlobalKey(); // Create a key
final _firestore = FirebaseFirestore.instance;
class LoginScreen extends StatefulWidget {
static const String id = 'login_screen';
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
bool showSpinner = false;
final _auth = FirebaseAuth.instance;
late String email;
late String password;
Future getUsers(String email) async {
final users = await _firestore
.collection('Users')
.where('email', isEqualTo: email)
.get();
for (var user in users.docs) {
print(user.get("email"));
//return user.get("isStudent") as String;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Flexible(
child: Hero(
tag: 'logo',
child: Container(
height: 200.0,
child: Image.asset('images/logo.png'),
),
),
),
const SizedBox(
height: 48.0,
),
TextField(
keyboardType: TextInputType.emailAddress,
textAlign: TextAlign.center,
onChanged: (value) {
//Do something with the user input.
email = value;
},
decoration:
kTextFieldDecoration.copyWith(hintText: 'Enter your email'),
),
const SizedBox(
height: 8.0,
),
TextField(
obscureText: true,
textAlign: TextAlign.center,
onChanged: (value) {
//Do something with the user input.
password = value;
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Enter your password'),
),
const SizedBox(
height: 24.0,
),
RoundedButton(
title: 'Log In',
colour: Colors.lightBlueAccent,
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
final _auth = FirebaseAuth.instance;
await _auth.signInWithEmailAndPassword(email: email, password: password).then((uid) => {
FirebaseFirestore.instance.collection("users").doc(uid.user.uid).get().then((value) {
Map<dynamic, dynamic> currentUser = value.data();
if(currentUser['isStudent']=="N") {
Navigator.pushNamed(context, ChatScreen.id);
}
else {
Navigator.pushNamed(context, MenuScreen.id);
}
}),
});
} catch (e) {
print(e);
}
setState(() {
showSpinner = false;
});
},
),
],
),
),
),
);
}
}

How to initialise a type ValueChanged in Dart/Flutter?

I am new in Flutter, specially Flutter for Web. I am trying to reach something that's probably easy and basic, but I am facing difficulty.
This is my main.dart
Widget build(BuildContext context) {
bool loggedIn = false;
return MaterialApp(
home: loggedIn ? Navigator(
pages: [
MaterialPage(child: DashboardPage())
],
onPopPage: (route, result) => route.didPop(result),
) : LoginPage(didLoggedIn: (user) => print('Hello, ' + user) )
);
}
}
What I am trying to archive : if not logged in, go to the login screen. After the user successfully login, I'd like to execute a callback that will print hello (in fact I will set the state to logged in, but nevermind).
However I am facing difficulty to implement this callback, and maybe I am doing a wrong approach. This is the login page code:
import 'package:flutter/material.dart';
import 'package:email_validator/email_validator.dart';
class LoginPage extends StatefulWidget {
#override
LoginPageState createState() {
return LoginPageState();
}
}
class LoginPageState extends State<LoginPage> {
final username = TextEditingController();
final password = TextEditingController();
final _formKey = GlobalKey<FormState>();
bool rememberMe = true;
//final ValueChanged didLoggedIn;
Widget _buildUsernameField() {
return TextFormField(
controller: username,
decoration: InputDecoration(labelText: 'Your E-Mail'),
validator: (value) {
if (value == null ||
value.isEmpty ||
!EmailValidator.validate(value)) {
return 'Invalid E-Mail';
}
return null;
});
}
Widget _buildPasswordField() {
return TextFormField(
controller: password,
obscureText: true,
decoration: InputDecoration(labelText: 'Password'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your password';
}
return null;
});
}
#override
Widget build(BuildContext context) {
return Card(
child: Container(
color: Colors.white,
alignment: Alignment.center,
child: Container(
//color: Colors.green,
width: 600,
height: 300,
child: Column(
children: [
Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildUsernameField(),
_buildPasswordField(),
],
),
),
Column(
children: [
CheckboxListTile(title: Text('Remember me') ,
controlAffinity: ListTileControlAffinity.leading,
value: rememberMe, onChanged: (bool? value) {
setState(() {
rememberMe = value!;
});
}),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
print(username.text + "/" + password.text);
//didLoggedIn(username.text);
}
},
child: Text('Log me in'),
),
Text('Forgot your password ?')
],
)
],
),
),
),
);
}
}
The problem is this line : final ValueChanged didLoggedIn;
It says that I need to initialise it. How to do that ?
And by the way, as I said, I am newbie, so maybe this could not be the best way to archive my goals, so if someone wants to give me a better solution, this will be more than welcomed.
Thanks !
You need to change 'LoginPage' like below.
move 'didLoggedIn' to 'LoginPage' not 'LoginPageState'.
make a constructor of 'LoginPage' to receive 'didLoggedIn'
access 'didLoggedIn' using 'widget.' prefix.
import 'package:flutter/material.dart';
import 'package:email_validator/email_validator.dart';
class LoginPage extends StatefulWidget {
final ValueChanged didLoggedIn;
LoginPage({required this.didLoggedIn});
#override
LoginPageState createState() {
return LoginPageState();
}
}
class LoginPageState extends State<LoginPage> {
final username = TextEditingController();
final password = TextEditingController();
final _formKey = GlobalKey<FormState>();
bool rememberMe = true;
Widget _buildUsernameField() {
return TextFormField(
controller: username,
decoration: InputDecoration(labelText: 'Your E-Mail'),
validator: (value) {
if (value == null ||
value.isEmpty ||
!EmailValidator.validate(value)) {
return 'Invalid E-Mail';
}
return null;
});
}
Widget _buildPasswordField() {
return TextFormField(
controller: password,
obscureText: true,
decoration: InputDecoration(labelText: 'Password'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your password';
}
return null;
});
}
#override
Widget build(BuildContext context) {
return Card(
child: Container(
color: Colors.white,
alignment: Alignment.center,
child: Container(
//color: Colors.green,
width: 600,
height: 300,
child: Column(
children: [
Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildUsernameField(),
_buildPasswordField(),
],
),
),
Column(
children: [
CheckboxListTile(title: Text('Remember me') ,
controlAffinity: ListTileControlAffinity.leading,
value: rememberMe, onChanged: (bool? value) {
setState(() {
rememberMe = value!;
});
}),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
print(username.text + "/" + password.text);
//didLoggedIn(username.text);
widget.didLoggedIn(username.text);
}
},
child: Text('Log me in'),
),
Text('Forgot your password ?')
],
)
],
),
),
),
);
}
}
You need to create a constructor for LoginPage to save the callback on this class, then when you need to use the callback in LoginPageState you use the widget.callbackName() or widget.callbackName.call().