TextFormField cursor did not move when changes occur - flutter

I have a screen that shows a textformfield, I want to retrieve the value that the user has inputted. But when I made a change the cursor didn't move, so it stays at position 0. How do I solve this problem?
class ScreenFormArtikel extends StatefulWidget {
final String mode;
ScreenFormArtikel(this.mode);
#override
_ScreenFormArtikelState createState() => _ScreenFormArtikelState();
}
class _ScreenFormArtikelState extends State<ScreenFormArtikel> {
var _key = GlobalKey<FormState>();
var _titleController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Article'),
),
body: Container(
child: Form(
key: _key,
child: Column(
children: [
Consumer<ProviderArtikel>(
builder: (context, artikel, child) {
return TextFormField(
decoration: InputDecoration(
labelText: 'Title',
floatingLabelBehavior: FloatingLabelBehavior.always),
controller: _titleController,
validator: (value) {
if (value.isEmpty) {
return "Please fill this field";
}
return null;
},
onChanged: (value) {
_titleController.text = value;
},
);
},
),
RaisedButton(onPressed: () {}),
],
),
),
),
);
}
}
I have attached the sample video below
click here

Commenting the onChanged will do the work.
TextFormField(
decoration: InputDecoration(
labelText: 'Title',
floatingLabelBehavior: FloatingLabelBehavior.always),
controller: _titleController,
validator: (value) {
if (value.isEmpty) {
return "Please fill this field";
}
return null;
},
// onChanged: (value) {
// _titleController.text = value;
// },
),
Actually the _titleController.text is setting the text onChange, that's why cursor is moving to the first place.
And you don't have to explicitly do the onTextChange thing.

Related

How to validate the TextFormField as we type in the input in Flutter

I have created a login screen with textformfield for email id and password using flutter. Also, I have added the validation to check these fields. The code is as below;
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
theme: ThemeData(
brightness: Brightness.dark,
),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
var _formKey = GlobalKey<FormState>();
var isLoading = false;
void _submit() {
final isValid = _formKey.currentState.validate();
if (!isValid) {
return;
}
_formKey.currentState.save();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Form Validation"),
leading: Icon(Icons.filter_vintage),
),
//body
body: Padding(
padding: const EdgeInsets.all(16.0),
//form
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
Text(
"Form-Validation In Flutter ",
style: TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold),
),
//styling
SizedBox(
height: MediaQuery.of(context).size.width * 0.1,
),
TextFormField(
decoration: InputDecoration(labelText: 'E-Mail'),
keyboardType: TextInputType.emailAddress,
onFieldSubmitted: (value) {
//Validator
},
validator: (value) {
if (value.isEmpty ||
!RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+#[a-zA-Z0-9]+\.[a-zA-Z]+")
.hasMatch(value)) {
return 'Enter a valid email!';
}
return null;
},
),
//box styling
SizedBox(
height: MediaQuery.of(context).size.width * 0.1,
),
//text input
TextFormField(
decoration: InputDecoration(labelText: 'Password'),
keyboardType: TextInputType.emailAddress,
onFieldSubmitted: (value) {},
obscureText: true,
validator: (value) {
if (value.isEmpty) {
return 'Enter a valid password!';
}
return null;
},
),
SizedBox(
height: MediaQuery.of(context).size.width * 0.1,
),
RaisedButton(
padding: EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 15.0,
),
child: Text(
"Submit",
style: TextStyle(
fontSize: 24.0,
),
),
onPressed: () => _submit(),
)
],
),
),
),
);
}
}
The issue I am facing is, I want to validate the fields as soon as the user starts typing the input(dynamically) rather than clicking on the submit button to wait for the validation to happen. I did a lot of research yet could not find a solution. Thanks in advance for any help!
Flutter Form Validation with TextFormField
Here's an alternative implementation of the _TextSubmitWidgetState that uses a Form:
class _TextSubmitWidgetState extends State<TextSubmitForm> {
// declare a GlobalKey
final _formKey = GlobalKey<FormState>();
// declare a variable to keep track of the input text
String _name = '';
void _submit() {
// validate all the form fields
if (_formKey.currentState!.validate()) {
// on success, notify the parent widget
widget.onSubmit(_name);
}
}
#override
Widget build(BuildContext context) {
// build a Form widget using the _formKey created above.
return Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(
decoration: const InputDecoration(
labelText: 'Enter your name',
),
// use the validator to return an error string (or null) based on the input text
validator: (text) {
if (text == null || text.isEmpty) {
return 'Can\'t be empty';
}
if (text.length < 4) {
return 'Too short';
}
return null;
},
// update the state variable when the text changes
onChanged: (text) => setState(() => _name = text),
),
ElevatedButton(
// only enable the button if the text is not empty
onPressed: _name.isNotEmpty ? _submit : null,
child: Text(
'Submit',
style: Theme.of(context).textTheme.headline6,
),
),
],
),
);
}
}
source : https://codewithandrea.com/articles/flutter-text-field-form-validation/
May be this can help someone. Inside the TextFormField use this line of code:
autovalidateMode: AutovalidateMode.onUserInteraction
use autovalidateMode in your Form widget
Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: FormUI(),
),

