Why suffixIcon does not work with river_pod? - flutter

I'm preparing password-field with TextFormFieldand riverpod on flutter as followings.
When the suffixIcon is tapped, the icon would change from solidEye to solidEyeSlash, accorging to the bool result of mask.
Actually, the bool result is toggled correctly as checked at //here,
however those icons never change as expected.
How should I fix my code below to let it works?
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
final mask = StateProvider<bool>((ref) => false);
class TextFieldPassword extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
return Container(
margin: const EdgeInsets.only(bottom: 10),
child: TextFormField(
style: const TextStyle(
fontSize: 13,
),
obscureText: true,
decoration: InputDecoration(
labelText: "password",//**
suffixIcon: IconButton(//**
icon: Icon(
ref.read(mask.notifier).state // false
? FontAwesomeIcons.solidEye
: FontAwesomeIcons.solidEyeSlash
),
onPressed: () {
ref.read(mask.notifier).update((state)=>!ref.read(mask.notifier).state);
print(ref.read(mask.notifier).state);//here
},
),
labelStyle: const TextStyle(
fontSize: 15,
color: Color.fromARGB(255, 219, 219, 219),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(2),
borderSide: const BorderSide(
color: Color.fromARGB(255, 219, 219, 219),
width: 1.0,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(2),
borderSide: const BorderSide(
color: Color.fromARGB(255, 219, 219, 219),
width: 1.0,
)),
),
));
}
}

