Flutter: objects inside InheritedWidget cannot change value - flutter

I have a question about InheritedWidget. Since most of the pages in my apps used the user object, so I created an InheritedWidget class called UserProvider so I don't need to pass the user object along my widget tree. It works fine until I tried to logout and login with another user. The User remains the old one. I do a bit of research and it seems that the value inside InheritedWidget class cannot be changed. It there a way to rewrite it so I can take advantage of InheritedWidget and still able to change the value of the user object?
UserProvider Class:
class UserProvider extends InheritedWidget {
UserProvider({Key key, Widget child, this.user}) : super(key: key, child: child);
final User user;
/* #override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
*/
#override
bool updateShouldNotify(UserProvider oldWidget) {
return user != oldWidget.user;
}
static UserProvider of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(UserProvider) as UserProvider);
}
}
HomePage class:
class HomePage extends StatefulWidget {
HomePage({this.auth, this.onSignedOut,this.userId});
final BaseAuth auth;
final VoidCallback onSignedOut;
final String userId;
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String _userName;
String _userEmail;
String _userPicURL;
User currentUser;
void _signOut() async {
try {
await widget.auth.signOut();
widget.onSignedOut();
} catch (e) {
print(e);
}
}
#override
void initState() {
super.initState();
currentUser = User(widget.userId);
currentUser.loadUserData();
...
#override
Widget build(BuildContext context) {
return UserProvider(
user: currentUser,
...
LoginPage class:
class LoginPage extends StatefulWidget {
LoginPage({this.auth, this.onSignedIn});
final BaseAuth auth;
final VoidCallback onSignedIn;
#override
//_LoginPageState createState() => _LoginPageState();
State<StatefulWidget> createState() => _LoginPageState();
}
enum FormType {
login,
register
}
class _LoginPageState extends State<LoginPage> {
final formKey = new GlobalKey<FormState>();
String _uid;
String _email;
String _password;
String _birthday;
String _fullname;
FormType _formType = FormType.login;
bool validateAndSave() {
final form = formKey.currentState;
if (form.validate()) {
form.save();
return true;
} else {
return false;
}
}
void _addData(String email, String fullname, String birthday) async {
_uid = await widget.auth.currentUser();
Firestore.instance.runTransaction((Transaction transaction) async{
Firestore.instance.collection("Users").document(_uid).setData(
{
"id": _uid,
"email" : email,
"fullname": fullname,
"birthday" : birthday
});
});
}
void validateAndSubmit() async{
final form = formKey.currentState;
if (validateAndSave()) {
try {
if (_formType == FormType.login) {
String userId = await widget.auth.signInWithEmailAndPassword( _email.trim(), _password.trim());
} else {
String userId = await widget.auth.createUserWithEmailAndPassword( _email.trim(), _password.trim());
_addData(_email, _fullname, _birthday);
}
widget.onSignedIn();
}
catch (e)
{
print('Error: $e');
}
} else {
print('form is invalid');
}
}
void moveToRegister () {
formKey.currentState.reset();
setState(() {
_formType = FormType.register;
});
}
void moveToLogin () {
formKey.currentState.reset();
setState(() {
_formType = FormType.login;
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Login"),
backgroundColor: const Color(0xFF86d2dd),
),
body: new Container(
padding: EdgeInsets.all(16.0),
child: new Form(
key: formKey,
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: buildInputs() + buildSubmitButtons(),
)
)
)
);
}
List<Widget> buildInputs() {
if (_formType == FormType.login) {
return [
new TextFormField(
decoration: new InputDecoration(labelText: "Email"),
validator: (value) => value.isEmpty ? 'Email can\'t be empty' : null,
onSaved: (value) => _email = value,
),
new TextFormField(
decoration: new InputDecoration(labelText: "Password"),
obscureText: true,
validator: (value) => value.isEmpty ? 'Password can\'t be empty' : null,
onSaved: (value) => _password = value,
),
];
} else {
return [
new TextFormField(
decoration: new InputDecoration(labelText: "Email"),
validator: (value) => value.isEmpty ? 'Email can\'t be empty' : null,
onSaved: (value) => _email = value,
),
new TextFormField(
decoration: new InputDecoration(labelText: "Password"),
obscureText: true,
validator: (value) => value.isEmpty ? 'Password can\'t be empty' : null,
onSaved: (value) => _password = value,
),
new TextFormField(
decoration: new InputDecoration(labelText: "Name "),
validator: (value) => value.isEmpty ? 'Name can\'t be empty' : null,
onSaved: (value) => _fullname = value,
),
new TextFormField(
decoration: new InputDecoration(labelText: "Birthday (MM/DD)"),
validator: (value) => value.isEmpty ? 'Birthday can\'t be empty' : null,
onSaved: (value) => _birthday = value,
),
];
}
}
List<Widget> buildSubmitButtons() {
if (_formType == FormType.login) {
return [
new RaisedButton(
child: new Text('Login', style: new TextStyle(fontSize: 20.0)),
onPressed: validateAndSubmit,
),
new FlatButton(
child: new Text('Create an account', style: new TextStyle(fontSize: 20.0)),
onPressed: moveToRegister,
)
];
} else {
return [
new RaisedButton(
child: new Text('Create an account', style: new TextStyle(fontSize: 20.0)),
onPressed: validateAndSubmit,
),
new FlatButton(
child: new Text('Have an account? Login', style: new TextStyle(fontSize: 20.0)),
onPressed: moveToLogin,
)
];
}
}
}

I'm experimenting with InheritedWidget myself. After reading https://stackoverflow.com/a/51912243/7050833 I would try placing the UserProvider above the MaterialApp.
UserProvider(child: MaterialApp(...

Related

Singin request with firebase is not working

I'm having a problem trying to do a login validation with firebase. Coincidentally I can create a new firebase account register it and I can enter my application. but when I go back to the home screen, I can't log in with that registered account. What do you think is the mistake I'm making?.
The console returns this to me.
Error image
my screen code
class AuthScreen extends StatefulWidget {
const AuthScreen({Key? key}) : super(key: key);
#override
State<AuthScreen> createState() => _AuthScreenState();
}
class _AuthScreenState extends State<AuthScreen> {
final _auth = FirebaseAuth.instance;
var _isLoading = false;
void _submitAuthForm(
String email,
String userName,
String password,
bool isLogin,
BuildContext ctx,
) async {
UserCredential authResult;
try {
setState(() {
_isLoading = true;
});
if (isLogin) {
authResult = await _auth.signInWithEmailAndPassword(
email: email, password: password);
} else {
authResult = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
await FirebaseFirestore.instance
.collection('users')
.doc(authResult.user!.uid)
.set({
'username': userName,
'email': email,
});
}
} on PlatformException catch (err) {
var message = 'An error ocurred, Please check your credentials';
if (err.message != null) {
message = err.message!;
}
Scaffold.of(ctx).showSnackBar(SnackBar(
content: Text(message),
backgroundColor: Theme.of(ctx).errorColor,
));
setState(() {
_isLoading = false;
});
} catch (err) {
print(err);
setState(() {
_isLoading = false;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).primaryColor,
body: AuthForm(_submitAuthForm, _isLoading),
);
}
}
authentication code
class AuthForm extends StatefulWidget {
AuthForm(this.submitFn, this.isLoading);
final bool isLoading;
final void Function(String email, String userName, String password,
bool isLogin, BuildContext ctx) submitFn;
#override
State<AuthForm> createState() => _AuthFormState();
}
class _AuthFormState extends State<AuthForm> {
final _formKey = GlobalKey<FormState>();
var _isLogin = true;
var _userEmail = '';
var _userName = '';
var _userPassword = '';
void _trySubmit() {
final isValid = _formKey.currentState!.validate();
FocusScope.of(context).unfocus();
if (isValid) {
_formKey.currentState!.save();
widget.submitFn(
_userEmail.trim(),
_userPassword.trim(),
_userName.trim(),
_isLogin,
context,
);
}
}
#override
Widget build(BuildContext context) {
return Center(
child: Card(
margin: EdgeInsets.all(20),
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(16),
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextFormField(
key: ValueKey('email'),
validator: (value) {
if (value!.isEmpty ||
!value.contains('#') ||
!value.contains('.com')) {
return 'Please enter a valid email';
}
return null;
},
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(labelText: 'Email address'),
onSaved: (value) {
_userEmail = value!;
},
),
if (!_isLogin)
TextFormField(
key: ValueKey('username'),
validator: (value) {
if (value!.isEmpty || value.length < 4) {
return 'Please enter at least 4 characters';
}
return null;
},
decoration: InputDecoration(labelText: 'Username'),
onSaved: (value) {
_userName = value!;
},
),
TextFormField(
key: ValueKey('password'),
validator: (value) {
if (value!.isEmpty || value.length < 7) {
return 'Password must be at least 7 characters long';
}
return null;
},
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
onSaved: (value) {
_userPassword = value!;
},
),
SizedBox(
height: 15,
),
if (widget.isLoading) CircularProgressIndicator(),
if (!widget.isLoading)
ElevatedButton(
onPressed: _trySubmit,
child: Text(_isLogin ? 'Login' : 'Signup'),
),
if (!widget.isLoading)
TextButton(
onPressed: () {
setState(() {
_isLogin = !_isLogin;
});
},
child: Text(_isLogin
? 'Create new account'
: 'I already have a account'))
],
)),
),
),
));
}
}

I/flutter (29470): [firebase_auth/invalid-email] The email address is badly formatted

my error was the email address is badly formated
I/flutter (29470): [firebase_auth/invalid-email] The email address is badly formatted.
i was tried solve it but already i have the same problem
my auth form code is
import 'package:flutter/material.dart';
class AuthForm extends StatefulWidget {
const AuthForm(this.submitFn, {Key? key}) : super(key: key);
final void Function(
String username,
String email,
String password,
bool isLogin,
BuildContext context,
) submitFn;
#override
State<AuthForm> createState() => _AuthFormState();
}
class _AuthFormState extends State<AuthForm> {
var emailController = TextEditingController();
final _formKey = GlobalKey<FormState>();
var _isLogin = true;
var _userName = '';
var _userEmail = '';
var _userPassword = '';
void _trySubmit() {
final isValid = _formKey.currentState!.validate();
FocusScope.of(context).unfocus();
if (isValid) {
_formKey.currentState!.save();
widget.submitFn(
_userEmail.toString(),
_userName.trim(),
_userPassword.trim(),
_isLogin,
context,
);
}
}
#override
Widget build(BuildContext context) {
return Center(
child: Card(
margin: const EdgeInsets.all(20),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16),
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextFormField(
key: const ValueKey('email'),
validator: (value) {
if (value!.isEmpty || !value.contains('#')) {
return 'Please Enter valid email address.';
} else {
return null;
}
},
controller: emailController,
keyboardType: TextInputType.emailAddress,
decoration: const InputDecoration(
labelText: 'Email Address',
),
onSaved: (value) {
_userEmail = value!;
},
),
if (!_isLogin)
TextFormField(
key: const ValueKey('username'),
validator: (value) {
if (value!.isEmpty || value.length < 6) {
return 'Please enter at least 6 characters.';
} else {
return null;
}
},
decoration: const InputDecoration(
labelText: 'Username',
),
onSaved: (value) {
_userName = value!;
},
),
TextFormField(
key: const ValueKey('password'),
validator: (value) {
if (value!.isEmpty || value.length < 8) {
return 'Password must be at least 7 characters along.';
} else {
return null;
}
},
decoration: const InputDecoration(
labelText: 'Password',
),
obscureText: true,
onSaved: (value) {
_userPassword = value!;
},
),
const SizedBox(
height: 12,
),
ElevatedButton(
onPressed: _trySubmit,
child: Text(_isLogin ? 'Login' : 'Signup')),
TextButton(
onPressed: () {
setState(() {
_isLogin = !_isLogin;
});
},
child: Text(
_isLogin
? 'Create a new account'
: 'I already have an account',
style: const TextStyle(color: Colors.pink),
))
],
),
),
),
),
),
);
}
}
and my authscreen code is
import 'package:chat_app/Widgets/auth/auth_form.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class AuthScreen extends StatefulWidget {
const AuthScreen({Key? key}) : super(key: key);
#override
State<AuthScreen> createState() => _AuthScreenState();
}
class _AuthScreenState extends State<AuthScreen> {
final _auth = FirebaseAuth.instance;
void _submitAuthForm(
String username,
String email,
String password,
bool isLogin,
BuildContext context,
) async {
UserCredential authResult;
try {
if (isLogin) {
authResult = await _auth.signInWithEmailAndPassword(
email: email, password: password);
} else {
authResult = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
}
} on PlatformException catch (error) {
var message = 'An error occured, please check your credentials';
if (error.message != null) {
message = error.message!;
}
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(message),
backgroundColor: Theme.of(context).errorColor,
));
} catch (error) {
// ignore: avoid_print
print(error.toString());
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.pink,
body: AuthForm(_submitAuthForm),
);
}
}
when i tried to sign up, i see the error in badly formated in my email address but i entered the right email address, can anyone help me please?