Clear textfield based onchanged (flutter)

TextField(
inputFormatters: [
new FilteringTextInputFormatter.allow(
RegExp('[0-9]')),
],
hintText: 'some text'
title: '',
editingController: controllers[31],
value: somenumber,
onChange: (value) {
if (num.parse(value) <= 3000 &&
num.parse(value) >= 30) {
// save some data
}else{
controllers[31].clear(),
},
),
so above is a textfield with onchange, right now the textfield does not clear if i put outside of the range, is it possible to clear the textfield based onchange?
Is the TextField() you are using part of a plugin? The properties look a bit odd compared with the latest stable release of Flutter.
Here's an example that clears the text:
TextEditingController textEditingController = new TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: TextField(
inputFormatters: [
new FilteringTextInputFormatter.allow(RegExp('[0-9]')),
],
decoration: InputDecoration(
hintText: 'some text',
),
controller: textEditingController,
onChanged: (value) {
if (value.length <= 10) {
// something
} else {
textEditingController.clear();
}
}
),
),
);
}
Create TextEditingController for your text field and then assign it to the controller property of TextField.
// create controller
TextEditingController _controller = new TextEditingController();
// assign it to TextField controller property
TextField(
controller : _controller
// your other properties
)
then to clear the text
// clear text
_controller.clear();
``
U can also do this :
TextEditingController textEditingController = new TextEditingController();
Function onchange;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: TextField(
inputFormatters: [
new FilteringTextInputFormatter.allow(RegExp('[0-9]')),
],
decoration: InputDecoration(
hintText: 'some text',
suffixIcon: IconButton(
onPressed: () {
widget.onchanged('');
controller.clear();
},
),
controller: textEditingController,
onChanged: (value) => onchange(value)
}
),
),
);
}

How to pass the data from the form to the next screen in flutter?

