I was trying to create a portfolio in flutter web,
then i wanted to add a contact form. I used EmailJS.
When I hit the "send" button I get emails but they are blank...
How can i fix this? can you help me?
and this is part of my code
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: TextField(
controller: messageController,
style: const TextStyle(color: Colors.white),
minLines: 10,
maxLines: 15,
decoration: const InputDecoration(
hintText: 'Enter Your Message',
hintStyle: TextStyle(color: Colors.grey),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFF66FcF1),
),
),
contentPadding: EdgeInsets.all(10.0)),
),
),
Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () {
sendEmail(
email: '',
message: '',
name: '',
subject: '');
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'Submit',
style: TextStyle(
color: Color(0xFF66FcF1),
fontSize: 16,
),
),
),
),
),
],
),
you're sending empty strings
sendEmail(
email: '',
message: '',
name: '',
subject: '');
},
try instead with TextFormField(). Don't forget you have to define the controller for the input to get your strings. And then it should work.
Form(
key: _formKey,
TextFormField(
controller: controllerMensagem,
validator: (value) {
if (value == null || value.isEmpty) {
return '*required';
}
return null;
},
),
),
Everything works correctly since your message is sent, except on line message: ' ', you didn't add the message content that should be sent.
The solution is this,
onPressed: () {
sendEmail(
email: '',
message: _messageController.text.trim(),
name: '',
subject: '');
},
I would also suggest you provide fields to fill all the other fields for a better end user experience.
Related
Widget commonTextFiled(
{required String title,
String errorText = "",
required TextEditingController controller,
int maxLength = 100,
List<TextInputFormatter> inputFormatters = const [],
required String? Function(String?)? validator,
Function(dynamic value)? onChange}) {
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 0.0),
child: TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
maxLength: maxLength,
controller: controller,
inputFormatters: inputFormatters,
autofocus: false,
onChanged: onChange ?? (value) {},
maxLines: 1,
validator: validator,
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(50.0),
),
),
counterText: "",
errorText: errorText == "" ? null : errorText,
errorStyle:
const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
// label: Text(
// title,
// style: TextStyle(fontSize: 14),
// ),
hintText: title,
hintStyle: const TextStyle(
color: ColorCode.placeholderDefault,
fontWeight: FontWeight.w500,
fontSize: 14,
),
filled: true,
labelStyle: const TextStyle(
fontWeight: FontWeight.w500,
),
fillColor: CommonFunctions.getThemeColor(
context,
lightColor: Colors.white70,
darkColor: Theme.of(context).scaffoldBackgroundColor,
),
),
style: const TextStyle(
fontWeight: FontWeight.w700,
),
),
),
const SizedBox(
height: 18,
),
],
);
}
Submit Button code
Center(
child: SizedBox(
height: 50,
width: 100.w,
child: FormButton(
enabled: _isButtonAble,
// enabled: _isButtonActive,
OnPressed: () async {
submitted = true;
FocusScope.of(context)
.requestFocus(FocusNode());
Future.delayed(
const Duration(milliseconds: 100),
() async {
if (_validateForm()) {
if (await CommonFunctions
.isConntectedToInternet(
context)) {
BlocProvider.of<
RequestCallbackCubit>(
context)
.submitForm(
propertyId:
widget.propertyId,
firstName:
_nameController.text,
email: _emailController.text
.replaceAll(' ', ''),
mobileNumber:
_phoneController.text,
description:
_descriptionController
.text,
agreedForContact:
isAgreed ?? true);
} else {
Fluttertoast.showToast(
msg: Constants
.checkInternetConnection);
}
}
});
},
buttonText: Constants.requestCallBack,
fontSize: 16),
),
);
This is the code I'm using
autoValidate:Autovalidate.onUserInteraction
to generate error message while the user is typing in the field. But even after submitting the form the error message is showing up. I tried
controller.clear() and controller.dispose()
but that is not working. How to achieve this? The textfield is clearing after submitting the code but the error message is still showing up. I want the error message to be cleared after submitting the form.
When email is wrong
When all fields are right
After submitting the form and all fields are cleared
So from what i see that you have to use the GlobalKey to validate the fields.
you can declare this one :
final _formKey = GlobalKey<FormState>();
Add the Form on the top of all the textfields :
and give it a key
child: Form(
key: _formKey,
child: // all your widgets.
and on button press in on press add this one :
if (_formKey.currentState!.validate()) {
// all the error fields will be removed, on button press.
}
This is the basic one that i have told you but from what you have described I am crating a example for you based on the code that you provided.
I have the following form to post data to api. the form has textfields and dropdownbuttons as follows. How can I map to the API and post to api?
Container(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 50.0),
child: Form(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: InputDecoration(hintText: 'Job Title'),
controller: titleController,
onChanged: (val) {},
),
SizedBox(height: 20.0),
TextFormField(
decoration: InputDecoration(hintText: 'Company'),
controller: companyController,
onChanged: (val) {},
),
DropdownButton(
value: currentValue,
icon: const Icon(Icons.keyboard_arrow_down),
items: <String>[
"1",
"2",
"3",
].map((String i) {
return DropdownMenuItem(
value: i,
child: Text(i),
);
}).toList(),
onChanged: onChangedCallback,
),
SizedBox(height: 20.0),
DropdownSearch<String>(
mode: Mode.MENU,
showSearchBox: false,
showClearButton: true,
items: ["Aviation", "Arts", "Business"],
dropdownSearchDecoration: InputDecoration(
labelText: "Category",
hintText: "Select Job Category",
),
onChanged: print,
),
SizedBox(height: 20.0),
TextFormField(
decoration: InputDecoration(hintText: 'Location'),
onChanged: (val) {},
),
SizedBox(height: 20.0),
TextFormField(
maxLines: 3,
decoration: InputDecoration(hintText: 'Description'),
onChanged: (val) {},
),
SizedBox(height: 20.0),
TextFormField(
decoration: InputDecoration(hintText: 'Salary'),
onChanged: (val) {},
),
SizedBox(height: 30.0),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Color.fromARGB(255, 252, 114, 2)),
onPressed: () {
},
child: Text(
"Add",
style:
TextStyle(fontSize: 17, fontWeight: FontWeight.bold),
)),
],
),
),
),
),
I want the textfields to get data and post to api while dropdownbuttons have an item and post the item with the corresponding item name on the api? (Like from the following dropdown buttton DropdownButton( value: currentValue, icon: const Icon(Icons.keyboard_arrow_down), items: <String>[ "1", "2", "3", ].map((String i) { return DropdownMenuItem( value: i, child: Text(i), ); }).toList(), onChanged: onChangedCallback, ),
if I select 1, it will post 1 to the api and the same for category. How can this be implmented? )
First of all you need to use an HTTP client package like Dio or tttp
Then you will need to make an api call using this package whenever you click the button.
So an example using Dio package would be like that
import 'package:dio/dio.dart';
void postHttp() async {
try {
var response = await Dio().post('your-api-link',data: {'title': "job title field", 'currentValue': 'current value field'});
print(response);
} catch (e) {
print(e);
}
}
And you will find more info in the package documentation.
Quick hint:
try to test your api using postman first to make sure it works correctly
import 'package:flutter/material.dart';
import 'package:mmitra/widgets/header.dart';
import 'home.dart';
class CreateAccount extends StatefulWidget {
#override
_CreateAccountState createState() => _CreateAccountState();
}
class _CreateAccountState extends State<CreateAccount> {
late String username;
final _key = Global Key<FormState>();
#override
Widget build(BuildContext parentContext) {
return Scaffold(
appBar: header(context, titleText: 'Create Account'),
body: ListView(children: [
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(top: 25),
child: Center(
child: Text(
'Create a username',
style: TextStyle(fontSize: 25.0),
),
),
),
Padding(
padding: EdgeInsets.all(16.0),
child: Form(
child: TextFormField(
key: _key,
// controller: myController,
onSaved: (val) => username = val!,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelStyle: TextStyle(fontSize: 15),
labelText: 'Username',
hintText: 'Must be at least 3 Characters'),
),
),
),
GestureDetector(
onTap: () {
_key.currentState!.save();
Navigator.pop(context, username
);
},
child: Container(
height: 50,
width: 300,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(7)),
child: Center(
child: Text(
'Submit',
style:
TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
),
),
),
),
],
),
),
]),
);
}
}
and I'm getting below shown errors:
Exception caught by gesture
The following Cast Error was thrown while handling a gesture
Null check operator used on a null value
I just want to get the textfromfield value and i want to pass it to the home.dart page in order to create the document in firebase collection
you must define a EditTextControll() ex:textController and set it to TextFormField to controller paramerter , and then get text as textController.text and pass with Navigatoer .
for pass wihtin first screen to two screen
Firstly /
TextEditingController controller = TextEditingController();
Secoundly /
body: Form(
child: TextFormField(
controller: controller,
onTap: (){
//here SettingsScreen() is example
// name is paramerter in the secound screen
Navigator.push(context,
MaterialPageRoute(builder: (context)=>SettingsScreen(name:controller.text),),
);
},
),
),
You added key in TextFormField
Form(
child: TextFormField(
key: _key,
// controller: myController,
onSaved: (val) => username = val!,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelStyle: TextStyle(fontSize: 15),
labelText: 'Username',
hintText: 'Must be at least 3 Characters'),
),
),
),
Remove it from TextFormField and Add it in Form
Just Like:
Form(
key: _key,
child: TextFormField(
// controller: myController,
onSaved: (val) => username = val!,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelStyle: TextStyle(fontSize: 15),
labelText: 'Username',
hintText: 'Must be at least 3 Characters'),
),
),
),
This will Solve your Issue.
Here when I type inside text fields Keyboard covers the Login button. So I need to scroll down to the Login button when typing. I tried wrapping LayoutBuilder with SingleChildScrollView and tried using Positioned widget inside Stack but nothing solved my issue. And I set physics to AlwaysScrollableScrollPhysics() inside SingleChildScrollView but it also didn't solve the problem. I can't figure out what I've done wrong. I would be grateful if anyone can help me with this issue
Here's my code
Material(
child: SingleChildScrollView(
child: Stack(
overflow: Overflow.clip,
children: <Widget>[
Image.asset(
'assets/login-screen-img.jpg'
),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 220.0, 16.0, 0),
child: Card(
child: Padding(
padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 24.0),
child: Form(
//associating global key with the form(It keeps track of the form)
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Email', style: TextStyle(fontSize: 14.0, color: Colors.grey),),
TextFormField( // email field
cursorColor: Colors.brown[500],
decoration: InputDecoration(
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.brown[500])
),
//hintText: 'Enter your Email'
),
// validation
validator: (email) => email.isEmpty ? 'Enter the email' : null,
onChanged: (emailInput) {
setState(() {
email = emailInput;
});
},
),
SizedBox(height: 16.0),
Text('Password', style: TextStyle(fontSize: 14.0, color: Colors.grey),),
TextFormField( // password field
cursorColor: Colors.brown[500],
decoration: InputDecoration(
//hintText: 'Enter your Password'
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.brown[500])
)
),
// validation
validator: (password) => password.length < 6 ? 'Password must be more than 6 characters' : null,
obscureText: true, // hide when type
onChanged: (passwordInput) {
setState(() {
password = passwordInput;
});
},
),
SizedBox(height: 48.0,),
Center(
child: RaisedButton( // login button
child: Text('LOG IN', style: TextStyle(fontSize: 16.0, color: Colors.white),),
color: Colors.brown[500],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25)
),
padding: EdgeInsets.fromLTRB(66.0, 16.0, 66.0, 16.0),
onPressed: () async {
if(_formKey.currentState.validate()) {
// show loading screen
setState(() {
loading = true;
});
dynamic result = await _auth.signInWithEmailAndPassword(email, password);
if(result == null) {
// stop showing loading screen/widget
setState(() {
loading = false;
});
// show an error message
Fluttertoast.showToast(
msg: 'Could not sign in!',
toastLength: Toast.LENGTH_SHORT,
);
}
}
},
),
),
SizedBox(height: 24.0),
Center(child: Text('Don\'t have and account ?' )),
SizedBox(height: 16.0,),
Center(
child: FlatButton( // sign up button
child: Text('SIGN UP', style: TextStyle(fontSize: 16.0, color: Colors.brown[500] )),
onPressed: (){
Navigator.push(context, MaterialPageRoute(
builder: (context) => SignUp()
));
},
),
)
],
),
),
),
),
)
],
),
),
);
Screenshot of my UI
Here I found that the issue is with the height of the stack. As #sajithlakmal mentioned in the comments, height of the stack is small and there is nothing to scroll. But in my case, I don't want to make an extra height than the screen height because this is just a login screen. I could easily solve the issue by replacing Material widget with Scaffold. inside the body of the Scaffold gives the required height when typing and able to scroll down.
Here's the working code.
Scaffold(
body: SingleChildScrollView(
child: Stack(
overflow: Overflow.visible,
children: <Widget>[
Image.asset(
'assets/login-screen-img.jpg',
alignment: Alignment.topCenter,
),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 220.0, 16.0, 0),
child: Card(
child: Padding(
padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 24.0),
child: Form(
//associating global key with the form(It keeps track of the form)
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Email', style: TextStyle(fontSize: 14.0, color: Colors.grey),),
TextFormField( // email field
cursorColor: Colors.brown[500],
decoration: InputDecoration(
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.brown[500])
),
//hintText: 'Enter your Email'
),
// validation
validator: (email) => email.isEmpty ? 'Enter the email' : null,
onChanged: (emailInput) {
setState(() {
email = emailInput;
});
},
),
SizedBox(height: 16.0),
Text('Password', style: TextStyle(fontSize: 14.0, color: Colors.grey),),
TextFormField( // password field
cursorColor: Colors.brown[500],
decoration: InputDecoration(
//hintText: 'Enter your Password'
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.brown[500])
)
),
// validation
validator: (password) => password.length < 6 ? 'Password must be more than 6 characters' : null,
obscureText: true, // hide when type
onChanged: (passwordInput) {
setState(() {
password = passwordInput;
});
},
),
SizedBox(height: 48.0,),
Center(
child: RaisedButton( // login button
child: Text('LOG IN', style: TextStyle(fontSize: 16.0, color: Colors.white),),
color: Colors.brown[500],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25)
),
padding: EdgeInsets.fromLTRB(66.0, 16.0, 66.0, 16.0),
onPressed: () async {
if(_formKey.currentState.validate()) { // check validation
// show loading screen
setState(() {
loading = true;
});
dynamic result = await _auth.signInWithEmailAndPassword(email, password);
if(result == null) {
// stop showing loading screen/widget
setState(() {
loading = false;
});
// show an error message
Fluttertoast.showToast(
msg: 'Could not sign in!',
toastLength: Toast.LENGTH_SHORT,
);
}
}
},
),
),
SizedBox(height: 24.0),
Center(child: Text('Don\'t have and account ?' )),
SizedBox(height: 16.0,),
Center(
child: FlatButton( // sign up button
child: Text('SIGN UP', style: TextStyle(fontSize: 16.0, color: Colors.brown[500] )),
onPressed: (){
Navigator.push(context, MaterialPageRoute(
builder: (context) => SignUp()
));
},
),
)
],
),
),
),
),
)
],
),
),
);
I was trying to create a register page for my app, but when i load the page i got this error
I'm working with Flutter in Visual Studio code
Widget build(BuildContext context) {
TextFormField email = new TextFormField(
validator: (value) {
Pattern pattern =
r'^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regex = new RegExp(pattern);
if (!regex.hasMatch(value))
return 'Ingrese un correo valido';
else
return null;
},
controller: emailcontroller,
decoration: InputDecoration(
hintText: 'Email',
prefixIcon: Icon(Icons.account_circle),
border: _textFieldBorder,
),
style: TextStyle(),
textAlign: TextAlign.left,
);
TextFormField name = new TextFormField(
controller: namecontroller,
decoration: InputDecoration(
hintText: 'Nombre',
prefixIcon: Icon(Icons.person_pin),
border: _textFieldBorder,
),
style: TextStyle(),
textAlign: TextAlign.left,
validator: (value) {
if (value.isEmpty) {
return 'Este campo no puede estar vacio';
} else
return null;
},
);
TextFormField cellphone = new TextFormField(
maxLength: 9,
controller: phonecontroller,
decoration: InputDecoration(
hintText: 'Phone',
prefixIcon: Icon(Icons.phone),
border: _textFieldBorder,
),
style: TextStyle(),
textAlign: TextAlign.left,
);
TextFormField password = new TextFormField(
controller: passwordcontroller,
obscureText: true,
decoration: InputDecoration(
hintText: 'Contraseña',
prefixIcon: Icon(Icons.lock),
border: _textFieldBorder,
),
style: TextStyle(),
textAlign: TextAlign.left,
);
TextFormField confirmpassword = new TextFormField(
obscureText: true,
decoration: InputDecoration(
hintText: 'Repita la contraseña',
prefixIcon: Icon(Icons.lock),
border: _textFieldBorder,
),
style: TextStyle(),
textAlign: TextAlign.left,
validator: (value) {
if (value == passwordcontroller.text) {
return null;
} else {
return 'Las contraseñas no son iguales';
}
},
);
return Scaffold(
appBar: AppBar(
title: Text(
'Registro',
style: TextStyle(fontSize: 35),
),
backgroundColor: Color(0xff2196F3),
centerTitle: true,
),
body: Mutation(
options: MutationOptions(
document: query,
),
builder: (RunMutation insert, QueryResult result) {
return new SingleChildScrollView(
child: Form(
key: _formKey,
child: Center(
child: Container(
height: MediaQuery.of(context).size.height / 1.1,
child: Padding(
padding: EdgeInsets.fromLTRB(30, 0, 30, 0),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
email,
SizedBox(height: 8),
name,
SizedBox(height: 8),
cellphone,
SizedBox(height: 8),
password,
SizedBox(height: 8),
confirmpassword,
Divider(height: 40),
GestureDetector(
onTap: () {
if (_formKey.currentState.validate()) {
insert(<String, dynamic>{
"name": namecontroller.text,
"phone": phonecontroller.text,
"password": passwordcontroller.text,
"email": emailcontroller.text
});
Navigator.pop(context);
}
},
child: new Container(
height: 50,
width: MediaQuery.of(context).size.width / 1.2,
decoration: BoxDecoration(
color: Color(0xff2196F3),
border: Border.all(
width: 1.0, color: Colors.transparent),
borderRadius: BorderRadius.all(
Radius.circular(10.0)
), ),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text('Registrarse', style: _loginTextStyle)
],
),
),
)
],
),
),
),
),
),
);
}));
}
I expect to see the register page but the only thing i get is this error "Another exception was thrown: NoSuchMethodError: The getter 'value' was called on null"
The problem was that i was using a GraphQL client and that client uses an instance of my widget called RegisterPage, and i was trying to navigate to RegisterPage , but the correct thing to do was navigate to my GraphQL client called RegistarPageGraphQL and that client build the RegisterPage