You like to change the icondata, therefore you need to watch the state instead of read.
Icon(ref.watch(mask)
icon: Icon(ref.watch(mask)
? FontAwesomeIcons.solidEye
: FontAwesomeIcons.solidEyeSlash),
And to update data onPressed
onPressed: () {
final bool isVisible = ref.read(mask);
ref.read(mask.notifier).update((state) => !isVisible);
},
class TextFieldPassword extends ConsumerWidget {
const TextFieldPassword({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
return Container(
margin: const EdgeInsets.only(bottom: 10),
child: TextFormField(
style: const TextStyle(
fontSize: 13,
),
obscureText: !ref.watch(mask),
decoration: InputDecoration(
labelText: "password", //**
suffixIcon: IconButton(
icon: Icon(ref.watch(mask) // false
? FontAwesomeIcons.solidEye
: FontAwesomeIcons.solidEyeSlash),
onPressed: () {
final bool isVisible = ref.read(mask);
ref.read(mask.notifier).update((state) => !isVisible);
},
),
labelStyle: const TextStyle(
fontSize: 15,
color: Color.fromARGB(255, 219, 219, 219),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(2),
borderSide: const BorderSide(
color: Color.fromARGB(255, 219, 219, 219),
width: 1.0,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(2),
borderSide: const BorderSide(
color: Color.fromARGB(255, 219, 219, 219),
width: 1.0,
)),
),
));
}
}

Related

SingleChildScrollView isn't working in my login screen

this is the login screen I had Implemented using flutter. I want this screen to be scrollable. how can I do that? I have uploaded my login screen code for your reference. I have tried out my column wrap with a single-child scroll view. but it doesn't work. there are multiple columns, maybe I'm wrapping the wrong column. I appriciate your help on this.
class LoginScreen extends StatefulWidget {
const LoginScreen({Key? key}) : super(key: key);
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
#override
Widget build(BuildContext context) {
final Size = MediaQuery.of(context).size;
return GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color.fromARGB(255, 3, 86, 124), Color(0xff141a3a)],
begin: Alignment.topRight,
end: Alignment.bottomLeft,
)),
child: Scaffold(
backgroundColor: Colors.transparent,
resizeToAvoidBottomInset: false,
body: Padding(
padding: const EdgeInsets.only(top: 40, left: 20, right: 20),
child: Column(
children: [
Expanded(
flex: 2,
child: Column(
children: [
// Spacer(flex: 1),
Image(
image: const AssetImage(
'assets/images/LogoVector.png',
),
height: Size.width / 2.9,
width: Size.width / 2.9,
),
SizedBox(height: 5),
Text(
"LOGIN",
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 30,
color: textWhite,
fontFamily: "Roboto"),
),
],
),
),
// const Spacer(flex: 1),
const Expanded(flex: 3, child: Center(child: LoginForm())),
],
),
),
),
),
);
}
}
class LoginForm extends StatefulWidget {
const LoginForm({Key? key}) : super(key: key);
#override
_LoginFormState createState() => _LoginFormState();
}
Map<String, String> loginUserData = {
'email': '',
'password': '',
'id': '',
'userName': '',
'token': '',
'userStatus': '',
};
class _LoginFormState extends State<LoginForm> {
TextEditingController emailEditingController = new TextEditingController();
TextEditingController passwordEditingController = new TextEditingController();
final _formKey = GlobalKey<FormState>();
String email = "";
String password = "";
String username = "";
bool isLoading = false;
bool typing = true;
bool _isObscure = true;
#override
Widget build(BuildContext context) {
final Size = MediaQuery.of(context).size;
return Form(
key: _formKey,
autovalidateMode: AutovalidateMode.disabled,
child: Column(
children: [
TextFormField(
controller: emailEditingController,
enabled: true,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(
color: textWhite,
),
// borderSide: BorderSide.none
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: textWhite),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: Colors.red),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: Colors.red),
),
isDense: true,
contentPadding: EdgeInsets.fromLTRB(10, 30, 10, 0),
hintText: "Email/ Username",
hintStyle: TextStyle(
color: textWhite, fontFamily: "Roboto", fontSize: 14),
),
style: TextStyle(color: textWhite),
validator: (String? UserName) {
if (UserName != null && UserName.isEmpty) {
return "Email can't be empty";
}
return null;
},
onChanged: (String? text) {
email = text!;
// print(email);
},
onSaved: (value) {
loginUserData['email'] = value!;
},
),
SizedBox(
height: 10,
),
TextFormField(
controller: passwordEditingController,
obscureText: _isObscure,
enabled: true,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: textWhite),
// borderSide: BorderSide.none
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: textWhite),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.red),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.red),
),
isDense: true,
contentPadding: EdgeInsets.fromLTRB(10, 10, 10, 0),
suffixIcon: IconButton(
icon: Icon(
_isObscure ? Icons.visibility : Icons.visibility_off),
color: textWhite,
onPressed: () {
setState(() {
_isObscure = !_isObscure;
});
}),
hintText: "Password",
hintStyle: TextStyle(
color: textWhite,
fontFamily: "Roboto",
fontSize: 14,
)),
style: TextStyle(color: textWhite),
validator: (String? Password) {
if (Password != null && Password.isEmpty) {
return "Password can't be empty";
}
return null;
},
onChanged: (String? text) {
password = text!;
print(password);
},
onSaved: (value) {
loginUserData['password'] = value!;
},
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: CheckboxListTile(
title: const Text(
"Remember Me",
style: TextStyle(
color: textWhite, fontFamily: "Roboto", fontSize: 14),
),
activeColor: buttontext,
// tileColor: buttontext,
value: checkedValue,
onChanged: (newValue) {
FocusManager.instance.primaryFocus?.unfocus();
setState(() {
if (isLoading != true) {
checkedValue = newValue!;
print(newValue);
}
});
},
contentPadding: EdgeInsets.only(left: 0, top: 0),
controlAffinity:
ListTileControlAffinity.leading, // <-- leading Checkbox
),
),
TextButton(
child: Text(
"Forget Password",
style: TextStyle(
color: textWhite, fontFamily: "Roboto", fontSize: 14),
),
onPressed: () {
Get.to(() => Forget_Screen());
},
)
],
),
SizedBox(height: 40),
isLoading
? SpinKitDualRing(
color: textWhite,
size: 40,
)
: GestureDetector(
child: MainButton("Login"),
onTap: () async {
FocusManager.instance.primaryFocus?.unfocus();
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
await LoginData();
// Get.to(BottomNavigation());
}
},
),
SizedBox(height: 15),
Container(
width: 275.0,
height: 40.0,
child: OutlinedButton(
onPressed: () {
Get.to(() => const Signup_Screen());
},
child: const Text(
'Signup',
style: TextStyle(
fontSize: 15, fontFamily: "Roboto", color: textWhite
//fontWeight: FontWeight.w500,
// color: Colors.black,
),
),
style: OutlinedButton.styleFrom(
side: const BorderSide(
width: 1.0,
color: textWhite,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
),
),
),
)
],
),
);
}
}
You should wrap everything under the Scaffold:
Scaffold(
backgroundColor: Colors.transparent,
resizeToAvoidBottomInset: false,
body: SingleChildScrollView(child: Padding(
padding: const EdgeInsets.only(top: 40, left: 20, right: 20),
child: Column(
........
I can not see you using the SingleChildScrollView in then code sample you have provided with.
But if you want to make the page scrollable, Wrap the child of the scaffold in the SingleChildScrollView
Scaffold(
body: SingleChildScrollView(
....

Passing a function in onChanged Flutter

I'm having these 4 inputs (TextFormFields) , and I want to handle a change that whenver the user changes what is written inside , a function is triggered and updates the sum of another TextFormField.
I thought using the onChanged is the best way to handle these changes , but I'm unable to make it.
I have a TextEditingController that I want to update whenever there is a change in the inputs :
TextEditingController totalDocument;
void _CalculTotal() {
setState(() {
total = total + double.tryParse(widget.controllers.last.text);
totalDocument.text = total.toString();
});
}
These are my inputs inside a class :
class InputRefNomProduit extends StatefulWidget {
final String label, label2, label3, label4;
final String content, content2, content3, content4;
var fieldController = TextEditingController();
var fieldController2 = TextEditingController();
var fieldController3 = TextEditingController();
var fieldController4 = TextEditingController();
final FunctionStringCallback Prix;
FormFieldValidator<String> fieldValidator = (_) {};
InputRefNomProduit(
{this.label,
this.content,
this.label2,
this.content2,
this.label3,
this.content3,
this.label4,
this.content4,
this.fieldController,
this.fieldController2,
this.fieldValidator,
this.fieldController3,
this.fieldController4,
**this.Prix**});
. . .
Expanded(
flex: 3,
child: Container(
width: MediaQuery.of(context).size.width / 3.7,
color: Color.fromARGB(255, 255, 255, 255),
child: TextFormField(
**onChanged: widget.Prix,**
enabled: true,
controller: widget.fieldController4,
validator: widget.fieldValidator,
style: TextStyle(
fontSize: 15.0,
color: Colors.black,
),
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
hintText: "${widget.content4}",
hintStyle: TextStyle(
color: Color.fromARGB(255, 190, 190, 190),
fontSize: 14),
fillColor: Color.fromARGB(255, 0, 0, 0),
),
),
),
),
The variable Prix is used to get the function _CalculTotal().
This is my sum TextFormField that will hold the total :
SizedBox(
width: 200,
child: Padding(
padding: const EdgeInsets.only(top: 15),
child: TextFormField(
**controller: totalDocument,**
keyboardType: TextInputType.text,
decoration: InputDecoration(
enabled: false,
hintText: '$total',
hintStyle: TextStyle(
fontFamily: 'Montserrat',
fontWeight: FontWeight.w400,
color: Colors.grey.shade400,
),
prefixIcon: Icon(
Icons.attach_money_outlined,
color: Colors.grey.shade400),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 1,
color: Colors.grey.shade200),
borderRadius:
BorderRadius.circular(5),
),
disabledBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 1,
color: Colors.grey.shade200),
borderRadius:
BorderRadius.circular(5),
),
focusedBorder: OutlineInputBorder(
borderRadius:
BorderRadius.circular(5),
borderSide: BorderSide(
color: Colors.grey.shade400,
width: 1,
),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius:
BorderRadius.circular(5),
borderSide: BorderSide(
color: Colors.grey.shade400,
width: 1,
),
),
errorBorder: OutlineInputBorder(
borderRadius:
BorderRadius.circular(5),
borderSide: BorderSide(
color: Colors.grey.shade400,
width: 1,
),
),
contentPadding:
EdgeInsets.fromLTRB(15, 0, 15, 0),
),
),
),
),
Use the addListener() method of your TextEditingController to listen to changes in the TextFormFields where you assigned your TextEditingController as a controller.
Checkout this flutter cookbook article for more details. It clearly explains the difference between setting the onChanged attribute of a TextFormField and using the addListener() of a controller to handle changes in your TextFields.
https://docs.flutter.dev/cookbook/forms/text-field-changes

How to make TextField's label wont move when out of focus in Flutter

I'm new to flutter.
I'm trying to replicate the following UI, it has multiple TextField and all of their labels won't maximize when I click on other TextField, they keep on focus to show the content inside it: https://i.stack.imgur.com/8lUeV.png
The UI I made: https://i.stack.imgur.com/o9Rpj.png
I tried the autofocus: on but it didn't work cuz it only work for one TextField at a time.
My code:
import 'dart:core';
import 'package:flutter/material.dart';
import 'package:login_sample/models/user.dart';
class EmployeeProfile extends StatefulWidget {
const EmployeeProfile({Key? key, required this.user}) : super(key: key);
final User user;
#override
_EmployeeProfileState createState() => _EmployeeProfileState();
}
class _EmployeeProfileState extends State<EmployeeProfile> {
late String name = '';
late String email = '';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.bottomCenter,
colors: [Colors.blue, Colors.blue])),
height: MediaQuery.of(context).size.height * 0.3
),
Card(
elevation: 20.0,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(50),
topRight: Radius.circular(50),
),
),
margin: const EdgeInsets.only(left: 0.0, right: 0.0, top: 100.0),
child: ListView(
children: <Widget>[
SizedBox(
child: TextField(
autofocus: true,
onChanged: (val){
name = val;
},
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(10.0),
labelText: 'Employee Name',
hintText: widget.user.name,
labelStyle: const TextStyle(
color: Color.fromARGB(255, 107, 106, 144),
fontSize: 14,
fontWeight: FontWeight.w500,
),
border: OutlineInputBorder(
borderSide: const BorderSide(color: Color.fromARGB(255, 107, 106, 144), width: 2),
borderRadius: BorderRadius.circular(10),
),
),
),
width: 150.0,
),
SizedBox(
child: TextField(
autofocus: true,
onChanged: (val){
email = val;
},
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(10.0),
labelText: 'Employee Email',
hintText: widget.user.email,
labelStyle: const TextStyle(
color: Color.fromARGB(255, 107, 106, 144),
fontSize: 14,
fontWeight: FontWeight.w500,
),
border: OutlineInputBorder(
borderSide: const BorderSide(color: Color.fromARGB(255, 107, 106, 144), width: 2),
borderRadius: BorderRadius.circular(10),
),
),
),
width: 150.0,
),
TextButton(
onPressed: (){
print(widget.user.name);
print(widget.user.email);
setState(() {
widget.user.name = name;
widget.user.email = email;
});
},
child: const Text('Save'),
),
],
)
),
Positioned(
top: 0.0,
left: 0.0,
right: 0.0,
child: AppBar(// Add AppBar here only
backgroundColor: Colors.transparent,
elevation: 0.0,
title: Text(
widget.user.name.toString(),
style: const TextStyle(
letterSpacing: 0.0,
fontSize: 20.0,
),
),
),
),
],
),
);
}
}
P/s: sr im not really good at English to describe it correctly
Label will be visible if you focus on the TextField or TextField has content. If what you mean is keeping the label always be visible, you can add floatingLabelBehavior: FloatingLabelBehavior.always on InputDecoration.
import 'dart:core';
import 'package:flutter/material.dart';
import 'package:login_sample/models/user.dart';
class EmployeeProfile extends StatefulWidget {
const EmployeeProfile({Key? key, required this.user}) : super(key: key);
final User user;
#override
_EmployeeProfileState createState() => _EmployeeProfileState();
}
class _EmployeeProfileState extends State<EmployeeProfile> {
late String name = '';
late String email = '';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.bottomCenter,
colors: [Colors.blue, Colors.blue])),
height: MediaQuery.of(context).size.height * 0.3
),
Card(
elevation: 20.0,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(50),
topRight: Radius.circular(50),
),
),
margin: const EdgeInsets.only(left: 0.0, right: 0.0, top: 100.0),
child: ListView(
children: <Widget>[
SizedBox(
child: TextField(
autofocus: true,
onChanged: (val){
name = val;
},
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(10.0),
labelText: 'Employee Name',
hintText: widget.user.name,
// add here
floatingLabelBehavior: FloatingLabelBehavior.always
labelStyle: const TextStyle(
color: Color.fromARGB(255, 107, 106, 144),
fontSize: 14,
fontWeight: FontWeight.w500,
),
border: OutlineInputBorder(
borderSide: const BorderSide(color: Color.fromARGB(255, 107, 106, 144), width: 2),
borderRadius: BorderRadius.circular(10),
),
),
),
width: 150.0,
),
SizedBox(
child: TextField(
autofocus: true,
onChanged: (val){
email = val;
},
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(10.0),
labelText: 'Employee Email',
hintText: widget.user.email,
// add here
floatingLabelBehavior: FloatingLabelBehavior.always
labelStyle: const TextStyle(
color: Color.fromARGB(255, 107, 106, 144),
fontSize: 14,
fontWeight: FontWeight.w500,
),
border: OutlineInputBorder(
borderSide: const BorderSide(color: Color.fromARGB(255, 107, 106, 144), width: 2),
borderRadius: BorderRadius.circular(10),
),
),
),
width: 150.0,
),
TextButton(
onPressed: (){
print(widget.user.name);
print(widget.user.email);
setState(() {
widget.user.name = name;
widget.user.email = email;
});
},
child: const Text('Save'),
),
],
)
),
Positioned(
top: 0.0,
left: 0.0,
right: 0.0,
child: AppBar(// Add AppBar here only
backgroundColor: Colors.transparent,
elevation: 0.0,
title: Text(
widget.user.name.toString(),
style: const TextStyle(
letterSpacing: 0.0,
fontSize: 20.0,
),
),
),
),
],
),
);
}
}
Try below code hope its helpful to you. add your ListView() inside Padding
Padding(
padding: EdgeInsets.all(20),
child: ListView(
children: <Widget>[
SizedBox(
child: TextField(
autofocus: true,
onChanged: (val) {},
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(10.0),
labelText: 'Employee Name',
hintText: 'widget.user.name',
labelStyle: const TextStyle(
color: Color.fromARGB(255, 107, 106, 144),
fontSize: 14,
fontWeight: FontWeight.w500,
),
border: OutlineInputBorder(
borderSide: const BorderSide(
color: Color.fromARGB(255, 107, 106, 144),
width: 2),
borderRadius: BorderRadius.circular(10),
),
),
),
width: 150.0,
),
SizedBox(
height: 20,
),
SizedBox(
child: TextField(
autofocus: true,
onChanged: (val) {},
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(10.0),
labelText: 'Employee Email',
hintText: 'widget.user.email',
labelStyle: const TextStyle(
color: Color.fromARGB(255, 107, 106, 144),
fontSize: 14,
fontWeight: FontWeight.w500,
),
border: OutlineInputBorder(
borderSide: const BorderSide(
color: Color.fromARGB(255, 107, 106, 144),
width: 2),
borderRadius: BorderRadius.circular(10),
),
),
),
width: 150.0,
),
TextButton(
onPressed: () {},
child: const Text('Save'),
),
],
),
),
Your Screen->

