How to make a TextFormField like Google? - flutter

I am interested in the implementation of TextField in registering a Google account on Flutter. How can I make a similar series of TextFields from a date where all three have one errorText and when they click “next”, three are checked at once, if one is not entered, everything turns red, even if they were correct. It is like one of the three.

For the outline text field, you could use
TextField(
decoration: new InputDecoration(
border: new OutlineInputBorder(),
filled: true,
hintText: "Type in your text",
),
)
and for the validation, the best way to achieve it is to use a form with validation
and TextFormField instead of TextField
example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final appTitle = 'Form Validation Demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: Text(appTitle),
),
body: MyCustomForm(),
),
);
}
}
// Create a Form widget.
class MyCustomForm extends StatefulWidget {
#override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
// Create a global key that uniquely identifies the Form widget
// and allows validation of the form.
//
// Note: This is a GlobalKey<FormState>,
// not a GlobalKey<MyCustomFormState>.
final _formKey = GlobalKey<FormState>();
String _firstName;
String _lastName;
#override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextFormField(
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (val) => _firstName = val,
),
TextFormField(
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (val) => _lastName = val,
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: RaisedButton(
onPressed: () {
// Validate returns true if the form is valid, or false
// otherwise.
final form = _formKey.currentState;
if (form.validate()) {
form.save();
// If the form is valid, display a Snackbar.
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('The result: $_firstName, $_lastName')));
}
},
child: Text('Submit'),
),
),
],
),
);
}
}

Related

Flutter Textformfield validator Focuses Last TextFormfield on validation error instead of first

I've two TextFormFields, it focus on the password field on validation error, even if email field has error already & comes before password field.
Any idea what's going wrong here?
//Email
TextFormField(
controller: _emailController,
focusNode: _emailFocus,
validator: (value) {
String? err = validateEmail(value);
if (err != null) {
_emailFocus.requestFocus();
}
return err;
},
),
//Password
TextFormField(
controller: _passwordController,
focusNode: _passwordFocus,
validator: (value) {
String? err = validatePassword(value);
if (err != null) {
_passwordFocus.requestFocus();
}
return err;
},
),
String? validateEmail(String? value) {
String pattern = r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[a-zA-Z0-9](?:[a-zA-Z0-9-]"
r"{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]"
r"{0,253}[a-zA-Z0-9])?)*$";
RegExp regex = RegExp(pattern);
if (value == null || value.isEmpty || !regex.hasMatch(value)) {
return 'Enter a valid email address';
} else {
return null;
}
}
String? validatePassword(String? value) {
String pattern = r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$!%*?&])[A-Za-z\d#$!%*?&]{8,}$";
RegExp regex = RegExp(pattern);
if (value == null || value.isEmpty) {
return 'Required';
}
if (value.length < 8) {
return "Length should be 8 or more";
}
if (!regex.hasMatch(value)) {
return "Must contain atleast 1 uppecase, 1 lowercase, 1 special character,";
}
return null;
}
Ignore this silly paragraph:(This is just bunch of text, to tell SO that I have added more question details even if it is NOT required and NOT available)
Wrap it with a form widget and validate it only on a button click like the following.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
const appTitle = 'Form Validation Demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: const Text(appTitle),
),
body: const MyCustomForm(),
),
);
}
}
// Create a Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({super.key});
#override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
// Create a global key that uniquely identifies the Form widget
// and allows validation of the form.
//
// Note: This is a GlobalKey<FormState>,
// not a GlobalKey<MyCustomFormState>.
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
// The validator receives the text that the user has entered.
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
// Validate returns true if the form is valid, or false otherwise.
if (_formKey.currentState!.validate()) {
// If the form is valid, display a snackbar. In the real world,
// you'd often call a server or save the information in a database.
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Processing Data')),
);
}
},
child: const Text('Submit'),
),
),
],
),
);
}
}
Check this for a detailed explanation
https://docs.flutter.dev/cookbook/forms/validation
Edit
Please remove focus request if it's null. That will always keep the focus on password field if both are null
Problem
You have 2 validators and the last one will work the last. That means if your both TextFormField is not valid the last one always works last and your focus goes to the last one.
Solution
check the other focusNode inside of another and focus password area if email focusNode has not focused like below
//Password
TextFormField(
controller: _passwordController,
focusNode: _passwordFocus,
validator: (value) {
String? err = validatePassword(value);
if (err != null) {
if(!_emailFocus.hasFocus){
_passwordFocus.requestFocus();
}
}
return err;
},
),