I'm building an application that has a pretty long form with a lot of text fields, so I divided the text fields into multiple screens. It got three screens, the first screen has some common text fields such as phone, website, email, etc. The second screen has some more text fields and the same with the third screen. I'm trying to display all the details from the three forms at the end on a different screen.
I want to display all the details in the end when I hit the 'Done' button on a different page.
Here's the code for the first screen which has the first form:-
import 'package:flutter/material.dart';
import 'package:instaskool/model.dart';
import 'package:validators/validators.dart' as validator;
import 'package:instaskool/home_screens/homescreen_student.dart';
import 'package:instaskool/home_screens/homescreen_school.dart';
import 'package:instaskool/screens/school_signup_two.dart';
class SchoolReg extends StatefulWidget {
#override
_SchoolRegState createState() => _SchoolRegState();
}
class _SchoolRegState extends State<SchoolReg> {
final _formKey = GlobalKey<FormState>();
School school = School();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: new Form(
key: _formKey,
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 130),
alignment: Alignment.topCenter,
child: MyTextFormField(
hintText: 'School name',
validator: (String value) {
if (value.isEmpty) {
return 'Enter your school name';
}
return null;
},
onSaved: (String value) {
school.schoolName = value;
},
),
),
MyTextFormField(
hintText: 'Phone',
validator: (String value) {
if (value.isEmpty) {
return 'Enter the phone number';
}
return null;
},
onSaved: (String value) {
school.schoolPhone = value;
},
),
MyTextFormField(
hintText: 'Email',
validator: (String value) {
if (value.isEmpty) {
return 'Enter the email address';
}
return null;
},
onSaved: (String value) {
school.schoolEmail = value;
},
),
MyTextFormField(
hintText: 'School Website',
isEmail: true,
validator: (String value) {
if (value.isEmpty) {
return "Enter the school's website";
}
return null;
},
onSaved: (String value) {
school.schoolWebsite = value;
},
),
RaisedButton(
color: Colors.blueAccent,
onPressed: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SchoolRegTwo()));
}
},
child: Text(
'Next',
style: TextStyle(
color: Colors.white,
),
),
)
],
),
),
),
);
}
}
class MyTextFormField extends StatelessWidget {
final String hintText;
final Function validator;
final Function onSaved;
final bool isPassword;
final bool isEmail;
MyTextFormField({
this.hintText,
this.validator,
this.onSaved,
this.isPassword = false,
this.isEmail = false,
});
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
decoration: InputDecoration(
hintText: hintText,
contentPadding: EdgeInsets.all(15.0),
border: InputBorder.none,
filled: true,
fillColor: Colors.grey[200],
),
obscureText: isPassword ? true : false,
validator: validator,
onSaved: onSaved,
keyboardType: isEmail ? TextInputType.emailAddress : TextInputType.text,
),
);
}
}
Here's the code for the second form:-
import 'package:flutter/material.dart';
import 'package:instaskool/model.dart';
import 'package:validators/validators.dart' as validator;
import 'package:instaskool/home_screens/homescreen_student.dart';
import 'package:instaskool/home_screens/homescreen_school.dart';
import 'package:instaskool/screens/school_signup_three.dart';
class SchoolRegTwo extends StatefulWidget {
#override
_SchoolRegTwoState createState() => _SchoolRegTwoState();
}
class _SchoolRegTwoState extends State<SchoolRegTwo> {
final _formKey = GlobalKey<FormState>();
SchoolDet schooldet = SchoolDet();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: new Form(
key: _formKey,
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 130),
alignment: Alignment.topCenter,
child: MyTextFormField(
hintText: 'School address 1',
validator: (String value) {
if (value.isEmpty) {
return "Enter your school's address";
}
return null;
},
onSaved: (String value) {
schooldet.addressOne = value;
},
),
),
MyTextFormField(
hintText: 'School address 2',
validator: (String value) {
if (value.isEmpty) {
return "Enter the school's address";
}
return null;
},
onSaved: (String value) {
schooldet.addressTwo = value;
},
),
MyTextFormField(
hintText: 'City',
validator: (String value) {
if (value.isEmpty) {
return 'Enter the city';
}
return null;
},
onSaved: (String value) {
schooldet.city = value;
},
),
MyTextFormField(
hintText: 'Pincode',
isEmail: true,
validator: (String value) {
if (value.isEmpty) {
return "Enter the pincode";
}
return null;
},
onSaved: (String value) {
schooldet.pincode = value;
},
),
MyTextFormField(
hintText: 'State',
isEmail: true,
validator: (String value) {
if (value.isEmpty) {
return "Enter the state";
}
return null;
},
onSaved: (String value) {
schooldet.state = value;
},
),
RaisedButton(
color: Colors.blueAccent,
onPressed: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SchoolRegThree(schooldet: this.schooldet)));
}
},
child: Text(
'Next',
style: TextStyle(
color: Colors.white,
),
),
)
],
),
),
),
);
}
}
class MyTextFormField extends StatelessWidget {
final String hintText;
final Function validator;
final Function onSaved;
final bool isPassword;
final bool isEmail;
MyTextFormField({
this.hintText,
this.validator,
this.onSaved,
this.isPassword = false,
this.isEmail = false,
});
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
decoration: InputDecoration(
hintText: hintText,
contentPadding: EdgeInsets.all(15.0),
border: InputBorder.none,
filled: true,
fillColor: Colors.grey[200],
),
obscureText: isPassword ? true : false,
validator: validator,
onSaved: onSaved,
keyboardType: isEmail ? TextInputType.emailAddress : TextInputType.text,
),
);
}
}
Here's the code for the third form:-
import 'package:flutter/material.dart';
import 'package:instaskool/model.dart';
import 'package:validators/validators.dart' as validator;
import 'package:instaskool/home_screens/homescreen_student.dart';
import 'package:instaskool/home_screens/homescreen_school.dart';
import 'package:instaskool/screens/school_signup_three.dart';
import 'package:instaskool/screens/school_code.dart';
class SchoolRegThree extends StatefulWidget {
School school;
SchoolRegThree({this.school, SchoolDet schooldet});
#override
_SchoolRegThreeState createState() => _SchoolRegThreeState();
}
class _SchoolRegThreeState extends State<SchoolRegThree> {
final _formKey = GlobalKey<FormState>();
SchoolUser schooluser = SchoolUser();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: new Form(
key: _formKey,
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 100),
child: MyTextFormField(
hintText: 'Username',
isPassword: true,
validator: (String value) {
if (value.length < 5) {
return 'Username should be at least 5 characters long';
}
_formKey.currentState.save();
return null;
},
onSaved: (String value) {
schooluser.username = value;
},
),
),
MyTextFormField(
hintText: 'New Password',
isPassword: true,
validator: (String value) {
if (value.length < 7) {
return 'Password should be at least 7 characters long';
} else if (schooluser.password != null) {
print(value);
print(schooluser.password);
}
return null;
},
onSaved: (String value) {
schooluser.password = value;
},
),
RaisedButton(
color: Colors.blueAccent,
onPressed: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ResultSchool(schooluser: this.schooluser)));
}
},
child: Text(
'Done',
style: TextStyle(
color: Colors.white,
),
),
)
],
),
),
),
);
}
}
class MyTextFormField extends StatelessWidget {
final String hintText;
final Function validator;
final Function onSaved;
final bool isPassword;
final bool isEmail;
MyTextFormField({
this.hintText,
this.validator,
this.onSaved,
this.isPassword = false,
this.isEmail = false,
});
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
decoration: InputDecoration(
hintText: hintText,
contentPadding: EdgeInsets.all(15.0),
border: InputBorder.none,
filled: true,
fillColor: Colors.grey[200],
),
obscureText: isPassword ? true : false,
validator: validator,
onSaved: onSaved,
keyboardType: isEmail ? TextInputType.emailAddress : TextInputType.text,
),
);
}
}
Here's the model.dart which has all the variables:-
import 'package:flutter/material.dart';
import 'package:instaskool/screens/school_code.dart';
class Model {
String fullname;
String code;
String standard;
String section;
String username;
String password;
Model({this.fullname, this.code, this.standard, this.section, this.username, this.password});
}
class School {
String schoolName;
String schoolPhone;
String schoolEmail;
String schoolWebsite;
School({this.schoolName, this.schoolPhone, this.schoolEmail, this.schoolWebsite});
}
class SchoolDet {
String addressOne;
String addressTwo;
String city;
String pincode;
String state;
SchoolDet({this.addressOne, this.addressTwo, this.city, this.pincode, this.state});
}
class SchoolUser{
String username;
String password;
SchoolUser({this.username, this.password});
}
class SchoolCode{
String principalCode;
String teacherCode;
String studentCode;
SchoolCode({this.principalCode, this.teacherCode, this.studentCode});
}
Here's the result screen where I wanna display all the data:-
import 'package:flutter/material.dart';
import 'package:instaskool/model.dart';
class ResultSchool extends StatelessWidget {
School school;
SchoolDet schooldet;
SchoolCode schoolcode;
SchoolUser schooluser;
ResultSchool({this.school, this.schooldet, this.schooluser});
#override
Widget build(BuildContext context) {
return (Scaffold(
appBar: AppBar(title: Text('School details')),
body: Container(
margin: EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(school.schoolName, style: TextStyle(fontSize: 22)),
Text(school.schoolPhone, style: TextStyle(fontSize: 22)),
Text(school.schoolEmail, style: TextStyle(fontSize: 22)),
Text(school.schoolWebsite, style: TextStyle(fontSize: 22)),
Text(schooldet.addressOne, style: TextStyle(fontSize: 22)),
Text(schooldet.addressTwo, style: TextStyle(fontSize: 22)),
Text(schooldet.city, style: TextStyle(fontSize: 22)),
Text(schooldet.pincode, style: TextStyle(fontSize: 22)),
Text(schooldet.state, style: TextStyle(fontSize: 22)),
Text(schooluser.username, style: TextStyle(fontSize: 22)),
Text(schooluser.password, style: TextStyle(fontSize: 22)),
Text(schoolcode.teacherCode, style: TextStyle(fontSize: 22)),
Text(schoolcode.principalCode, style: TextStyle(fontSize: 22)),
],
),
),
));
}
}
Add a widget to manage form transitions
enum SchoolFormPhase { BASIC_DTL, ADDRESS, USER_DTL }
class SchoolRegistration extends StatefulWidget {
#override
_SchoolRegistrationState createState() => _SchoolRegistrationState();
}
class _SchoolRegistrationState extends State<SchoolRegistration> {
SchoolFormPhase phase;
School schoolForm;
#override
void initState() {
phase = SchoolFormPhase.BASIC_DTL;
schoolForm = School();
super.initState();
}
#override
Widget build(BuildContext context) {
switch (phase) {
case SchoolFormPhase.BASIC_DTL:
return SchoolReg(
school: schoolForm,
onSaved: (school) {
setState(() {
schoolForm = school;
phase = SchoolFormPhase.ADDRESS;
});
});
case SchoolFormPhase.ADDRESS:
return SchoolRegTwo(
school: schoolForm,
onSaved: (school) {
setState(() {
schoolForm = school;
phase = SchoolFormPhase.USER_DTL;
});
});
case SchoolFormPhase.USER_DTL:
return SchoolRegThree(
school: schoolForm,
onSaved: (school) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ResultSchool(
schooluser: school.user,
school: school,
schooldet: school.address)));
},
);
}
return Container();
}
}
change the Form widget to accept inputs
class SchoolReg extends StatefulWidget {
final School school;
final Function(School) onSaved;
const SchoolReg({Key key, this.school, this.onSaved}) : super(key: key);
#override
_SchoolRegState createState() => _SchoolRegState();
}
class _SchoolRegState extends State<SchoolReg> {
final _formKey = GlobalKey<FormState>();
School _school;
#override
void initState() {
_school = widget.school;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: new Form(
key: _formKey,
child: Column(children: <Widget>[
Container(
margin: EdgeInsets.only(top: 130),
alignment: Alignment.topCenter,
child: MyTextFormField(
hintText: 'School name',
validator: (String value) {
return value.isEmpty
? 'Enter your school name'
: null;
},
onSaved: (value) => _school.schoolName = value)),
MyTextFormField(
hintText: 'Phone',
validator: (String value) {
if (value.isEmpty) {
return 'Enter the phone number';
}
return null;
},
onSaved: (String value) {
_school.schoolPhone = value;
},
),
MyTextFormField(
hintText: 'Email',
validator: (String value) {
if (value.isEmpty) {
return 'Enter the email address';
}
return null;
},
onSaved: (String value) {
_school.schoolEmail = value;
},
),
MyTextFormField(
hintText: 'School Website',
isEmail: true,
validator: (String value) {
if (value.isEmpty) {
return "Enter the school's website";
}
return null;
},
onSaved: (String value) {
_school.schoolWebsite = value;
},
),
RaisedButton(
color: Colors.blueAccent,
onPressed: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
widget.onSaved(_school);
}
},
child:
Text('Next', style: TextStyle(color: Colors.white)))
]))));
}
}
form 2
class SchoolRegTwo extends StatefulWidget {
final School school;
final Function(School) onSaved;
const SchoolRegTwo({Key key, this.school, this.onSaved}) : super(key: key);
#override
_SchoolRegTwoState createState() => _SchoolRegTwoState();
}
class _SchoolRegTwoState extends State<SchoolRegTwo> {
final _formKey = GlobalKey<FormState>();
SchoolDet schooldet;
#override
void initState() {
schooldet = widget.school.address;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: new Form(
key: _formKey,
child: Column(children: <Widget>[
Container(
margin: EdgeInsets.only(top: 130),
alignment: Alignment.topCenter,
child: MyTextFormField(
hintText: 'School address 1',
validator: (String value) {
if (value.isEmpty) {
return "Enter your school's address";
}
return null;
},
onSaved: (String value) {
schooldet.addressOne = value;
},
),
),
MyTextFormField(
hintText: 'School address 2',
validator: (String value) {
if (value.isEmpty) {
return "Enter the school's address";
}
return null;
},
onSaved: (String value) {
schooldet.addressTwo = value;
},
),
MyTextFormField(
hintText: 'City',
validator: (String value) {
if (value.isEmpty) {
return 'Enter the city';
}
return null;
},
onSaved: (String value) {
schooldet.city = value;
},
),
MyTextFormField(
hintText: 'Pincode',
isEmail: true,
validator: (String value) {
if (value.isEmpty) {
return "Enter the pincode";
}
return null;
},
onSaved: (String value) {
schooldet.pincode = value;
},
),
MyTextFormField(
hintText: 'State',
isEmail: true,
validator: (String value) {
if (value.isEmpty) {
return "Enter the state";
}
return null;
},
onSaved: (String value) {
schooldet.state = value;
},
),
RaisedButton(
color: Colors.blueAccent,
onPressed: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
widget.school.address = schooldet;
widget.onSaved(widget.school);
}
},
child:
Text('Next', style: TextStyle(color: Colors.white)))
]))));
}
}
form 3
class SchoolRegThree extends StatefulWidget {
School school;
final Function(School) onSaved;
SchoolRegThree({this.school, this.onSaved});
#override
_SchoolRegThreeState createState() => _SchoolRegThreeState();
}
class _SchoolRegThreeState extends State<SchoolRegThree> {
final _formKey = GlobalKey<FormState>();
SchoolUser schooluser;
#override
void initState() {
schooluser = widget.school.user;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: new Form(
key: _formKey,
child: Column(children: <Widget>[
Container(
margin: EdgeInsets.only(top: 100),
child: MyTextFormField(
hintText: 'Username',
isPassword: true,
validator: (String value) {
if (value.length < 5) {
return 'Username should be at least 5 characters long';
}
_formKey.currentState.save();
return null;
},
onSaved: (String value) {
schooluser.username = value;
},
),
),
MyTextFormField(
hintText: 'New Password',
isPassword: true,
validator: (String value) {
if (value.length < 7) {
return 'Password should be at least 7 characters long';
} else if (schooluser.password != null) {
print(value);
print(schooluser.password);
}
return null;
},
onSaved: (String value) {
schooluser.password = value;
},
),
RaisedButton(
color: Colors.blueAccent,
onPressed: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
widget.school.user = schooluser;
widget.onSaved(widget.school);
}
},
child: Text('Done',
style: TextStyle(
color: Colors.white,
)))
]))));
}
}
and finally consolidate the model to a single model class
class School {
String schoolName;
String schoolPhone;
String schoolEmail;
String schoolWebsite;
SchoolDet address;
SchoolUser user;
}
class SchoolDet {
String addressOne;
String addressTwo;
String city;
String pincode;
String state;
}
class SchoolUser {
String username;
String password;
}
Best is to use a state management solution like flutter_bloc, provider, etc.
Store it in an array on a different file using state management.
Make the bloc/provider available to all the screens.
You can pass through constructor but I don't recommend that as it will be too messy.