Getting data from TextFormField

I have 3 files:
database.dart
import 'package:cloud_firestore/cloud_firestore.dart';
class DatabaseService{
final String uid;
DatabaseService({required this.uid});
final CollectionReference userCollection = FirebaseFirestore.instance.collection('users');
Future updateUserData(String name) async {
return await userCollection.doc(uid).set({
'name': name,
});
}
another file called auth.dart
class AuthService{
final FirebaseAuth _auth = FirebaseAuth.instance;
Future registerWithEmailAndPassword(String email, String password) async {
try{
UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
User? user = result.user;
// Create a new document for the user with the uid
await DatabaseService(uid: user!.uid).updateUserData();
return _userFromFirebaseUser(user);
}
catch(e){
print(e);
return null;
}
}
}
And another file called register.dart with this code:
import 'package:flutter/material.dart';
class Register extends StatefulWidget {
const Register({Key? key}) : super(key: key);
#override
_RegisterState createState() => _RegisterState();
}
class _RegisterState extends State<Register> {
String name = '';
#override
Widget build(BuildContext context) {
return Container(
child: Form(
child: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration
(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
hintText: "Full Name",
border: OutlineInputBorder(borderRadius: BorderRadius.circular(32.0))
),
validator: (val) => val!.isEmpty ? 'Enter an email' : null,
onChanged: (val){
setState(() => name = val);
},
),
],
),
),
);
}
}
I want to get the data from TextFormField on register.dart to pass into the function updateUserData on auth.dart. This means the Name will be the data from the keyboard input by the user.
How can I do it? Can someone help me, please?
In your case you are using a Form in combination to TextFormField, in order to retrieve your value you can set a Key to your Form and use it to retrieve your data.
In the case of a simple TextField, you would assign a TextEditingController to it and retrieve its value that way.
Here is an example with a Form, a Key, and validators:
You can then use this value to call your auth fonction with the name as parameter.
final _formKey = GlobalKey<FormState>();
String name = "";
#override
Widget build(BuildContext context) {
return Scaffold(
body: Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (newValue) {
setState(() => name = newValue);
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save(); // Calls onSaved method in your fields
// Other actions such as call your update method
}
},
child: Text('Save'),
),
],
),
),
);
}