Flutter: How to underline in textformfield

I'm trying to make it such that the user can enter his pin, and the pin is supposed to be 4 digits long. Now, the 4 digits that he can enter should be underscored in the text form field (even before he enters the pin), somewhat like ____.
So something like this:
Basically just a normal input field that has the 4 underscores. Is there anyway I can do this in textformfield?
Thanks in advance!
I have tried this on Android Kotlin by using TextWatcher, and I think this could be done by this link below:
Here's a reference!
Try This : https://api.flutter.dev/flutter/material/UnderlineInputBorder-class.html
Full Code :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TextField Input Decoration Demo'),
),
body: new SafeArea(
top: true,
bottom: true,
child: Column(
children: [
Padding(
padding: EdgeInsets.all(20),
child: Row(
children: [
Container(
width: 40,
child: TextFormField(
style: TextStyle(fontSize: 50),
textInputAction: TextInputAction.next,
decoration: const InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black,
width: 10.0,
)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black,
width: 10.0,
),
),
),
)),
SizedBox(width: 5),
Container(
width: 40,
child: TextFormField(
style: TextStyle(fontSize: 50),
textInputAction: TextInputAction.next,
decoration: const InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black,
width: 10.0,
)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black,
width: 10.0,
),
),
),
)),
SizedBox(width: 5),
Container(
width: 40,
child: TextFormField(
style: TextStyle(fontSize: 50),
textInputAction: TextInputAction.next,
decoration: const InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black,
width: 10.0,
)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black,
width: 10.0,
),
),
),
)),
SizedBox(width: 5),
Container(
width: 40,
child: TextFormField(
style: TextStyle(fontSize: 50),
textInputAction: TextInputAction.next,
decoration: const InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black,
width: 10.0,
)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black,
width: 10.0,
),
),
),
)),
],
)),
new Divider(
color: Colors.black,
),
],
)));
}
}

