Each app is used the TextFormField for different types of use for example saving user name, age, email, password, etc. so have must validate a value of the TextFormField and check null/empty entered by the user so the current TextFormField value does not send the blank.
The developer’s responsibility to the user is not to send the blank value in a save if the value is blank they give the error message on a screen with the red mark and message to indicate.
So the user can verify the fill value current. In this article, I have explained and resolved this type of error for the beginner developer help full.
I have used this app screen in live API and the result is properly working with iOS and Android devices.
Environment
- 1.71.0 (Universal)
- Dart Programming
- Flutter 3.0.5+ Version
- Build iOS
- Build Android
- Run iPhone 11 Pro
- Android SDK
- XCODE
- CMD (on Windows) or Terminal Terminal (on Mac/Linux)
- IDE (Android Studio/IntelliJ/Visual Studio Code)
Features structure
- MVC structure.
- Refactor TextFormField Border Color Red and Normal.
- Delegate pass value in validation.
- ScrollView UI Design.
- Widgets – Button / Textfield / link / Text
Pods(http://pub.dev)
- path_provider: ^2.0.8
- http: ^0.13.0
- shared_preferences: ^2.0.7
- fluttertoast: ^8.0.8
- flutter_spinkit: ^5.1.0
- intl_utils: ^2.7.0
- package_info: ^2.0.0
- permission_handler: ^6.0.1+1
- google_fonts: ^2.1.0
- progress_dialog_null_safe: ^1.0.6
- intl_phone_field: ^3.1.0
- pin_code_fields: ^7.4.0
Let’s get started on the main steps!
have used the StatefulWidget.
final _formKey = GlobalKey<FormState>();
This is the main feature of the error validation of the flutter in a Form create a final globleKey and form state set of the top of the screen as per the requirement.
Basic UI with TextField and ElevatedButton
create TextEditingController as per our screen I have used some text values. major function in a form there are two functions is main -don’t forget to apply on the form
1-->body: Container(
child: Form(
key: _formKey,
child: Center(
child: SingleChildScrollView(
.....
),
),),
)
2-> user the button click event
onPressed: () async {
if (_formKey.currentState!.validate()) {
}
final nameController = TextEditingController();
final mobileController = TextEditingController();
final emailController = TextEditingController();
final confirmPassController = TextEditingController();
final passController = TextEditingController();
bool isShowPass = true;
bool onError = false;
Main Container Widget We have Used the TextFormField in Form under the show error message with creating top of the article bool OnError is a type of value is false.
Example Single TextFormField
Container(
padding: EdgeInsets.only(left: 30, right: 35, top: 4),
child: TextFormField(
style: TextStyle(
color: AppColors.textRegColor, fontSize: 14),
controller: nameController,
decoration: InputDecoration(
prefixIcon: InkWell(
child: Padding(
padding: EdgeInsets.all(5.0),
child: SizedBox(
height: 20,
child: SvgPicture.asset(
"assets/images/Sign_up_user.svg",
fit: BoxFit.scaleDown),
),
),
onTap: () {}),
alignLabelWithHint: true,
fillColor: Colors.white,
filled: true,
floatingLabelBehavior: FloatingLabelBehavior.never,
contentPadding: EdgeInsets.fromLTRB(8, 5, 10, 5),
labelText: "Your Name",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(2.0),
),
labelStyle: TextStyle(
color: AppColors.textWaterColor,
fontSize: 16,
fontFamily: 'Poppins',
fontStyle: FontStyle.normal,
),
),
validator: (String? val) {
if (val!.isEmpty) {
return "Your name is required";
}
return null;
},
)),
onError
? Positioned(
bottom: 0,
child: Padding(
padding: const EdgeInsets.only(left: 30),
child: Text('', style: TextStyle(color: Colors.red)),
))
: Container(),
Full code same page design is given in the images uploaded on this article – see the youtube video demo.
Full code (if you need the same screen please copy and paste the code for images and colors you want to change as per your need)
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:intl_phone_field/phone_number.dart';
import 'package:securisk_app/mainapp/google_location.dart';
import 'package:securisk_app/mainapp/main_login.dart';
import 'package:securisk_app/mainapp/otp.dart';
import 'package:securisk_app/other_desgin/screens/google_map.dart';
import 'package:securisk_app/other_desgin/screens/login_and_reg/google_map2.dart';
import 'package:securisk_app/other_desgin/screens/login_and_reg/map.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart';
import 'package:securisk_app/remote/api_client.dart';
import 'package:securisk_app/utlity/colors.dart';
import 'package:securisk_app/utlity/my_flutter_app_icons.dart';
import 'package:securisk_app/utlity/utils.dart';
import 'package:securisk_app/widgets/bg_image.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:intl_phone_field/intl_phone_field.dart';
import 'package:flutter/gestures.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:geolocator/geolocator.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:progress_dialog_null_safe/progress_dialog_null_safe.dart';
class SignupForm extends StatefulWidget {
@override
State<SignupForm> createState() => _SignupFormState();
}
class _SignupFormState extends State<SignupForm> {
final _formKey = GlobalKey<FormState>();
final nameController = TextEditingController();
final mobileController = TextEditingController();
final emailController = TextEditingController();
final confirmPassController = TextEditingController();
final passController = TextEditingController();
late ProgressDialog prg;
bool isShowPass = true;
bool onError = false;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
double width = MediaQuery.of(context).size.width;
prg = ProgressDialog(context, type: ProgressDialogType.normal);
prg.style(
message: 'Please wait.......',
borderRadius: 4.0,
backgroundColor: Colors.grey,
progressWidget: CircularProgressIndicator(),
maxProgress: 1.0,
);
return Scaffold(
appBar: AppBar(
title: Padding(
padding: const EdgeInsets.only(right: 158.0),
child: Text('Sign Up', style: TextStyle(color: Colors.white)),
),
backgroundColor: AppColors.headerColor,
elevation: 1,
actions: [
Container(
padding: const EdgeInsets.all(8.0),
child: Center(
child: InkWell(
child: Text("Skip for now"),
onTap: () {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => GoogleMapForm()),
ModalRoute.withName("/selectLocation"));
print("value of your selectLocation");
},
))),
]),
backgroundColor: AppColors.mainBg, //
body: Container(
child: Form(
key: _formKey,
child: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 32.0),
child: Text(
'Help us to know better ! ',
style: TextStyle(
fontFamily: 'Poppins',
fontStyle: FontStyle.normal,
fontSize: 16,
color: AppColors.textRegColor),
),
),
),
SizedBox(
height: 20,
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 32.0),
child: Text(
'Name',
style: TextStyle(
fontFamily: 'Poppins',
fontStyle: FontStyle.normal,
fontSize: 16,
color: AppColors.textRegColor),
),
),
),
Container(
padding: EdgeInsets.only(left: 30, right: 35, top: 4),
child: TextFormField(
style: TextStyle(
color: AppColors.textRegColor, fontSize: 14),
controller: nameController,
decoration: InputDecoration(
prefixIcon: InkWell(
child: Padding(
padding: EdgeInsets.all(5.0),
child: SizedBox(
height: 20,
child: SvgPicture.asset(
"assets/images/Sign_up_user.svg",
fit: BoxFit.scaleDown),
),
),
onTap: () {}),
alignLabelWithHint: true,
fillColor: Colors.white,
filled: true,
floatingLabelBehavior: FloatingLabelBehavior.never,
contentPadding: EdgeInsets.fromLTRB(8, 5, 10, 5),
labelText: "Your Name",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(2.0),
),
labelStyle: TextStyle(
color: AppColors.textWaterColor,
fontSize: 16,
fontFamily: 'Poppins',
fontStyle: FontStyle.normal,
),
),
validator: (String? val) {
if (val!.isEmpty) {
return "Your name is required";
}
return null;
},
)),
onError
? Positioned(
bottom: 0,
child: Padding(
padding: const EdgeInsets.only(left: 30),
child: Text('', style: TextStyle(color: Colors.red)),
))
: Container(),
SizedBox(
height: 10,
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 32.0, top: 10),
child: Text(
'Phone Number',
style: TextStyle(
fontFamily: 'Poppins',
fontStyle: FontStyle.normal,
fontSize: 16,
color: AppColors.textRegColor),
),
),
),
Container(
padding: EdgeInsets.only(left: 30, right: 35, top: 10),
child: Column(children: <Widget>[
Container(
child: IntlPhoneField(
controller: mobileController,
// textAlign: TextAlign.right,
// dropdownIconPosition: IconPosition.trailing,
// dropdownTextStyle: ,
style: TextStyle(
fontFamily: 'Poppins',
fontStyle: FontStyle.normal,
fontSize: 16,
color: AppColors.textRegColor),
decoration: InputDecoration(
floatingLabelBehavior: FloatingLabelBehavior.never,
contentPadding: EdgeInsets.fromLTRB(8, 5, 10, 5),
fillColor: Colors.white,
filled: true,
//decoration for Input Field
// labelText: 'Phone Number',
border: OutlineInputBorder(
borderSide: BorderSide(),
),
),
initialCountryCode:
'IN', //default contry code, NP for Nepal
onChanged: (phone) {
setState(() {
codeMobile = phone.countryCode;
});
//when phone number country code is changed
print(phone.completeNumber); //get complete number
print(phone.countryCode); // get country code only
print(phone.number); // only phone number
},
)),
])),
onError
? Positioned(
bottom: 0,
child: Padding(
padding: const EdgeInsets.only(left: 30),
child: Text('', style: TextStyle(color: Colors.red)),
))
: Container(),
SizedBox(
height: 5,
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 32.0),
child: Text(
'E-mail',
style: TextStyle(
fontFamily: 'Poppins',
fontStyle: FontStyle.normal,
fontSize: 16,
color: AppColors.textRegColor),
),
),
),
Container(
padding: EdgeInsets.only(left: 30, right: 35, top: 10),
child: TextFormField(
style: TextStyle(
color: AppColors.textRegColor, fontSize: 14),
controller: emailController,
decoration: InputDecoration(
fillColor: Colors.white,
filled: true,
prefixIcon: InkWell(
child: Padding(
padding: EdgeInsets.all(5.0),
child: SizedBox(
height: 20,
child: SvgPicture.asset(
"assets/images/Sign_up_email_icon.svg",
fit: BoxFit.scaleDown),
),
),
onTap: () {}),
alignLabelWithHint: true,
floatingLabelBehavior: FloatingLabelBehavior.never,
contentPadding: EdgeInsets.fromLTRB(8, 5, 10, 5),
labelText: "Your Email",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(2.0),
),
labelStyle: TextStyle(
color: AppColors.textWaterColor, fontSize: 16),
),
// inputFormatters: [new LengthLimitingTextInputFormatter(10)],
validator: (value) => validateEmail(value),
)),
onError
? Positioned(
bottom: 0,
child: Padding(
padding: const EdgeInsets.only(left: 30),
child: Text('', style: TextStyle(color: Colors.red)),
))
: Container(),
SizedBox(
height: 8,
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 32.0, top: 10),
child: Text(
'Password',
style: TextStyle(
fontFamily: 'Poppins',
fontStyle: FontStyle.normal,
fontSize: 16,
color: AppColors.textRegColor),
),
),
),
Container(
padding: EdgeInsets.only(left: 30, right: 35, top: 10),
child: TextFormField(
style: TextStyle(
color: AppColors.textRegColor, fontSize: 14),
controller: passController,
obscureText: isShowPass,
decoration: InputDecoration(
fillColor: Colors.white,
filled: true,
labelText: "Your Password",
alignLabelWithHint: true,
prefixIcon: InkWell(
child: Padding(
padding: EdgeInsets.all(5.0),
child: SizedBox(
height: 20,
child: SvgPicture.asset(
"assets/images/lock.svg",
fit: BoxFit.scaleDown),
),
),
onTap: () {}),
floatingLabelBehavior: FloatingLabelBehavior.never,
contentPadding: EdgeInsets.fromLTRB(8, 5, 10, 5),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(2.0),
),
labelStyle: TextStyle(
color: AppColors.textWaterColor, fontSize: 16),
suffixIcon: GestureDetector(
onTap: () {
setState(() {
isShowPass = !isShowPass;
});
},
child: Icon(
isShowPass
? Icons.visibility
: Icons.visibility_off,
size: 16,
)),
),
// inputFormatters: [new LengthLimitingTextInputFormatter(10)],
validator: (String? val) {
// String patttern = r'(^[0-9]*$)';
// RegExp regExp = new RegExp(patttern);
if (val!.isEmpty) {
return "Password is required";
}
return null;
},
)),
onError
? Positioned(
bottom: 0,
child: Padding(
padding: const EdgeInsets.only(left: 30),
child: Text('', style: TextStyle(color: Colors.red)),
))
: Container(),
SizedBox(
height: 5,
),
Padding(
padding: EdgeInsets.only(left: 20, right: 35),
child: privacyPolicyLinkAndTermsOfService(),
),
Padding(
padding: EdgeInsets.only(left: 35, right: 35),
child: Container(
padding: EdgeInsets.all(8.0),
// height: 100,
child: Row(
children: [
Expanded(
child: Container(
//height: 100,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: AppColors.buttonColor,
onPrimary: Colors.white,
side: BorderSide(
color: AppColors.buttonColor, width: 5),
),
onPressed: () async {
if (_formKey.currentState!.validate()) {
FocusScope.of(context).unfocus();
var now = DateTime.now();
var formatter =
DateFormat('yyyy-MM-dd hh:mm:ss');
String formattedDate = formatter.format(now);
print(formattedDate);
var deviceDateTimeInfo = formattedDate;
print(centerLetLong);
print(centerLetLong!.latitude);
prg.show();
await ApiClient.oTPApi(
mobileController.text,
).then((myOTP) async {
if (myOTP.status == 'fail') {
prg.hide();
Fluttertoast.showToast(
msg: myOTP.message.toString(),
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0);
} else {
prg.hide();
Fluttertoast.showToast(
msg: myOTP.message.toString(),
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 1,
backgroundColor: Colors.green,
textColor: Colors.white,
fontSize: 16.0);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
OtpVerificationScreen(
userName: nameController.text,
mobileNumber:
mobileController.text,
countryCode: codeMobile,
email: emailController.text,
password: passController.text,
//languageList: [],
lat: centerLetLong!.latitude,
long:
centerLetLong!.longitude,
appVersion: '1.2',
deviceDate:
deviceDateTimeInfo,
)));
}
});
}
prg.hide();
},
child: Text("Continue"),
),
)),
],
),
),
),
InkWell(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => MainLogin()));
},
child: Center(
child: RichText(
text: TextSpan(
text: "already signed up? ",
style: TextStyle(
fontFamily: 'Poppins',
fontStyle: FontStyle.normal,
fontSize: 12,
color: AppColors.darkColor),
children: [
TextSpan(
text: "Login",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
color: Colors.blue),
)
]),
)),
),
],
),
),
),
)),
);
}
}
Final OUTPUT Screen
I hope it was a useful article, please share and subscribe to my channel, See you soon 🤗