"onSaved()" function in TextFormField not being reached (dart/flutter)

I have built a very simple form in flutter and I am trying to save the value of whatever is typed in the form fields to variables. This way, I can push these variables to firebase. However, nothing in the onSaved() block of the TextFormFields is being run. I have called save() on the current state of the form, but it still doesn't seem to work. Any ideas?
I have attached the code for the page below:
import 'package:flutter/material.dart';
class AddJobPage extends StatefulWidget {
const AddJobPage({Key? key}) : super(key: key);
static Future<void> show(BuildContext context) async {
await Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const AddJobPage(),
fullscreenDialog: true
));
}
#override
_AddJobPageState createState() => _AddJobPageState();
}
class _AddJobPageState extends State<AddJobPage> {
final _formKey = GlobalKey<FormState>();
//These two variables are where we will store the values of the text form fields
//before we push to firestore.
String? _name = '';
int _ratePerHour = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 2,
title: const Text("New Job"),
backgroundColor: Colors.teal.shade700,
actions: [
TextButton(
onPressed: _submit,
child: const Text(
'Save',
style: TextStyle(fontSize: 18, color: Colors.white),
)
)
],
),
body: _buildContents(),
backgroundColor: Colors.grey.shade200,
);
}
void _submit() {
if(_validateAndSave()) {
print("form saved, name: $_name, ratePerHour: $_ratePerHour");
}
}
bool _validateAndSave() {
final form = _formKey.currentState;
if(form!.validate()) {
print("the form was saved here");
form.save;
return true;
}
return false;
}
Widget _buildContents() {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16),
child: Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: _buildForm(),
),
),
),
);
}
Widget _buildForm() {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: _buildFormChildren(),
)
);
}
List<Widget> _buildFormChildren() {
return [
TextFormField(
decoration: const InputDecoration(labelText: 'Job name'),
onSaved: (value) {
print("code doesn't reach here");
_name = value; //save the value of the text field to _name
}
),
TextFormField(
decoration: const InputDecoration(labelText: 'Rate Per Hour'),
keyboardType: const TextInputType.numberWithOptions(
signed: false,
decimal: false
),
onSaved: (value) {
_ratePerHour = int.tryParse(value!) ?? 0;
}
),
];
}
}
You need to call the save function.
So replace form.save; in your code with form.save();
replace form.save with form.save() and you're done. No need to worry about text controllers since you're using a text form field
you need to add a TextEditingController to get the text from a TextFormField. Then you need to call setstate inside OnChanged, not on Onsaved so you can transfer the text to a variable. This is what your code should look like:
First you initialize a controller like this
final TextEditingController textController = TextEditingController();
String _name = "not set";
then you add the controller to your textformfield like this
TextFormField(
controller: textController ,
onChanged:(value)
{
setState(() {
_name = textController.text;
});
} ,
)
This is a complete example:
class TextFormFieldExample extends StatefulWidget {
const TextFormFieldExample({Key? key}) : super(key: key);
#override
_TextFormFieldExampleState createState() => _TextFormFieldExampleState();
}
class _TextFormFieldExampleState extends State<TextFormFieldExample> {
//Create the controller here
final TextEditingController textController = TextEditingController();
String _name = "not set";
#override
Widget build(BuildContext context) {
print(_name);
return Scaffold(
body: Material(
child: Container(
color: Colors.white,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child:TextFormField(
controller: textController ,//Attach the controller to the text form here
onChanged:(value)
{
setState(() {
_name = textController.text;//Save the text from the controller to a variable
});
} ,
),
),
),
);
}
}

DateFormField call save from form