Flutter provider, Right way to use GlobalKey<FormState> in Provider

I'm new at Provider package. and Just making demo app for learning purpose.
Here is my code of simple Form Widget.
1) RegistrationPage (Where my app is start)
class RegistrationPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("Title"),
),
body: MultiProvider(providers: [
ChangeNotifierProvider<UserProfileProvider>.value(value: UserProfileProvider()),
ChangeNotifierProvider<RegiFormProvider>.value(value: RegiFormProvider()),
], child: AllRegistrationWidgets()),
);
}
}
class AllRegistrationWidgets extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
SetProfilePicWidget(),
RegistrationForm(),
],
),
),
),
BottomSaveButtonWidget()
],
),
),
);
}
}
class BottomSaveButtonWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
final _userPicProvider =
Provider.of<UserProfileProvider>(context, listen: false);
final _formProvider =
Provider.of<RegiFormProvider>(context, listen: false);
return SafeArea(
bottom: true,
child: Container(
margin: EdgeInsets.all(15),
child: FloatingActionButton.extended(
heroTag: 'saveform',
icon: null,
label: Text('SUBMIT',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
)),
onPressed: () {
print(_userPicProvider.strImageFileName);
_formProvider.globalFormKey.currentState.validate();
print(_formProvider.firstName);
print(_formProvider.lastName);
},
)),
);
}
}
2) RegistrationForm
class RegistrationForm extends StatefulWidget {
#override
_RegistrationFormState createState() => _RegistrationFormState();
}
class _RegistrationFormState extends State<RegistrationForm> {
TextEditingController _editingControllerFname;
TextEditingController _editingControllerLname;
#override
void initState() {
_editingControllerFname = TextEditingController();
_editingControllerLname = TextEditingController();
super.initState();
}
#override
Widget build(BuildContext context) {
final formProvider = Provider.of<RegiFormProvider>(context);
return _setupOtherWidget(formProvider);
}
_setupOtherWidget(RegiFormProvider _formProvider) {
return Container(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
SizedBox(height: 20),
Text(
'Fields with (*) are required.',
style: TextStyle(fontStyle: FontStyle.italic),
textAlign: TextAlign.left,
),
SizedBox(height: 20),
_formSetup(_formProvider)
],
),
);
}
_formSetup(RegiFormProvider _formProvider) {
return Form(
key: _formProvider.globalFormKey,
child: Container(
child: Column(
children: <Widget>[
TextFormField(
controller: _editingControllerFname,
textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration(
labelText: "First Name *",
hintText: "First Name *",
),
onSaved: (value) {},
validator: (String value) =>
_formProvider.validateFirstName(value)),
SizedBox(height: 15),
TextFormField(
controller: _editingControllerLname,
textCapitalization: TextCapitalization.sentences,
validator: (String value) =>
_formProvider.validateLastName(value),
onSaved: (value) {},
decoration: InputDecoration(
labelText: "Last Name *",
hintText: "Last Name *",
),
)
],
),
),
);
}
#override
void dispose() {
_editingControllerFname.dispose();
_editingControllerLname.dispose();
super.dispose();
}
}
3) RegiFormProvider
class RegiFormProvider with ChangeNotifier {
final GlobalKey<FormState> globalFormKey = GlobalKey<FormState>();
String _strFirstName;
String _strLasttName;
String get firstName => _strFirstName;
String get lastName => _strLasttName;
String validateFirstName(String value) {
if (value.trim().length == 0)
return 'Please enter first name';
else {
_strFirstName = value;
return null;
}
}
String validateLastName(String value) {
if (value.trim().length == 0)
return 'Please enter last name';
else {
_strLasttName = value;
return null;
}
}
}
Here you can see, RegiFormProvider is my first page where other is children widgets in widget tree. I'm using final GlobalKey<FormState> globalFormKey = GlobalKey<FormState>(); in the RegiFormProvider provider, Because I want to access this in the 1st RegistrationPage to check my firstName and lastName is valid or not.
I'm using a builder widget to get form level context like below , and then easily we can get the form instance by using that context. by this way we don't need global key anymore.
Form(
child: Builder(
builder: (ctx) {
return ListView(
padding: EdgeInsets.all(12),
children: <Widget>[
TextFormField(
decoration: InputDecoration(labelText: "Title"),
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) => FocusScope.of(context).nextFocus(),
initialValue: formProduct.title,
validator: validateTitle,
onSaved: (value) {
formProduct.title = value;
},
),
TextFormField(
decoration: InputDecoration(labelText: "Price"),
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) => FocusScope.of(context).nextFocus(),
initialValue: formProduct.price == null
? ""
: formProduct.price.toString(),
keyboardType: TextInputType.number,
validator: validatePrice,
onSaved: (value) {
formProduct.price = double.parse(value);
},
),
TextFormField(
decoration: InputDecoration(labelText: "Description"),
textInputAction: TextInputAction.next,
initialValue: formProduct.description,
maxLines: 3,
validator: validateDescription,
onFieldSubmitted: (_) => FocusScope.of(context).nextFocus(),
onSaved: (value) {
formProduct.description = value;
},
),
TextFormField(
decoration: InputDecoration(labelText: "Image Url"),
textInputAction: TextInputAction.done,
onFieldSubmitted: (_) => FocusScope.of(context).unfocus(),
initialValue: formProduct.imageUrl,
validator: validateImageUrl,
onSaved: (value) {
formProduct.imageUrl = value;
},
),
Padding(
padding: EdgeInsets.all(10),
child: FlatButton(
color: Colors.amberAccent,
onPressed: () {
if (Form.of(ctx).validate()) {
Form.of(ctx).save();
formProduct.id =
Random.secure().nextDouble().toString();
ProductsProvider provider =
Provider.of<ProductsProvider>(context,
listen: false);
editing
? provider.setProduct(formProduct)
: provider.addProduct(formProduct);
Router.back(context);
}
},
child: Text("Save"),
),
)
],
);
},
),
)
you can see the Form.of(ctx) gives us the current level form.

