I have a basic email contact form that I got from online and I'm trying to send a sample email but some reason I can not enter any text in my input filed. Any suggestion or idea what it might be?
I can enter a text when ever I delete my _formKey but I can not send the email successfully anymore.
Any suggestion or help will be really appreciated.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:email_validator/email_validator.dart';
import 'package:http/http.dart' as http;
class EmailForm extends StatelessWidget {
EmailForm({Key? key}) : super(key: key);
final _formKey = GlobalKey<FormState>();
final nameController = TextEditingController();
final emailController = TextEditingController();
final messageController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xfff5f5fd),
body: Center(
child: Container(
height: 450,
width: 400,
margin: const EdgeInsets.symmetric(
horizontal: 40,
vertical: 20,
),
padding: const EdgeInsets.symmetric(
horizontal: 40,
vertical: 20,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
offset: const Offset(0, 5),
blurRadius: 10,
spreadRadius: 1,
color: Colors.grey[300]!)
]),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Text('Contact Us',
style:
TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
TextFormField(
controller: nameController,
decoration: const InputDecoration(hintText: 'Name'),
validator: (value) {
if (value == null || value.isEmpty) {
return '*Required';
}
return null;
},
),
TextFormField(
controller: emailController,
decoration: const InputDecoration(hintText: 'Email'),
validator: (email) {
if (email == null || email.isEmpty) {
return 'Required*';
} else if (!EmailValidator.validate(email)) {
return 'Please enter a valid Email';
}
return null;
},
),
TextFormField(
controller: messageController,
decoration: const InputDecoration(hintText: 'Message'),
maxLines: 5,
validator: (value) {
if (value == null || value.isEmpty) {
return '*Required';
}
return null;
},
),
SizedBox(
height: 45,
width: 110,
child: TextButton(
style: TextButton.styleFrom(
primary: Colors.white,
backgroundColor: const Color(0xff151534),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40))),
onPressed: () async {
if (_formKey.currentState!.validate()) {
final response = await sendEmail(
nameController.value.text,
emailController.value.text,
messageController.value.text);
ScaffoldMessenger.of(context).showSnackBar(
response == 200
? const SnackBar(
content: Text('Message Sent!'),
backgroundColor: Colors.green)
: const SnackBar(
content: Text('Failed to send message!'),
backgroundColor: Colors.red),
);
nameController.clear();
emailController.clear();
messageController.clear();
}
},
child: const Text('Send', style: TextStyle(fontSize: 16)),
),
),
],
),
),
),
),
);
}
}
Future sendEmail(String name, String email, String message) async {
final url = Uri.parse('https://api.emailjs.com/api/v1.0/email/send');
final response = await http.post(url,
headers: {'Content-Type': 'application/json'},
body: json.encode({
'service_id': '',
'template_id': '',
'user_id': '',
'template_params': {
'from_name': name,
'from_email': email,
'message': message
}
}));
return response.statusCode;
}
When you're using a Form Widget, you don't necessarily need to use controllers you can used the onchange function in your TextFormField Widget.
Instead of defining TextEditingControllers like this:
final nameController = TextEditingController();
final emailController = TextEditingController();
Do this instead:
var name = '';
var email = '';
Instead of
TextFormField(
controller: nameController,
decoration: const InputDecoration(hintText: 'Name'),
validator: (value) {
if (value == null || value.isEmpty) {
return '*Required';
}
return null;
},
),
Do this:
TextFormField(
decoration: const InputDecoration(hintText: 'Name'),
validator: (value) {
if (value == '') {
return '*Required';
}
return null;
},
onChanged: (value) {
name = value; //If it gives some null check error just add an exclamation like this - name = value!;
}
),
Now you just have to pass these variables in your request just like you use any normal variables. Instead of using nameController.text, just name (variable created in the beginning) is enough. This should make the form work perfectly.
If you are not getting 200 status then the problem could also be the difference between the data you're sending and the data that is expected to receive on the server.
To figure out the root cause, I would recommend you to add Print Statements everywhere to see which functions are running properly and which are not.
Printing the error statements coming from the server might help in understanding the root problem. So try this before returning statusCode
print(response.body); //Add this line for the output from the server
return response.statusCode;
And let me know what reply you get in the console.
Future sendEmail(String name, String email, String message) async {
final url = Uri.parse('https://api.emailjs.com/api/v1.0/email/send%27);
const serviceId = 'Enter your service Id';
const templateId = 'Enter your template Id';
const userId = 'Enter your User Id';
final response = await http.post(url,
headers: {'Content-Type': 'application/json'},//This line makes sure it works for all platforms.
body: json.encode({
'service_id': serviceId,
'template_id': templateId,
'user_id': userId,
'template_params': {
'from_name': name,
'from_email': email,
'message': message
}
})
);
print (response.body);
return response.statusCode;
}
You will get Service Id, template Id and User Id from the account that you have created on MailJS.
Related
I am new to flutter and fairly new to Firebase. I have created or I should say, followed a tutorial on how to create a Login/Register Page using Firebase as the backend. Everything works fine, except the fact that when I restart my application and enter an account already created I get the error from the title. NoSuchMethodError (NoSuchMethodError: The getter 'uid' was called on null. Receiver: null Tried calling: uid)
Here is the link to the tutorial I have followed.
https://www.freecodespot.com/blog/flutter-login-and-registration-using-firebase/#Logindart_Source_Code
After that, the application freezes, so I restart it and it shows I am logged in.
P.S. Creating a new account, and trying to log in works. Trying to Log In into an existing account seems to be the problem, but after restarting the app, it works. Sorry for any spelling errors, english is not my first language.
Error on line 91.
import '/models/loginuser.dart';
import '/services/auth.dart';
import 'package:flutter/material.dart';
class Login extends StatefulWidget {
final Function? toggleView;
Login({this.toggleView});
#override
State<StatefulWidget> createState() {
return _Login();
}
}
class _Login extends State<Login> {
bool _obscureText = true;
final _email = TextEditingController();
final _password = TextEditingController();
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final AuthSerice _auth = AuthSerice();
#override
Widget build(BuildContext context) {
final emailField = TextFormField(
controller: _email,
autofocus: false,
validator: (value) {
if (value != null) {
if (value.contains('#') && value.endsWith('.com')) {
return null;
}
return 'Enter an email address!';
}
},
decoration: InputDecoration(
contentPadding: const EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
hintText: "Email",
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(32.0))));
final passwordField = TextFormField(
obscureText: _obscureText,
controller: _password,
autofocus: false,
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'This field is required!';
}
if (value.trim().length < 6) {
return 'Password must be at least 6 characters in length!';
}
// Return null if the entered password is valid
return null;
},
decoration: InputDecoration(
contentPadding: const EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
hintText: "Password",
suffixIcon: IconButton(
icon:
Icon(_obscureText ? Icons.visibility : Icons.visibility_off),
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0),
)));
final txtbutton = TextButton(
onPressed: () {
widget.toggleView!();
},
child: const Text('Register here!'));
final loginEmailPasswordButon = Material(
elevation: 5.0,
borderRadius: BorderRadius.circular(30.0),
color: Theme.of(context).primaryColor,
child: MaterialButton(
minWidth: MediaQuery.of(context).size.width,
padding: const EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
onPressed: () async {
if (_formKey.currentState!.validate()) {
dynamic result = await _auth.signInEmailPassword(LoginUser(email: _email.text,password: _password.text));
if (result.uid == null) { //null means unsuccessfull authentication
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text(result.code),
);
});
}
}
},
child: Text(
"Log in",
style: TextStyle(color: Theme.of(context).primaryColorLight),
textAlign: TextAlign.center,
),
),
);
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: const Text('LOGIN VXS FARMING'),
backgroundColor: Theme.of(context).primaryColor,
centerTitle: true,
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
emailField,
const SizedBox(height: 25.0),
passwordField,
txtbutton,
const SizedBox(height: 35.0),
loginEmailPasswordButon,
const SizedBox(height: 15.0),
],
),
),
),
],
),
);
}
}
Commenting the if from line 91, there is no crash and the log in happens, but if I enter a wrong email, or password, there is no prompt saying that. It just clicks the button and nothing happens.
When making a put request and I edit all the field the object is updated. But if I only update one field on the edit screen than the object is not update. I think this is because the unchanged controllers do not keep the value that is already given to them in the Set state method.
Can anyone give me some hints how to solve this ?
import 'package:fin_app/apiservice/variables.dart';
import 'package:fin_app/screens/reminder/reminder_screen.dart';
import 'package:fin_app/screens/login_screen/login_screen.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../../models/user.dart';
import '../monthly_expense/expense_app_theme.dart';
const String myhomepageRoute = '/';
const String myprofileRoute = 'profile';
class ReminderEdit extends StatefulWidget {
final String id;
final String title;
final String date;
final int amount;
const ReminderEdit({Key? key, required this.id, required this.title,required this.date,required this.amount}) : super(key: key);
#override
_ReminderEditState createState() => _ReminderEditState();
}
class _ReminderEditState extends State<ReminderEdit> {
final GlobalKey<FormState> _formKey = GlobalKey();
String id="";
final TextEditingController _titleInput = TextEditingController();
final TextEditingController _dateInput = TextEditingController();
final TextEditingController _amountInput = TextEditingController();
Future<Reminder>? _futureReminder;
Future<void> edit(String id,String title,String date,int amount) async {
Map<String, String> requestHeaders = {
'Content-type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token'
};
final editReminder = jsonEncode({
'title': title,
'date': date,
'amount': amount
});
if (_titleInput.text.isNotEmpty &&
_dateInput.text.isNotEmpty ) {
var response = await http.put(
Uri.parse("$baseUrl/reminder/me/$id/details"),
body: editReminder,
headers: requestHeaders);
if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Profile edited succesfully.")));
} else {
print(response.statusCode);
print(id);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Could not edit the reminder.")));
}
} else {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("Please fill out the field.")));
}
}
#override
void initState() {
_dateInput.text = ""; //set the initial value of text field
super.initState();
setState(() {
id = widget.id;
_titleInput.text = widget.title;
_dateInput.text = widget.date;
_amountInput.text=widget.amount.toString();
});
}
void navigate() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AlarmPage()),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ExpenseAppTheme.background,
body: Padding(
padding: const EdgeInsets.all(16.0),
child: ListView(
children: <Widget>[
const Align(
alignment: Alignment.topLeft,
child: Text("Edit reminder",
style: TextStyle(
fontSize: 24,
)),
),
const SizedBox(
height: 20,
),
Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
TextFormField(
controller: _titleInput,
decoration: const InputDecoration(
labelText: 'Title',
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
borderSide:
BorderSide(color: Colors.grey, width: 0.0),
),
border: OutlineInputBorder()),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null ||
value.isEmpty ||
value.contains(RegExp(r'^[a-zA-Z\-]'))) {
return 'Use only text!';
}
},
),
const SizedBox(
height: 20,
),
TextFormField(
controller:_dateInput, //editing controller of this TextField
decoration: InputDecoration(
icon: Icon(Icons.calendar_today), //icon of text field
labelText: "Enter Date" //label text of field
),
readOnly:
true, //set it true, so that user will not able to edit text
onTap: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(
2000), //DateTime.now() - not to allow to choose before today.
lastDate: DateTime(2101));
if (pickedDate != null) {
print(
pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000
String formattedDate =
DateFormat('yyyy-MM-dd').format(pickedDate);
print(
formattedDate); //formatted date output using intl package => 2021-03-16
//you can implement different kind of Date Format here according to your requirement
setState(() {
_dateInput.text =
formattedDate; //set output date to TextField value.
});
} else {
print("Date is not selected");
}
},
),
const SizedBox(
height: 35,
),
TextFormField(
controller: _amountInput,
decoration: const InputDecoration(
labelText: 'Amount',
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
borderSide:
BorderSide(color: Colors.grey, width: 0.0),
),
border: OutlineInputBorder()),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Amount must not be empty';
} else if (value.contains(RegExp(r'^[0-9]*$'))) {
return 'Amount must only contain numbers';
}
},
), const SizedBox(
height: 20,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(60)),
onPressed: () {
edit(widget.id,_titleInput.text,_dateInput.text,int.parse(_amountInput.text));
navigate();
},
child: const Text("Submit"),
),
],
),
),
],
),
),
);
}
FutureBuilder<Reminder> buildFutureBuilder() {
return FutureBuilder<Reminder>(
future: _futureReminder,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('Expense added');
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
);
}
}
I'm working on my Flutter app and I try to set up a redirection process after creating an entity (user). The state management is handled by Riverpod. I'm using Firebase for the auth and Postgres for the database.
The insert method in the repository returns a User. With a StateNotifier I just want to check if the method returns a user. If the user is returned I set a success state object (CreateAccountStateSuccess), if it's not I set an error state object with a message. Problem: my saveUser method always return null in my StateNotifier, even though my user is persisted in Firebase and my database. I think it's a Riverpod issue. Any idea?
My repository:
Future<AppUser?> saveUser(String email, String nickname, String role,
String firstname, String lastname) async {
try {
connection.open().then((value) async {
Future<List<Map<String, Map<String, dynamic>>>> result = connection.mappedResultsQuery(
'insert into public.user(email,nickname,role,firstname,lastname) '
'values(#emailValue,#nicknameValue,#roleValue,#firstnameValue,#lastnameValue) '
'returning *',
substitutionValues: {
'emailValue': email,
'nicknameValue': nickname,
'roleValue': role,
'firstnameValue': firstname,
'lastnameValue': lastname,
},
allowReuse: true,
timeoutInSeconds: 30,
);
result.then((value) {
final userFromDataBase = value[0]['user']!;
return AppUser(
email: userFromDataBase['email'],
nickname: userFromDataBase['nickname'],
role: userFromDataBase['role'],
firstname: userFromDataBase['firstname'],
lastname: userFromDataBase['lastname']
);
});
});
} catch (e) {
print(ErrorHandler(message: e.toString()));
return null;
}
return null;
}
My Firebase method to create user for Firebase and using my repository method:
Future<AppUser?> registerWithEmailAndPassword(String email, String password, String nickname, String role, String firstname, String lastname) async {
FirebaseApp app = await Firebase.initializeApp(
name: 'Secondary', options: Firebase.app().options);
try {
UserCredential result =
await FirebaseAuth.instanceFor(app: app).createUserWithEmailAndPassword(email: email, password: password);
User? user = result.user;
if (user == null) {
throw Exception("No user found");
} else {
try {
return await UserRepository(user.email!).saveUser(email, nickname, role, firstname, lastname);
} on PostgreSQLException catch (e) {
print('CATCH POSTGRES EXCEPTION');
print(ErrorHandler(message: e.code.toString()));
}
}
} on FirebaseException catch (e) {
print('CATCH FIREBASE EXCEPTION');
print(ErrorHandler(message: e.code.toString()));
}
return null;
}
My controller:
class CreateAccountController extends StateNotifier<CreateAccountState> {
CreateAccountController(this.ref) : super(const CreateAccountStateInitial());
final Ref ref;
void register(String email, String password, String nickname, String role, String firstname, String lastname) async {
state = const CreateAccountStateLoading();
try {
await ref.read(authRepositoryProvider).registerWithEmailAndPassword(
email,
password,
nickname,
role,
firstname,
lastname
).then((user){
user != null ? state = const CreateAccountStateSuccess() : state = const CreateAccountStateError('Something went wrong with the user creation in database');
});
} catch (e) {
state = CreateAccountStateError(e.toString());
}
}
}
final createAccountControllerProvider =
StateNotifierProvider<CreateAccountController, CreateAccountState>((ref) {
return CreateAccountController(ref);
});
My state objects :
class CreateAccountState extends Equatable {
const CreateAccountState();
#override
List<Object> get props => [];
}
class CreateAccountStateInitial extends CreateAccountState {
const CreateAccountStateInitial();
#override
List<Object> get props => [];
}
class CreateAccountStateLoading extends CreateAccountState {
const CreateAccountStateLoading();
#override
List<Object> get props => [];
}
class CreateAccountStateSuccess extends CreateAccountState {
const CreateAccountStateSuccess();
#override
List<Object> get props => [];
}
class CreateAccountStateError extends CreateAccountState {
final String error;
const CreateAccountStateError(this.error);
#override
List<Object> get props => [error];
}
My screen:
class CreateAccountScreen extends StatefulHookConsumerWidget {
const CreateAccountScreen({Key? key}) : super(key: key);
#override
ConsumerState<CreateAccountScreen> createState() => _CreateAccountScreenState();
}
class _CreateAccountScreenState extends ConsumerState<CreateAccountScreen> {
TextEditingController emailController = TextEditingController();
TextEditingController passwordController = TextEditingController();
TextEditingController nicknameController = TextEditingController();
TextEditingController roleController = TextEditingController();
TextEditingController firstnameController = TextEditingController();
TextEditingController lastnameController = TextEditingController();
#override
Widget build(BuildContext context) {
ref.listen<CreateAccountState>(createAccountControllerProvider, ((previous, state) {
if (state is CreateAccountStateError) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(state.error.toString()),
backgroundColor: Colors.red,
));
}
print(state.toString());
if (state is CreateAccountStateSuccess) {
context.goNamed('/', params:
{
'screenName': 'users'
});
}
}));
return Scaffold(
appBar: AppBar(
title: const Text('Create an account'),
elevation: 8.0,
backgroundColor: Colors.deepOrangeAccent,
actions: [
TextButton.icon(
icon: const Icon(
Icons.logout_rounded,
color: Colors.white,
),
label: const Text('', style: TextStyle(color: Colors.white)),
onPressed: () async {
ref.read(loginControllerProvider.notifier).signOut();
},
),
]
),
body: Padding(
padding: const EdgeInsets.all(10),
child: ListView(
children: <Widget>[
Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(10),
child: const Text(
'Ludocal 2',
style: TextStyle(
color: Colors.deepOrange,
fontWeight: FontWeight.w500,
fontSize: 30),
)),
Container(
padding: const EdgeInsets.all(10),
child: TextFormField(
validator: (value) =>
value == null || value.isEmpty ? "Enter an email" : null,
controller: emailController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Email Address',
),
),
),
Container(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
child: TextFormField(
obscureText: true,
validator: (value) =>
value == null || value.isEmpty ? "Enter a password" : null,
controller: passwordController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
),
),
),
Container(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
child: TextFormField(
validator: (value) =>
value == null || value.isEmpty ? "Enter a nickname" : null,
controller: nicknameController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Nickname',
),
),
),
Container(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
child: TextFormField(
validator: (value) =>
value == null || value.isEmpty ? "Enter a role" : null,
controller: roleController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Role',
),
),
),
Container(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
child: TextFormField(
validator: (value) =>
value == null || value.isEmpty ? "Enter a firstname" : null,
controller: firstnameController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Firstname',
),
),
),
Container(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
child: TextFormField(
validator: (value) =>
value == null || value.isEmpty ? "Enter a lastname" : null,
controller: lastnameController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Lastname',
),
),
),
Container(
height: 50,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
margin: const EdgeInsets.only(top:20),
child: ElevatedButton(
child: const Text('Create', style: TextStyle(color: Colors.white)),
onPressed: () {
ref
.read(createAccountControllerProvider.notifier)
.register(emailController.text, passwordController.text, nicknameController.text,
roleController.text, firstnameController.text, lastnameController.text);
},
)),
],
)),
);
}
}
The problem is in the saveUser function. Instead of using .then use await. It would be like the following:
Future<AppUser?> saveUser(String email, String nickname, String role,
String firstname, String lastname) async {
try {
await connection.open();
final result = await connection.mappedResultsQuery(
'insert into public.user(email,nickname,role,firstname,lastname) '
'values(#emailValue,#nicknameValue,#roleValue,#firstnameValue,#lastnameValue) '
'returning *',
substitutionValues: {
'emailValue': email,
'nicknameValue': nickname,
'roleValue': role,
'firstnameValue': firstname,
'lastnameValue': lastname,
},
allowReuse: true,
timeoutInSeconds: 30,
);
final userFromDataBase = result[0]['user']!;
return AppUser(
email: userFromDataBase['email'],
nickname: userFromDataBase['nickname'],
role: userFromDataBase['role'],
firstname: userFromDataBase['firstname'],
lastname: userFromDataBase['lastname'],
);
} catch (e) {
print(ErrorHandler(message: e.toString()));
return null;
}
}
ive been getting this error for days now but still cant figure it out. My objective is to put message response everytime user enter the same email into the snackbar but what i get is a null.
This is my signup screen.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:form_field_validator/form_field_validator.dart';
import 'package:formvalid/models/register_model.dart';
import 'package:http/http.dart' as http;
import 'package:formvalid/progressHUD.dart';
import 'package:formvalid/screens/login.dart';
class SignupScreen extends StatefulWidget {
const SignupScreen({Key? key}) : super(key: key);
#override
_SignupScreenState createState() => _SignupScreenState();
}
class RegisterAPI {
Future<RegisterResponse> register(
String name, String email, String password) async {
String url = 'http://api.staging.tarsoft.co/api/register';
final response = await http.post(Uri.parse('$url'),
body: {'name': name, 'email': email, 'password': password});
if (response.statusCode == 200 || response.statusCode == 401) {
print(response.body);
// print(response.statusCode);
return RegisterResponse.fromJson(json.decode(response.body));
} else {
print(response.body);
throw Exception('Failed to load data');
}
}
}
class _SignupScreenState extends State<SignupScreen> {
TextEditingController passController = new TextEditingController();
TextEditingController emailController = new TextEditingController();
TextEditingController nameController = new TextEditingController();
GlobalKey<FormState> formKey = GlobalKey<FormState>();
final scaffoldKey = GlobalKey<FormState>();
late RegisterResponse _user;
bool hidePassword = true;
bool isApiCallprocess = false;
#override
void initState() {
super.initState();
_user = new RegisterResponse();
}
#override
Widget build(BuildContext context) {
return ProgressHUD(
child: _uibuild(context),
inAsyncCall: isApiCallprocess,
opacity: 0.3,
);
}
Widget _uibuild(BuildContext context) {
final double height = MediaQuery.of(context).size.height;
return Scaffold(
appBar: AppBar(
key: scaffoldKey,
backgroundColor: Colors.transparent,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.black),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => LoginScreen()));
},
)),
backgroundColor: Color(0xFFffffff),
body: SingleChildScrollView(
child: Form(
key: formKey,
child: Container(
padding: EdgeInsets.only(left: 40, right: 40),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: height * 0.04),
Text(
'Prizes and Suprises',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: Color(0xFF363f93)),
),
Text(
'Await you !',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: Color(0xFF363f93)),
),
SizedBox(
height: height * 0.03,
),
TextFormField(
controller: nameController,
onSaved: (input) => _user.name = input,
decoration: InputDecoration(labelText: 'Enter your name'),
validator: (value) {
if (value!.isEmpty ||
!RegExp(r'^[a-z A-Z]+$').hasMatch(value)) {
return 'Enter correct name';
} else {
return null;
}
}),
SizedBox(height: height * 0.05),
TextFormField(
controller: emailController,
onSaved: (input) => _user.email = input,
decoration: InputDecoration(labelText: 'Enter your email'),
validator: MultiValidator([
RequiredValidator(errorText: 'Enter your email'),
EmailValidator(errorText: 'Not A Valid Email')
])),
SizedBox(height: height * 0.05),
TextFormField(
controller: passController,
onSaved: (input) => _user.password = input,
decoration: InputDecoration(labelText: 'Enter your password'),
validator: MultiValidator([
RequiredValidator(errorText: 'Enter your password'),
MinLengthValidator(6,
errorText: 'Should be at least 6 characters')
]),
obscureText: hidePassword,
),
SizedBox(height: height * 0.05),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () {
final String name = nameController.text;
final String email = emailController.text;
final String password = passController.text;
if (validateAndSave()) {
setState(() {
isApiCallprocess = true;
});
RegisterAPI registerAPI = new RegisterAPI();
registerAPI
.register(name, email, password)
.then((value) => {
setState(() {
isApiCallprocess = false;
}),
if (value.token?.isNotEmpty ?? false)
{
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text(
'${_user.name}')))
}
else
{
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content:
Text('${_user.message}')))
}
});
print(_user.toJson());
}
},
child: Container(
margin: EdgeInsets.only(left: 100.0),
child: Padding(
padding: const EdgeInsets.only(left: 35, top: 10),
child: Text(
'Register',
style: TextStyle(color: Colors.white),
),
),
height: 40,
width: 120,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
color: Color(0xFF363f93),
),
)),
SizedBox(
height: height * 0.09,
),
],
),
],
),
),
),
),
);
}
bool validateAndSave() {
final form = formKey.currentState;
if (form!.validate()) {
form.save();
return true;
}
return false;
}
}
//ignore: must_be_immutable
class TextInput extends StatelessWidget {
final String textString;
TextEditingController textController;
final bool obscureText;
TextInput(
{Key? key,
required this.textString,
required this.textController,
required this.obscureText})
: super(key: key);
#override
Widget build(BuildContext context) {
return TextField(
style: TextStyle(color: Color(0xFF000000)),
cursorColor: Color(0xFF9b9b9b),
controller: textController,
keyboardType: TextInputType.text,
obscureText: obscureText,
decoration: InputDecoration(
hintText: this.textString,
hintStyle: TextStyle(
color: Color(0xFF9b9b9b),
fontSize: 15,
fontWeight: FontWeight.normal),
),
);
}
}
This is my responseregister
class RegisterResponse{
String? email;
String? password;
String? name;
String? token;
String? error;
String? message;
String? success;
RegisterResponse({this.email, this.password, this.name,this.token,this.error,this.message,this.success});
factory RegisterResponse.fromJson(Map<String, dynamic> json) =>
RegisterResponse(
name: json['name'],
email: json['email'],
password: json['password'],
token: json['token'] == null ? json['token'] : '',
error: json['error'],
message: json['email'],
success: json['Success']
);
Map<String, dynamic> toJson() {
Map<String, dynamic> map = {
'name': name,
'email': email,
'password': password,
};
return map;
}
}
this is in debugger console shows "the email has already been taken"
display null
I have watched a couple of tutorials on how to make validators work but none of them seemed to work. Can anyone help me with this? This is the code of a simple sign in page. My validators don't show up on screen if there's any sort of error it should be detecting. I've watched tutorials where it shows up in red but in my app, it doesn't show up at all.
class UserLogin extends StatefulWidget {
UserLogin({this.auth,this.onSignedIn});
final BaseAuth auth;
final VoidCallback onSignedIn;
#override
State<StatefulWidget> createState()=> _UserLoginState();
}
class _UserLoginState extends State<UserLogin> {
final formkey = GlobalKey<FormState>();
bool _validateAndSave()
{
final form = formkey.currentState;
if(form.validate())
{
form.save();
return true;
}
else
return false;
}
static final incorrect_icon = Icon(
Icons.error,
color: Colors.pink,
);
void _validateAndSubmit() async
{
if(_validateAndSave()) {
try {
String userId = await widget.auth.signIn(emailid, password);
print('Signed in! $userId');
//widget.onSignedIn();
Navigator.push(context, MaterialPageRoute(builder: (context)=>Feed()));
}
catch (e) {
print('Error: $e');
}
}
}
static final TextEditingController emailContr = new TextEditingController();
static final TextEditingController passwordContr = new TextEditingController();
static String get emailid => emailContr.text;
static String get password => passwordContr.text;
final _email = Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: TextFormField(
keyboardType: TextInputType.emailAddress,
controller: emailContr,
autofocus: false,
validator: (input) {
if(input.isEmpty)
{
return 'Email cannot be empty';
}
return null;
},
//onSaved: (input)=> emailid = input,
decoration: InputDecoration(
hintText: 'Enter Email Address',
suffixIcon: Icon(Icons.email),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)
),
),
),
);
final _pass = Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: TextFormField(
controller: passwordContr,
obscureText: true,
autofocus: false,
validator: (input) {
if(input.length <= 6)
{
return 'Password should be at least 6 characters';
}
return null;
},
decoration: InputDecoration(
hintText: 'Enter password',
suffixIcon: Icon(Icons.lock),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)
),
),
),
);
/*final login_button =
},
);
*/
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.yellow,
body: Container(
child: Form(
key: formkey,
child: Column(
children: <Widget>[
SizedBox(height: 200,),
Text('Vibing',
style:TextStyle(
fontWeight: FontWeight.bold,
fontSize: 64,
),
),
SizedBox(height: 100,),
_email,
SizedBox(height: 20,),
_pass,
SizedBox(height:30),
RaisedButton(
color: Colors.yellow,
elevation: 5,
child: Text('Login'),
onPressed: (){
_validateAndSubmit();
formkey.currentState.reset();
}
),
SizedBox(height:10),
FlatButton(
child: Text('Forgot password'),
onPressed: ()=> Navigator.push(context, MaterialPageRoute(builder:(context)=>ForgotPassword()),)
),
SizedBox(height:10),
FlatButton(
child: Text('New? Register here!'),
onPressed: ()=> Navigator.push(context, MaterialPageRoute(builder:(context)=>UserReg()),)
),
],
),
),
) ,
);
}
}
The problem is that you're resetting the form after validation so any error shown will reset. Just remove this line from your login button callback:
formkey.currentState.reset();
And voila:
reset():
Resets every [FormField] that is a descendant of this [Form] back to its
[FormField.initialValue].
In your case, the initialValue is empty string "" and that's why when you called reset() method of Form, it's setting an empty string, that will not show any error, as nothing is there.