the code from this question:
Receive Response from pop navigator in Flutter
with added DateTime picker form field.
If we add DateFormField like this:
maind.dart
import 'package:flutter/material.dart';
import 'package:date_field/date_field.dart';
import 'answer.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final appTitle = 'Form Validation Demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: Text(appTitle),
),
body: ShowData(),
),
);
}
}
// Create a Form widget.
class MyCustomForm extends StatefulWidget {
#override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
// Create a global key that uniquely identifies the Form widget
// and allows validation of the form.
//
// Note: This is a GlobalKey<FormState>,
// not a GlobalKey<MyCustomFormState>.
final _formKey = GlobalKey<FormState>();
final myController = TextEditingController();
Data stateData = Data();
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Scaffold(
body: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
DateFormField(
initialDatePickerMode: DatePickerMode.day,
enabled: true,
key:_formKey,
onSaved: (DateTime value)
{
stateData.datefield = value;
},
validator: (DateTime value){
return stateData.datefield != null ? null : 'enter date';
},
firstDate: DateTime.now().subtract(Duration(days: 180)),
lastDate: DateTime.now().add(Duration(days: 365)),
),
TextFormField(
controller: myController,
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (value){
stateData.load = value;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: RaisedButton(
onPressed: () {
// Validate returns true if the form is valid, or false
// otherwise.
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
// If the form is valid, display a Snackbar.
Navigator.pop(context,stateData);
// Scaffold.of(context)
// .showSnackBar(SnackBar(content: Text(myController.text)));
// myController.text = 'look at me';
}
},
child: Text('Submit'),
),
),
],
),
),
);
}
}
class Data {
String load;
DateTime datefield;
}
when pop happens there is exception that the validator was called on null.
and "The following assertion was thrown while finalizing the widget tree:
Multiple widgets used the same GlobalKey."
if the key field is not used, then the validator is not called upon.
why?
how do you use DateFormField? from package
date_field: "^0.1.2"
adding answer.dart
import 'package:flutter/material.dart';
import 'main.dart';
class ShowData extends StatefulWidget {
#override
_ShowDataState createState() => _ShowDataState();
}
class _ShowDataState extends State<ShowData> {
String data = 'start';
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: () async {
final holder = await getFormData(context);
setState(() {
data = holder.load;
});
},
elevation: 4,
),
body:Text(data,style: TextStyle(fontSize: 80),));
}
Future<Data> getFormData(BuildContext context) async {
final answer = await Navigator.push(context,MaterialPageRoute(builder: (context)=>MyCustomForm()));
return (Future.value(answer));
}
}
You can copy paste run full code below
Step 1: remove key:_formKey
DateFormField(
initialDatePickerMode: DatePickerMode.day,
enabled: true,
//key:_formKey,
Step 2: validator use value != null not stateData.datefield != null
validator: (DateTime value) {
//return stateData.datefield != null ? null : 'enter date';
return value != null ? null : 'enter date';
},
working demo
full code
import 'package:flutter/material.dart';
import 'package:date_field/date_field.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final appTitle = 'Form Validation Demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: Text(appTitle),
),
body: ShowData(),
),
);
}
}
// Create a Form widget.
class MyCustomForm extends StatefulWidget {
#override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
// Create a global key that uniquely identifies the Form widget
// and allows validation of the form.
//
// Note: This is a GlobalKey<FormState>,
// not a GlobalKey<MyCustomFormState>.
final _formKey = GlobalKey<FormState>();
final myController = TextEditingController();
Data stateData = Data();
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Scaffold(
body: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
DateFormField(
initialDatePickerMode: DatePickerMode.day,
enabled: true,
//key:_formKey,
onSaved: (DateTime value) {
stateData.datefield = value;
},
validator: (DateTime value) {
//return stateData.datefield != null ? null : 'enter date';
return value != null ? null : 'enter date';
},
firstDate: DateTime.now().subtract(Duration(days: 180)),
lastDate: DateTime.now().add(Duration(days: 365)),
),
TextFormField(
controller: myController,
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (value) {
stateData.load = value;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: RaisedButton(
onPressed: () {
// Validate returns true if the form is valid, or false
// otherwise.
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
// If the form is valid, display a Snackbar.
Navigator.pop(context, stateData);
// Scaffold.of(context)
// .showSnackBar(SnackBar(content: Text(myController.text)));
// myController.text = 'look at me';
}
},
child: Text('Submit'),
),
),
],
),
),
);
}
}
class Data {
String load;
DateTime datefield;
}
class ShowData extends StatefulWidget {
#override
_ShowDataState createState() => _ShowDataState();
}
class _ShowDataState extends State<ShowData> {
String data = 'start';
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () async {
final holder = await getFormData(context);
print(holder.datefield);
setState(() {
data = holder.load;
});
},
elevation: 4,
),
body: Text(
data,
style: TextStyle(fontSize: 80),
));
}
Future<Data> getFormData(BuildContext context) async {
final answer = await Navigator.push(
context, MaterialPageRoute(builder: (context) => MyCustomForm()));
return (Future.value(answer));
}
}
This is now fixed with the version 0.2.0 of the package!

Multiple widgets used the same globalkey error in flutter