Flutter redirects on textFormField open

whenever i click on the textFormField the keyboard opens and closes almost immediately i think it kinda refreshes the page. i have another page with a form where i dont face this problem.
here is the code for that page, the other form i have in the app is almost identical to this one
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import '../scoped_models/products.dart';
class ProductAdd extends StatelessWidget {
final _formData = {
'title': null,
'description': null,
'price': null,
'image': 'assets/food.jpg'
};
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Widget _titleField() {
return TextFormField(
decoration: InputDecoration(labelText: 'Enter Title'),
keyboardType: TextInputType.text,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter a valid title';
}
},
onSaved: (value) {
print(value);
_formData['title'] = value;
},
);
}
Widget _descriptionField() {
return TextFormField(
decoration: InputDecoration(labelText: 'Enter Description'),
keyboardType: TextInputType.text,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter a valid description';
}
},
onSaved: (value) {
print(value);
_formData['description'] = value;
},
);
}
Widget _priceField() {
return TextFormField(
decoration: InputDecoration(labelText: 'Enter Price'),
keyboardType: TextInputType.number,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter a valid price';
}
},
onSaved: (value) {
print(value);
_formData['price'] = value;
},
);
}
Widget _submitButton(context) {
return RaisedButton(
textColor: Colors.white,
child: Text('LOGIN'),
onPressed: () {
if (!_formKey.currentState.validate()) {
return;
}
_formKey.currentState.save();
Navigator.pushReplacementNamed(context, '/products');
},
);
}
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<ProductsModel>(
builder: (context, child, ProductsModel model) {
return Container(
child: Center(
child: Container(
child: SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(20.0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
_titleField(),
_descriptionField(),
_priceField(),
SizedBox(
height: 10.0,
),
_submitButton(context)
],
),
),
),
),
),
),
);
},
);
}
}
i'm using flutter version: 1.0.0
I was not able to reproduce the issue. I re-created your case and the keyboard worked just fine. I although skipped the scoped-model part of your code because I don't know how your setup is. But with minimal code, I couldn't replicate it. See below:
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
//import '../scoped_models/products.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: ProductAdd(),
);
}
}
class ProductAdd extends StatelessWidget {
final _formData = {
'title': null,
'description': null,
'price': null,
'image': 'assets/food.jpg'
};
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Widget _titleField() {
return TextFormField(
decoration: InputDecoration(labelText: 'Enter Title'),
keyboardType: TextInputType.text,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter a valid title';
}
},
onSaved: (value) {
print(value);
_formData['title'] = value;
},
);
}
Widget _descriptionField() {
return TextFormField(
decoration: InputDecoration(labelText: 'Enter Description'),
keyboardType: TextInputType.text,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter a valid description';
}
},
onSaved: (value) {
print(value);
_formData['description'] = value;
},
);
}
Widget _priceField() {
return TextFormField(
decoration: InputDecoration(labelText: 'Enter Price'),
keyboardType: TextInputType.number,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter a valid price';
}
},
onSaved: (value) {
print(value);
_formData['price'] = value;
},
);
}
Widget _submitButton(context) {
return RaisedButton(
textColor: Colors.white,
child: Text('LOGIN'),
onPressed: () {
if (!_formKey.currentState.validate()) {
return;
}
_formKey.currentState.save();
Navigator.pushReplacementNamed(context, '/products');
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
child: SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(20.0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
_titleField(),
_descriptionField(),
_priceField(),
SizedBox(
height: 10.0,
),
_submitButton(context)
],
),
),
),
),
),
),
);
}
}
My Flutter version: 0.11.10
this is my correction, hope it works; u need to add TextEditingController titlecontroller froeach TextFormField,dont use onsaved() ; and on the submitbutton funtion use this :
TextEditingController _pricecontroller;
Widget _priceField() {
return TextFormField(
//addcontroller;
controller : __pricecontroller
decoration: InputDecoration(labelText: 'Enter Price'),
keyboardType: TextInputType.number,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter a valid price';
}
},
onSaved: (value) {
print(value);
_formData['price'] = value;
},
);
}
Widget _submitButton(context) {
return RaisedButton(
textColor: Colors.white,
child: Text('LOGIN'),
onPressed: () {
if (!_formKey.currentState.validate()) {
_formKey.currentState.save();
_formData['title'] = __titlecontroller.text;
_formData['description'] = __desccontroller.text;
_formData['price'] = __pricecontroller.text;
}
Navigator.pushReplacementNamed(context, '/products');
},
);
}