How do you stop a Flutter "Form" from throwing errors?

Working on my personal site, and I want to add a contact form so anyone on the site can fill out a simple form and the site will send me an email. I think I've figured out the email part, but the form the users would have to fill out isn't working yet.
Whenever I start debugging my code it loads into my home page. I click on "Contact Me" at the top right and it takes me to the form page just like it's supposed to, but it immediately shows
Below is my contact_form.dart file in its entirety:
import 'package:flutter/material.dart';
import 'package:flutter_email_sender/flutter_email_sender.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../extensions/email_detection_extension.dart';
import '../../extensions/hover_extensions.dart';
class ContactForm extends StatefulWidget {
const ContactForm({Key key}) : super(key: key);
#override
_ContactFormState createState() => _ContactFormState();
}
class _ContactFormState extends State<ContactForm> {
static const bool _autovalidate = false;
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
final _emailController = TextEditingController();
final _messageController = TextEditingController();
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
autovalidate: _autovalidate, //as you can see I have autovalidate disabled so that shouldn't be it
child: ListView(
children: <Widget>[
Row(
children: <Widget>[
Text(
'Name: ',
style: GoogleFonts.ubuntu(
fontSize: 18,
fontWeight: FontWeight.w500,
height: 1.7,
color: Colors.white,
),
),
SizedBox(width: 10),
SizedBox(
child: TextFormField(
validator: (value){
if(value.isEmpty) return 'This field is required.';
return null;
},
decoration: InputDecoration(
hintText: 'First Last',
hintStyle: GoogleFonts.ubuntu(
fontSize: 18,
fontWeight: FontWeight.w500,
height: 1.7,
color: Colors.white,
),
enabledBorder: const UnderlineInputBorder(
borderSide: const BorderSide(color: Color.fromARGB(255, 220, 220, 220))
),
focusedBorder: const OutlineInputBorder(
borderSide: const BorderSide(color: Color.fromARGB(255, 220, 220, 220))
),
fillColor: Color.fromARGB(255, 255, 255, 255),
focusColor: Color.fromARGB(50, 150, 50, 200),
errorText: 'This field is required.'
),
controller: _nameController
),
width: 500
)
]
),
Row(
children: <Widget>[
Text(
'Email: ',
style: GoogleFonts.ubuntu(
fontSize: 18,
fontWeight: FontWeight.w500,
height: 1.7,
color: Colors.white,
),
),
SizedBox(width: 10),
SizedBox(
child: TextFormField(
validator: (value){
if(!value.isValidEmail()) return 'This field is required and must be of the form "example#website.abc".';
return null;
},
decoration: InputDecoration(
hintText: 'example#website.abc',
hintStyle: GoogleFonts.ubuntu(
fontSize: 18,
fontWeight: FontWeight.w500,
height: 1.7,
color: Colors.white,
),
enabledBorder: const UnderlineInputBorder(
borderSide: const BorderSide(color: Color.fromARGB(255, 220, 220, 220))
),
focusedBorder: const OutlineInputBorder(
borderSide: const BorderSide(color: Color.fromARGB(255, 220, 220, 220))
),
fillColor: Color.fromARGB(255, 255, 255, 255),
focusColor: Color.fromARGB(50, 150, 50, 200),
errorText: 'This field is required and must be of the form "example#website.abc".'
),
controller: _emailController,
keyboardType: TextInputType.emailAddress,
),
width: 500
)
]
),
Row(
children: <Widget>[
Text(
'Message:',
style: GoogleFonts.ubuntu(
fontSize: 18,
fontWeight: FontWeight.w500,
height: 1.7,
color: Colors.white,
),
),
SizedBox(width: 10),
SizedBox(
child: TextFormField(
validator: (value){
if(!value.isValidEmail()) return 'This field is required.';
return null;
},
decoration: InputDecoration(
hintText: 'Message',
hintStyle: GoogleFonts.ubuntu(
fontSize: 18,
fontWeight: FontWeight.w500,
height: 1.7,
color: Colors.white,
),
enabledBorder: const UnderlineInputBorder(
borderSide: const BorderSide(color: Color.fromARGB(255, 220, 220, 220))
),
focusedBorder: const OutlineInputBorder(
borderSide: const BorderSide(color: Color.fromARGB(255, 220, 220, 220))
),
fillColor: Color.fromARGB(255, 255, 255, 255),
focusColor: Color.fromARGB(50, 150, 50, 200),
errorText: 'This field is required.',
),
controller: _messageController
),
width: 500
)
]
),
//Submit button
GestureDetector(
onTap: () async {
if(_formKey.currentState.validate()){
final Email email = Email(
subject: 'Contact Email from ' + _nameController.text,
body: 'Dear Future Me,\n\n' + _nameController.text + " sent you an email from your contact form just now. Here's their message:\n\n" + _messageController.text + '\n\nYou can reach out to them at their provided email: ' + _emailController.text + '\n\nHave a good one, and keep that chin up!\n ~Pseudo-Robotic Past You',
recipients: ['redacted (my email address)'],
isHTML: false
);
try {
await FlutterEmailSender.send(email);
//Navigate to success page
} catch (error) {
//Navigate to failure page
}
}
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 60, vertical: 15),
child: Text(
'Submit',
style: GoogleFonts.ubuntu(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
decoration: BoxDecoration(
color: Color.fromARGB(255, 120, 20, 220),
borderRadius: BorderRadius.circular(5)
),
width: 200
),
).showCursorOnHover
]
)
);
}
}
It's not perfect, I'm gonna clean it up when I'm done, but this is where I stand. Been at this for about 2.5 hours, it's 2 am, and I have a partially functional form that doesn't want to stop erroring. (It shows the error messages even after I've put in acceptable strings and hit submit.) I'd appreciate any ideas on how to fix my autovalidation problem.
Thanks.