I tried to solve this, I looked up the answers in Stack Overflow
But I haven't solved it yet
I used the global key in the create and update pages
What I've done
I tried adding static to the global key ,but I couldn't
because I couldn't wrap the key in a refreshIndicator.
I used Navigator pushNamed instead of Navigator push
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class Update extends StatefulWidget {
#override
_UpdateState createState() => _UpdateState();
}
class _UpdateState extends State<Update> {
GlobalKey<FormState> _formKey1 = GlobalKey<FormState>(debugLabel: '_updateFormKey');
TextEditingController _titleController1 = TextEditingController();
TextEditingController _descController1 = TextEditingController();
final db = Firestore.instance;
DocumentSnapshot _currentDocument;
#override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
return MaterialApp(
home: Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text('update'),
),
body: _buildUpdate(context)));
}
Widget _buildUpdate(BuildContext context) {
final Size size = MediaQuery.of(context).size;
return StreamBuilder<QuerySnapshot>(
stream: db.collection('flutter_data2').snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: snapshot.data.documents.map<Widget>((doc) {
return Column(
children: <Widget>[
Padding(
padding: EdgeInsets.all(20.0),
child: Card(
elevation: 2.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0)),
child: Form(
key: _formKey1,
child: Padding(
padding: EdgeInsets.only(left: 12, right: 12),
child: Column(
children: <Widget>[
TextFormField(
controller: _titleController1,
decoration: InputDecoration(labelText: doc.data['title']),
validator: (String value) {
if (value.isEmpty) {
return 'title empty';
} else {
return null;
}
},
),
TextFormField(
controller: _descController1,
decoration: InputDecoration(labelText: doc.data['desc']),
validator: (String value) {
if (value.isEmpty) {
return 'desc empty';
} else {
return null;
}
},
),
],
),
),
),
),
),
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
child: Text('update'),
color: Colors.blue,
onPressed: () async {
if (_formKey1.currentState.validate()) {
db
.collection('flutter_data2')
.document(doc.documentID)
.updateData({'title': _titleController1.text,'desc':_descController1.text});
Navigator.pop(context);
}
},
),
],
);
}).toList(),
);
} else {
return SizedBox();
}
},
);
}
}
You might really want to use some modularity here. Create your custom Form widget preferably in a different file with their own set of controllers. This way you won't have to manage the controllers explicitly. One more thing to notice is that your Button is doing the same job for every entry. In this case, you might as well add the global key inside your custom Form widget and hardcode the onPressed function there.
Here is an example
// This is a mock data. Your firebase snapshot.data will have a similar structure
List<Map<String, dynamic>> _mockData = [
{
'title':'Title 1',
'desc':'Description 1',
},
{
'title':'Title 2',
'desc':'Description 2',
},
{
'title':'Title 3',
'desc':'Description 3',
},
{
'title':'Title 4',
'desc':'Description 4',
},
];
// There are many ways to make this work.
// Instead of hardcoding the function in our custom form widget, We would like to pass a function implementation which will be called after the button in the form is pressed. This way we will have more control on what will happen when we press the button
typedef onFormData = Future<void> Function(String, String); // Future void to allow async updates // The two strings are title and description respectively.
// This is the custom form widget you need to create
class MyForm extends StatefulWidget {
final Map<String, dynamic> data; // Replace it with DocumentSnapshot data.
final onFormData onPressed; // We will use the type we defined up there. So we will be expecting a function implementation here which takes two strings, a title and a description
MyForm({#required this.data, #required this.onPressed, Key key}):super(key: key);
#override
createState() => _MyFormState();
}
// Our custom form widget is defined here
class _MyFormState extends State<MyForm> {
// Define the controllers
TextEditingController _titleController;
TextEditingController _descController;
// Create the key
GlobalKey<FormState> _formKey;
#override
void initState() {
// Initialize the values here
super.initState();
_titleController = TextEditingController();
_descController = TextEditingController();
_formKey = GlobalKey<FormState>();
}
#override
void dispose() {
// Remember that you have to dispose of the controllers once the widget is ready to be disposed of
_titleController.dispose();
_descController.dispose();
_formKey = null;
super.dispose();
}
#override
Widget build(BuildContext context) {
// Everything remains almost same here as in your code
return Column(
children: <Widget>[
Padding(
padding: EdgeInsets.all(20.0),
child: Card(
elevation: 2.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0)),
child: Form(
key: _formKey,
child: Padding(
padding: EdgeInsets.only(left: 12, right: 12),
child: Column(
children: <Widget>[
TextFormField(
controller: _titleController, // Assign the controller
decoration:
InputDecoration(labelText: widget.data['title']), // widget.data can still be indexed like this after you replace datatype of the data to DocumentSnapshot
validator: (String value) {
if (value.isEmpty) {
return 'title is empty';
} else {
return null;
}
},
),
TextFormField(
controller: _descController,
decoration:
InputDecoration(labelText: widget.data['desc']), // Same goes here
validator: (String value) {
if (value.isEmpty) {
return 'description is empty';
} else {
return null;
}
},
),
],
),
),
),
),
),
// The button associated with this form
RaisedButton(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
child: Text('Update'),
color: Colors.blue,
onPressed: () async {
// If validation is successful, then call the on pressed function we assigned to the widget. // Check the MyWidget class
if (_formKey.currentState.validate()) {
await widget.onPressed(_titleController.text, _descController.text); // Instead of putting firebase update code here, we are passing the title and description to our main widget from where we will post
}
},
),
],
);
}
}
// Our main widget
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Demo'),
),
// Wrap this up in your stream builder
// I am using a listview with mock data for the sake of this example.
body: ListView.builder(
itemBuilder: (context, index) {
// We create a new instance of our custom form and we don't need to manage any controllers or keys. We just need to pass the data and what happens when we press the update button in our custom form.
// Here is why we defined a type named onFormData before.
// You can simply post updates in your form widget directly if your logic is same for each form
// We are getting the title and description info here through our custom defined Forms without managing any keys and controllers.
// Also this method is async so you can post your firebase updates from here waiting for them to complete using await
return MyForm(data: _mockData[index], onPressed: (String title, String description) async {
// Put your firebase update code here
_mockData[index]['title'] = title;
_mockData[index]['desc'] = description;
Navigator.of(context).pop(); // Go back after the updates are made as written in your example
});
},
physics: BouncingScrollPhysics(),
itemCount: _mockData.length, // Length of the data.
),
);
}
}
Before any updates:
After writing your title and description:
After pressing update, when you go back to the same screen:
Hope this helps!