Flutter form is showing error for all the fields all at once. How can I make it to show the validation message of one field after the other?

below is my code. i have 6 fields. all of them are showing error all at once. even after i submit the form. once it gets cleared. it would show the validation message as field cannot be empty /Name is required.
how do i validate each field at a time. i don't want all the fields to show validate message as in the below image.
import 'package:flutter/material.dart';
class FormScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return FormScreenState();
}
}
class FormScreenState extends State<FormScreen> {
String _name;
String _email;
String _password;
String _url;
String _phoneNumber;
String _calories;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Widget _buildName() {
return TextFormField(
decoration: InputDecoration(labelText: 'Name'),
maxLength: 10,
validator: (String value) {
if (value.isEmpty) {
return 'Name is Required';
}
return null;
},
onSaved: (String value) {
_name = value;
},
);
}
Widget _buildEmail() {
return TextFormField(
decoration: InputDecoration(labelText: 'Email'),
validator: (String value) {
if (value.isEmpty) {
return 'Email is Required';
}
if (!RegExp(
r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")
.hasMatch(value)) {
return 'Please enter a valid email Address';
}
return null;
},
onSaved: (String value) {
_email = value;
},
);
}
Widget _buildPassword() {
return TextFormField(
decoration: InputDecoration(labelText: 'Password'),
keyboardType: TextInputType.visiblePassword,
validator: (String value) {
if (value.isEmpty) {
return 'Password is Required';
}
return null;
},
onSaved: (String value) {
_password = value;
},
);
}
Widget _builURL() {
return TextFormField(
decoration: InputDecoration(labelText: 'Url'),
keyboardType: TextInputType.url,
validator: (String value) {
if (value.isEmpty) {
return 'URL is Required';
}
return null;
},
onSaved: (String value) {
_url = value;
},
);
}
Widget _buildPhoneNumber() {
return TextFormField(
decoration: InputDecoration(labelText: 'Phone number'),
keyboardType: TextInputType.phone,
validator: (String value) {
if (value.isEmpty) {
return 'Phone number is Required';
}
return null;
},
onSaved: (String value) {
_url = value;
},
);
}
Widget _buildCalories() {
return TextFormField(
decoration: InputDecoration(labelText: 'Calories'),
keyboardType: TextInputType.number,
validator: (String value) {
int calories = int.tryParse(value);
if (calories == null || calories <= 0) {
return 'Calories must be greater than 0';
}
return null;
},
onSaved: (String value) {
_calories = value;
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Form Demo")),
body: Container(
margin: EdgeInsets.all(24),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildName(),
_buildEmail(),
_buildPassword(),
_builURL(),
_buildPhoneNumber(),
_buildCalories(),
SizedBox(height: 100),
RaisedButton(
child: Text(
'Submit',
style: TextStyle(color: Colors.blue, fontSize: 16),
),
onPressed: () {
if (!_formKey.currentState.validate()) {
return;
}
_formKey.currentState.save();
print(_name);
print(_email);
print(_phoneNumber);
print(_url);
print(_password);
print(_calories);
//Send to API
},
)
],
),
),
),
);
}
}
Use FocusNode with autovalidateMode: tempFocusNode.hasFocus? AutovalidateMode.always:AutovalidateMode.disabled as follows,in TextFormField this will solve your problem.
import 'package:flutter/material.dart';
class FormScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return FormScreenState();
}
}
class FormScreenState extends State<FormScreen> {
String _name;
String _email;
String _password;
String _url;
String _phoneNumber;
String _calories;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
//create focusnodes here
FocusNode nameFocusNode= FocusNode();
FocusNode emailFocusNode= FocusNode();
Widget _buildName() {
return TextFormField(
focusNode:nameFocusNode,
autovalidateMode: nameFocusNode.hasFocus
? AutovalidateMode.always
: AutovalidateMode.disabled,
decoration: InputDecoration(labelText: 'Name'),
maxLength: 10,
validator: (String value) {
if (value.isEmpty) {
return 'Name is Required';
}
return null;
},
onSaved: (String value) {
_name = value;
},
);
}
............
Widget _buildEmail() {
return TextFormField(
focusNode:emailFocusNode,
autovalidateMode: nameFocusNode.hasFocus
? AutovalidateMode.always
: AutovalidateMode.disabled,
decoration: InputDecoration(labelText: 'Email'),
validator: (String value) {
if (value.isEmpty) {
return 'Email is Required';
}
if (!RegExp(
r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")
.hasMatch(value)) {
return 'Please enter a valid email Address';
}
return null;
},
onSaved: (String value) {
_email = value;
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Form Demo")),
body: Container(
margin: EdgeInsets.all(24),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildName(),
_buildEmail(),
_buildPassword(),
_builURL(),
_buildPhoneNumber(),
_buildCalories(),
SizedBox(height: 100),
RaisedButton(
child: Text(
'Submit',
style: TextStyle(color: Colors.blue, fontSize: 16),
),
onPressed: () {
if (!_formKey.currentState.validate()) {
return;
}
_formKey.currentState.save();
print(_name);
print(_email);
print(_phoneNumber);
print(_url);
print(_password);
print(_calories);
//Send to API
},
)
],
),
),
),
);
}
}
You should be not use Form widget and TextFormField if you want to display error one at a time. Instead use validation by controllers.
For Example
class MyHomePage extends StatefulWidget {
#override
MyHomePageState createState() {
return new MyHomePageState();
}
}
class MyHomePageState extends State<MyHomePage> {
final _text = TextEditingController();
bool _validate = false;
#override
void dispose() {
_text.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TextField Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Error Showed if Field is Empty on Submit button Pressed'),
TextField(
controller: _text,
decoration: InputDecoration(
labelText: 'Enter the Value',
),
),
RaisedButton(
onPressed: () {
if(_text.text.length<=5){
// open dialog
}
},
child: Text('Submit'),
textColor: Colors.white,
color: Colors.blueAccent,
)
],
),
),
);
}
}
Form's validate() method will validate all the fields in it and it can't break. This is the source code in FormState.
bool _validate() {
bool hasError = false;
for (final FormFieldState<dynamic> field in _fields)
hasError = !field.validate() || hasError;
return !hasError;
}
If you want to validate the field one by one, you should use TextFormField's validate() manually.
import 'package:flutter/material.dart';
class FormScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return FormScreenState();
}
}
class FormScreenState extends State<FormScreen> {
String _name;
String _email;
String _password;
String _url;
String _phoneNumber;
String _calories;
List<GlobalKey<FormFieldState>> fieldKeys;
GlobalKey<FormFieldState> nameKey;
GlobalKey<FormFieldState> emailKey;
GlobalKey<FormFieldState> passwordKey;
GlobalKey<FormFieldState> urlKey;
GlobalKey<FormFieldState> phoneNumberKey;
GlobalKey<FormFieldState> caloriesKey;
#override
void initState() {
super.initState();
nameKey = GlobalKey<FormFieldState>();
emailKey = GlobalKey<FormFieldState>();
passwordKey = GlobalKey<FormFieldState>();
urlKey = GlobalKey<FormFieldState>();
phoneNumberKey = GlobalKey<FormFieldState>();
caloriesKey = GlobalKey<FormFieldState>();
fieldKeys = [
nameKey,
emailKey,
passwordKey,
urlKey,
phoneNumberKey,
caloriesKey,
];
}
bool validate() {
return fieldKeys.every((element) => element.currentState.validate());
}
void save() {
fieldKeys.forEach((element) => element.currentState.save());
}
Widget _buildName() {
return TextFormField(
key: nameKey,
// if you use autovalidateMode:AutovalidateMode.onUserInteractionit is showing validation message for the remaining fields too even before user starts entering the other fields
// then don't use any autovalidateMode.
// autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: InputDecoration(labelText: 'Name'),
maxLength: 10,
validator: (String value) {
if (value.isEmpty) {
return 'Name is Required';
}
return null;
},
onSaved: (String value) {
_name = value;
},
);
}
Widget _buildEmail() {
return TextFormField(
key: emailKey,
decoration: InputDecoration(labelText: 'Email'),
validator: (String value) {
if (value.isEmpty) {
return 'Email is Required';
}
if (!RegExp(
r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")
.hasMatch(value)) {
return 'Please enter a valid email Address';
}
return null;
},
onSaved: (String value) {
_email = value;
},
);
}
Widget _buildPassword() {
return TextFormField(
key: passwordKey,
decoration: InputDecoration(labelText: 'Password'),
keyboardType: TextInputType.visiblePassword,
validator: (String value) {
if (value.isEmpty) {
return 'Password is Required';
}
return null;
},
onSaved: (String value) {
_password = value;
},
);
}
Widget _buildURL() {
return TextFormField(
key: urlKey,
decoration: InputDecoration(labelText: 'Url'),
keyboardType: TextInputType.url,
validator: (String value) {
if (value.isEmpty) {
return 'URL is Required';
}
return null;
},
onSaved: (String value) {
_url = value;
},
);
}
Widget _buildPhoneNumber() {
return TextFormField(
key: phoneNumberKey,
decoration: InputDecoration(labelText: 'Phone number'),
keyboardType: TextInputType.phone,
validator: (String value) {
if (value.isEmpty) {
return 'Phone number is Required';
}
return null;
},
onSaved: (String value) {
_url = value;
},
);
}
Widget _buildCalories() {
return TextFormField(
key: caloriesKey,
decoration: InputDecoration(labelText: 'Calories'),
keyboardType: TextInputType.number,
validator: (String value) {
int calories = int.tryParse(value);
if (calories == null || calories <= 0) {
return 'Calories must be greater than 0';
}
return null;
},
onSaved: (String value) {
_calories = value;
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Form Demo")),
body: Container(
margin: EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildName(),
_buildEmail(),
_buildPassword(),
_buildURL(),
_buildPhoneNumber(),
_buildCalories(),
SizedBox(height: 100),
RaisedButton(
child: Text(
'Submit',
style: TextStyle(color: Colors.blue, fontSize: 16),
),
onPressed: () {
if (!validate()) {
return;
}
save();
print(_name);
print(_email);
print(_phoneNumber);
print(_url);
print(_password);
print(_calories);
//Send to API
},
)
],
),
),
);
}
}
I really liked Thusithas idea but since the widget was not rebuilt when the focus was changed, the autoValidateMode was not changed either.
So I created an observable variable hasFocus, integrated a listener on focus changes (see https://stackoverflow.com/a/68675855/17203788) and wrapped the widget with Obx so it would re-render on focus changes.
In my implementation I used flutter_form_builder but the concept is also applicable to the classic Material form.
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:get/get.dart';
class FormInputField extends StatelessWidget {
/// Unique name of this input field
final String name;
/// Validator for this input field
final FormFieldValidator<String>? validator;
/// Observable boolean whether this text field currently has focus
final RxBool _hasFocus = false.obs;
FormInputField({
required this.name,
this.validator,
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Focus(
onFocusChange: (hasFocus) {
// Update observable
_hasFocus(hasFocus);
},
child: Obx(() {
return FormBuilderTextField(
name: name,
autovalidateMode: _hasFocus.value
? AutovalidateMode.always
: AutovalidateMode.disabled,
validator: validator,
);
}),
);
}
}
Alternatively you could also use a stateful widget and call setState in the onFocusChanged-callback.
Usage:
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
/// Key to identify the form
final GlobalKey<FormBuilderState> _formKey = GlobalKey();
#override
Widget build(BuildContext context) {
return Scaffold(
body: FormBuilder(
key: _formKey,
child: Column(
children: [
FormInputField(
name: 'username',
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(
context,
errorText: 'Name is required',
),
]),
),
FormInputField(
name: 'email',
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(
context,
errorText: 'Email is required',
),
FormBuilderValidators.email(
context,
errorText: 'This is not a valid email',
),
]),
),
ElevatedButton(
onPressed: () {
// Save and validate the form
if (!_formKey.currentState!.saveAndValidate()) {
return;
}
// Extract values from form
final Map<String, dynamic> values = _formKey.currentState!.value;
String username = values['username'];
String email = values['email'];
print(username);
print(email);
},
child: const Text('Submit'),
),
],
),
),
);
}
}