how to validate a textfieldfom widget of a form in which there are 5 textfieldfom widget ,one by one in flutter

I am trying to validate a text field in the form,and showing alert if it is empty.
My problem is that all the fields are validating at the same time, but I need one field to validate at a time
My current code:
import 'package:flutter/material.dart';
void main(){
runApp(new MaterialApp(
home: new App(),
));
}
class App extends StatefulWidget {
#override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
final GlobalKey<FormState> formkey = GlobalKey<FormState>();
String _name,_last,_add;
var alert;
m(){
if(_name.isEmpty){
showDialog(context: context,
child: new AlertDialog(
title: new Text('name'),
));
}
else if(_last.isEmpty){
showDialog(context: context,
child: new AlertDialog(
title: new Text('last'),
));
}
else {
showDialog(context: context,
child: new AlertDialog(
title: new Text('add'),
));
}
}
pressed() {
var form = formkey.currentState;
if (form.validate()) {
form.save();
}
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(),
body: new Form(
key: formkey,
child: new ListView(
children: <Widget>[
new Text('name'),
new TextFormField(
validator: (str){
return str.isEmpty?m():null;
},
),
new Text('last'),
new TextFormField(
validator: (str){
return str.isEmpty? m():null;
},
),
new Text('add'),
new TextFormField(
validator: (str){
return str.isEmpty?m():null;
},
),
new RaisedButton(onPressed: pressed)
],
)
),
);
}
Several things to point out. First, the validator is expecting a string to be returned so that it can display an error message below the field, but you are calling a method m() that returns nothing. See validator documentation. For example:
new Text("Name"),
new TextFormField(
validator: (str){
if (str.isEmpty) {
return "Please enter a name";
}
},),
If you want to have an alert dialog display instead of using the error text below the field, then don't use the validator in each TextFormField nor the form.validate(). A problem you have for this approach is that you are not capturing any of the TextFormField inputs. To do so, you can either use the onSaved property or assign a GlobalKey to each TextFormField. I'll show the onSaved method:
new TextFormField(
onSaved: (value){
setState(() {
_name = value
});
},),
Now that you have the values set in your state variables, you can change pressed() to something like this:
pressed() {
var form = formkey.currentState;
form.save(); //Calling form.save() in turns calls all TextFormField's onSaved function
var errorMsg = ""
if (_name.isEmpty) {
errorMsg += "Name is empty\n";
}
if (_last.isEmpty) {
errorMsg += "Last is empty\n";
}
if (_add.isEmpty) {
errorMsg += "Add is empty";
}
//Print debug if errorMsg is not empty
if (!errorMsg.isEmpty) (
debugPrint(errorMsg);
}
}