firebase auth with flutter

Please I need help I can't connect firebase with flutter I have this problem I have this message:
Firebase: Could not find the Android Application module. Only Android Application Modules can be connected to Firebase online projects. Create a new Android Application Module or create/import a different Android Studio project.
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'homepage.dart';
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
String _email;
String _password;
GlobalKey<FormState> _keyform = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: new Text("flutter login"),
),
body: new Form(
key: _keyform,
child: new Column(
children: <Widget>[
new TextFormField(
validator: (input) {
if (input.isEmpty) {
return "saisir votre email";
}
},
onSaved: (input) => _email = input,
decoration: InputDecoration(labelText: "email"),
),
new TextFormField(
validator: (input) {
if (input.length < 6) {
return "changer votre mot de passe";
}
},
onSaved: (input) => _password = input,
obscureText: true,
decoration: InputDecoration(labelText: "mot de passe"),
),
RaisedButton(
onPressed: signin,
child: Text("envoyer"),
)
],
)),
);
}
Future<void> signin() async {
final formState = _keyform.currentState;
if (formState.validate()) {
formState.save();
try {
FirebaseUser user = await FirebaseAuth.instance
.signInWithEmailAndPassword(email: _email, password: _password);
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return new homepage();
}));
} catch (e) {
print(e.message);
}
;
}
}
}