Related
I have form to grab info from User. But when I tap on TextFormField and keyboard shows it disappears.
https://youtu.be/UhVL2hqWOlQ
Here is my code:
class _AddInfoState extends State<AddInfo>{
static final GlobalKey<FormState> _key = new GlobalKey();
bool _validate = false;
String strTitle;
File fileImage;
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body:new Container(
child: new Form(
key: _key,
autovalidate: _validate,
child: formUI(),
),
),
);
}
Widget formUI(){
return new ListView(
children: <Widget>[
//Title Field
new Container(
padding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 0.0),
child:new TextFormField(
style: styleTextField,
keyboardType: TextInputType.text,
autofocus: true,
maxLength: 45,
decoration: InputDecoration(
hintText: 'Enter Title',
prefixIcon: Icon(
Icons.title,
size: 30.0,
color: blueColor,
)
),
validator: validateTitle,
onSaved: (String val) {
strTitle = val;
},
),
),
],
physics: new ClampingScrollPhysics(),
);
}
By my understanding from the video, since you have used a scrollable ListView inside your form, the list is scrolling upward once the keyboard appears hence it disappears.
Try setting resizeToAvoidBottomPadding: false in your Scaffold. This will make the keyboard appear as an overlay and won't push the content upward.
Example:
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
resizeToAvoidBottomPadding: false,
body:new Container(
child: new Form(
key: _key,
autovalidate: _validate,
child: formUI(),
),
),
);
}
Let me know if this helps!
Wrap the Container which has the TextFormField in a SizedBox :
new Form(
autovalidate: false,
key: _formKey,
child: new Container(
height: orientation == Orientation.portrait ? MediaQuery.of(context).size.height : MediaQuery.of(context).size.width/1.2 ,
padding: EdgeInsets.only(left: 10.0, right: 10.0),
child: new Column(
//mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
padding: EdgeInsets.only(top: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new SizedBox(
width: MediaQuery.of(context).size.width,
height: 100.0,
child: new Container(
padding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 0.0),
child:new TextFormField(
style: styleTextField,
keyboardType: TextInputType.text,
autofocus: true,
maxLength: 45,
decoration: InputDecoration(
hintText: 'Enter Title',
prefixIcon: Icon(
Icons.title,
size: 30.0,
color: blueColor,
)
),
validator: validateTitle,
onSaved: (String val) {
strTitle = val;
},
),
),
)
]
)
)
]
)
)
)
Set the resizeToAvoidBottomPadding: false and then change the Column to a ListView
Im creating a login form in flutter and I want to use snackbar to show a message when login fails. I read this documentation and if i have this code should works
final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));
Scaffold.of(context).showSnackBar(snackBar);
But return this error
Scaffold.of() called with a context that does not contain a Scaffold.
My login.dart all code
import 'package:flutter/material.dart';
import 'package:fluttercrud/screens/home_page.dart';
class LoginPage extends StatelessWidget {
static String tag = 'login-page';
#override
Widget build(BuildContext context) {
final logo = Hero(
tag: 'hero',
child: CircleAvatar(
backgroundColor: Colors.transparent,
radius: 48.0,
child: Image.asset('assets/logo.png'),
),
);
final email = TextFormField(
keyboardType: TextInputType.emailAddress,
autofocus: false,
decoration: InputDecoration(
hintText: 'Usuario',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(32.0)),
),
);
final password = TextFormField(
autofocus: false,
obscureText: true,
decoration: InputDecoration(
hintText: 'Contraseña',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(32.0)),
),
);
final loginButton = Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: Material(
borderRadius: BorderRadius.circular(30.0),
child: MaterialButton(
minWidth: 200.0,
height: 42.0,
onPressed: () {
final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));
Scaffold.of(context).showSnackBar(snackBar);
//Navigator.of(context).pushNamed(HomePage.tag);
},
color: Colors.blue[300],
child: Text('Entrar', style: TextStyle(color: Colors.white)),
),
),
);
final forgotLabel = FlatButton(
child: Text(
'¿Contraseña olvidada?',
style: TextStyle(color: Colors.black),
),
onPressed: () {},
);
return Scaffold(
backgroundColor: Colors.white,
body: Center(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0),
children: <Widget>[
logo,
SizedBox(height: 48.0),
email,
SizedBox(height: 8.0),
password,
SizedBox(height: 24.0),
loginButton,
forgotLabel
],
),
),
);
}
}
The scaffold return a error but i don´t know how can i fix this without rewrite all code.
So the question is: How can i to show the snackbar when login fails and avoid this error? And why this error appears?
UPDATED
void initState() {
super.initState();
final snackBar = SnackBar(
content: Text(
'Usuario/Contraseña incorrecto',
textAlign: TextAlign.center,
));
Scaffold.of(context).showSnackBar(snackBar);
seriesList = _createSampleData();
animate = false;
}
And how can i show snackbar when init page?
This is because you are using the context of the widget that creates the Scaffold (the parent context), not the context of the Scaffold itself. Thus the error.
You can fix the error either by creating a method builder that will receive the correct context:
Widget _buildLoginButton(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: Material(
borderRadius: BorderRadius.circular(30.0),
child: MaterialButton(
minWidth: 200.0,
height: 42.0,
onPressed: () {
final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));
Scaffold.of(context).showSnackBar(snackBar);
},
color: Colors.blue[300],
child: Text('Entrar', style: TextStyle(color: Colors.white)),
),
),
);
}
And refactor the page to use the builder method you've just created:
Scaffold(
appBar: AppBar(
title: Text('My Page'),
),
body: Builder(
builder: (context) =>
Column(
children: [
.....
_buildLoginButton(context),
.....
]
),
),
),
);
Or just extract the login button to its own Widget, without changing any other of your code, and it will receive the proper context.
class LoginButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: Material(
borderRadius: BorderRadius.circular(30.0),
child: MaterialButton(
minWidth: 200.0,
height: 42.0,
onPressed: () {
final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));
Scaffold.of(context).showSnackBar(snackBar);
},
color: Colors.blue[300],
child: Text('Entrar', style: TextStyle(color: Colors.white)),
),
),
);
}
}
TextFormField is not working properly, its blinking continuously and it doesn't allow me to write anything, as I tap on the TextFormField my keyboard appears for a second and disappear instantly. I am confused what wrong I have done with my code, I've matched my code with previous working code, but still getting this behaviour .
Here is my code.
class ComingSoonState extends State<ComingSoon> {
String language;
TextEditingController _textEdititingController ;
#override
void initState() {
_textEdititingController = new TextEditingController(); //Initialised TextEditingController
super.initState();
}
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final Size size = MediaQuery.of(context).size;
final formData = new Form(
key: widget._formKey,
child: new Container(
padding: const EdgeInsets.only(
left: 35.0,
right: 35.0),
child: new Column(
children: <Widget>[
new Theme(
data: theme.copyWith(primaryColor: Colors.black54),
child: new TextFormField(
controller: _textEdititingController, //ADDED CONTROLLER HERE
style: const TextStyle(color: Colors.black54),
decoration: new InputDecoration(
labelText: 'Amount',
labelStyle: const TextStyle(color: Colors.black54)
),
// validator: this._validateEmail,
validator: (val) {
return val.isEmpty
? "Please enter amount"
: null;
},
onSaved: (String value) {
// this._data.email = value;
this.language = value;
}
),
),
],
),
),
);
return Scaffold(
appBar: new AppBar(
leading: null,
title: const Text('Send Money', style: const TextStyle(
color: Colors.white
),
),
),
body: new Container(
color: Colors.grey[300],
child: new ListView(
children: <Widget>[
new Container(
child: new Column(
children: <Widget>[
new Container(
height: 60.0 ,
padding: const EdgeInsets.all(5.0),
child: new Card(
child: new Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: new Text("Available balance in wallet", style:
new TextStyle(color: Colors.black54,
fontSize: 16.0
),),
),
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: new Text("123 KSH", style:
new TextStyle(color: Colors.blueAccent,
fontSize: 16.0
),),
),
],
),
),
) ,
new Container(
//height: 300.0,
padding: const EdgeInsets.all(5.0),
child: new Card(
child: new Container(
child: new Center(
child: new Column(
children: <Widget>[
formData
],
),
),
),
),
)
],
)
),
],
),
),
);
}
}
I added a floating action button that presents a dialog that will show what you entered into the TextField (using the controller). I'm not sure what form key you were passing in before but making the GlobalKey instance a member variable eliminates the keyboard present/dismiss issue.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String language;
TextEditingController _textEditingController;
final _formKey = GlobalKey<FormState>();
#override
void initState() {
_textEditingController =
TextEditingController(); //Initialised TextEditingController
super.initState();
}
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final Size size = MediaQuery.of(context).size;
final formData = Form(
key: _formKey,
child: Container(
padding: const EdgeInsets.only(left: 35.0, right: 35.0),
child: Column(
children: <Widget>[
Theme(
data: theme.copyWith(primaryColor: Colors.black54),
child: TextFormField(
controller: _textEditingController,
//ADDED CONTROLLER HERE
style: const TextStyle(color: Colors.black54),
decoration: InputDecoration(
labelText: 'Amount',
labelStyle: const TextStyle(color: Colors.black54)),
// validator: this._validateEmail,
validator: (val) {
return val.isEmpty ? "Please enter amount" : null;
},
onSaved: (String value) {
// this._data.email = value;
language = value;
}),
),
],
),
),
);
return Scaffold(
appBar: AppBar(
leading: null,
title: const Text(
'Send Money',
style: const TextStyle(color: Colors.white),
),
),
body: Container(
color: Colors.grey[300],
child: ListView(
children: <Widget>[
Container(
child: Column(
children: <Widget>[
Container(
height: 60.0,
padding: const EdgeInsets.all(5.0),
child: Card(
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
"Available balance in wallet",
style: TextStyle(
color: Colors.black54, fontSize: 16.0),
),
),
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
"123 KSH",
style: TextStyle(
color: Colors.blueAccent, fontSize: 16.0),
),
),
],
),
),
),
Container(
//height: 300.0,
padding: const EdgeInsets.all(5.0),
child: Card(
child: Container(
child: Center(
child: Column(
children: <Widget>[formData],
),
),
),
),
)
],
)),
],
),
),
floatingActionButton: FloatingActionButton(
// When the user presses the button, show an alert dialog with the
// text the user has typed into our text field.
onPressed: () {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
// Retrieve the text the user has typed in using our
// TextEditingController
content: Text(_textEditingController.text),
);
},
);
},
tooltip: 'Show me the value!',
child: Icon(Icons.text_fields),
),
);
}
}
I am trying to create a dropdown / combobox widget and having a hard time to figure. Below is the code that I have tried. Basically creating a list that I gathered from here and there but not able to put it in my own code accordingly.
import 'package:flutter/material.dart';
class Signup extends StatefulWidget{
Signup({Key key}) : super (key:key);
final String title;
#override
SignupPage createState() => new SignupPage();
}
class SignupPage extends State<Signup> {
#override
Widget build(BuildContext context) {
List _countrycodes = ["+65","+91"];
List<DropdownMenuItem<String>> _dropDownMenuItems;
String _selectedCountryCode;
List<String> _colors = <String>['', 'red', 'green', 'blue', 'orange'];
String _color = '';
List<DropdownMenuItem<String>> getDropDownMenuItems() {
List<DropdownMenuItem<String>> items = new List();
for (String code in _countrycodes) {
items.add(new DropdownMenuItem(
value: code,
child: new Text(code)
));
}
return items;
}
/**return new Material(
color: Colors.blueAccent,
child: new Scaffold(
body: new Stack(
children: <Widget>[
new Container(
decoration: new BoxDecoration(
image: new DecorationImage(image: new AssetImage("assets/images/download.jpg"), fit: BoxFit.cover,),
),
),
new Center(
child: new Text("Hello LandingPage"),
)
],
)
));**/
final name = TextFormField(
keyboardType: TextInputType.text,
autofocus: false,
initialValue: 'Techie Quickie',
decoration: InputDecoration(
hintText: 'Name',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0)
),
),
);
final email = TextFormField(
keyboardType: TextInputType.emailAddress,
autofocus: false,
initialValue: 'tq#gmail.com',
decoration: InputDecoration(
hintText: 'Email',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0)
),
),
);
final password = TextFormField(
keyboardType: TextInputType.text,
obscureText: true,
autofocus: false,
initialValue: 'password',
decoration: InputDecoration(
hintText: 'Password',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0)
),
),
);
final passwordConfirmation = TextFormField(
keyboardType: TextInputType.text,
obscureText: true,
autofocus: false,
initialValue: 'password',
decoration: InputDecoration(
hintText: 'Password',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0)
),
),
);
final loginButton = Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: Material(
borderRadius: BorderRadius.circular(30.0),
shadowColor: Colors.lightBlueAccent.shade100,
elevation: 5.0,
child: MaterialButton(
minWidth: 200.0,
height: 42.0,
onPressed: (){
print("Signup button clicked");
},
color: Colors.lightBlueAccent,
child:
Text('Sign Up',
style: TextStyle(color: Colors.white, fontSize: 20.0),
),
),
)
);
final countryCode = DropdownButton(
value: _selectedCountryCode,
items: _dropDownMenuItems,
onChanged: null
);
final inputDecoratoring =InputDecorator(
decoration: const InputDecoration(
icon: const Icon(Icons.color_lens),
labelText: 'Color',
),
isEmpty: _color == '',
child: new DropdownButtonHideUnderline(
child: new DropdownButton<String>(
value: _color,
isDense: true,
onChanged: (String newValue) {
setState(() {
_color = newValue;
});
},
items: _colors.map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
),
),
);
return Scaffold(
backgroundColor: Colors.white,
body: Center (
child: ListView(
padding: EdgeInsets.only(left: 24.0, right: 24.0),
shrinkWrap: true,
children: <Widget>[
name,
SizedBox(height: 18.0),
email,
SizedBox(height: 18.0),
password,
SizedBox(height: 18.0),
passwordConfirmation,
SizedBox(height: 18.0),
// countryCode,
// SizedBox(height: 18.0),
loginButton,
SizedBox(height: 38.0),
],
)
)
);
}
}
This is my main.dart class
import 'package:flutter/material.dart';
import 'package:elegal_dart/screens/landing_page.dart';
import 'package:elegal_dart/screens/home.dart';
import 'package:elegal_dart/screens/devices.dart';
import 'package:elegal_dart/screens/signup.dart';
import 'package:elegal_dart/screens/signin.dart';
/*
void main()
{
Devices( new MaterialApp(
home: new Devices(),
));
}*/
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget
{
#override
Widget build(BuildContext context)
{
return new MaterialApp(
title: 'Navigation',
routes: <String, WidgetBuilder>
{
'/Home' : (BuildContext context) => new Home(),
'/LandingPage' : (BuildContext context) => new LandingPage(),
'/Devices' : (BuildContext context) => new Devices(),
'/Signup' : (BuildContext context) => new Signup(),
'/Signin' : (BuildContext context) => new Signin(),
},
//home: new Signin(onSubmit: null),
home: new Signup(),
);
}
}
UPDATED CODE:
import 'package:flutter/material.dart';
class Signup extends StatefulWidget{
Signup({Key key}) : super (key:key);
final String title;
#override
SignupPage createState() => new SignupPage();
}
class SignupPage extends State<Signup> {
#override
Widget build(BuildContext context) {
List _countrycodes = ["+65","+91"];
List<DropdownMenuItem<String>> _dropDownMenuItems;
String _selectedCountryCode;
List<String> _ccodes = <String>['', '+65', '+91', '+60', 'orange'];
String _ccode = '';
List<DropdownMenuItem<String>> getDropDownMenuItems() {
List<DropdownMenuItem<String>> items = new List();
for (String code in _countrycodes) {
items.add(new DropdownMenuItem(
value: code,
child: new Text(code)
));
}
return items;
}
final name = TextFormField(
keyboardType: TextInputType.text,
autofocus: false,
initialValue: 'Techie Quickie',
decoration: InputDecoration(
hintText: 'Name',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0)
),
),
);
final email = TextFormField(
keyboardType: TextInputType.emailAddress,
autofocus: false,
initialValue: 'tq#gmail.com',
decoration: InputDecoration(
hintText: 'Email',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0)
),
),
);
final password = TextFormField(
keyboardType: TextInputType.text,
obscureText: true,
autofocus: false,
initialValue: 'password',
decoration: InputDecoration(
hintText: 'Password',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0)
),
),
);
final passwordConfirmation = TextFormField(
keyboardType: TextInputType.text,
obscureText: true,
autofocus: false,
initialValue: 'password',
decoration: InputDecoration(
hintText: 'Password',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0)
),
),
);
final loginButton = Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: Material(
borderRadius: BorderRadius.circular(30.0),
shadowColor: Colors.lightBlueAccent.shade100,
elevation: 5.0,
child: MaterialButton(
minWidth: 200.0,
height: 42.0,
onPressed: (){
print("Signup button clicked");
},
color: Colors.lightBlueAccent,
child:
Text('Sign Up',
style: TextStyle(color: Colors.white, fontSize: 20.0),
),
),
)
);
final countryCode = DropdownButtonHideUnderline (
child: DropdownButton<String>(
value: _ccode,
isDense: true,
items: _ccodes.map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
onChanged: (String newValue) {
setState(() {
_ccode = newValue;
});
}
)
);
return Scaffold(
backgroundColor: Colors.white,
body: Center (
child: ListView(
padding: EdgeInsets.only(left: 24.0, right: 24.0),
shrinkWrap: true,
children: <Widget>[
name,
SizedBox(height: 18.0),
email,
SizedBox(height: 18.0),
password,
SizedBox(height: 18.0),
passwordConfirmation,
SizedBox(height: 18.0),
countryCode,
SizedBox(height: 18.0),
loginButton,
SizedBox(height: 38.0),
],
)
)
);
}
}
The error that I am getting in the AVD
The assertion tells you that items must not be null. It's referring to this one: items: _dropDownMenuItems,. You don't ever assign _dropDownMenuItems.
This is a good use for a Dart-y pattern. Replace it with:
final countryCode = DropdownButton(
value: _selectedCountryCode,
items: _countrycodes
.map((code) =>
new DropdownMenuItem(value: code, child: new Text(code)))
.toList(),
onChanged: null,
);
and you no longer need the field or the getDropDownMenuItems function. If you aren't familiar with this syntax, read it as "map each code in countryCodes to a DropdownMenuItem and form them back into a List"
Also, the scope of your variables is wrong. Move them out of the build function and into the State, like this:
class SignupPage extends State<Signup> {
List<String> _countrycodes = ["+65", "+91"];
List<String> _colors = ['', 'red', 'green', 'blue', 'orange'];
String _selectedCountryCode;
String _color = '';
My answer will solve also the problem of using more than one DropdownButton in the same page without the error of more than 1 item in the DropdownMenuItem .
First define the lists you will use this way:
static const List<String> _razas = const
["Schnauzer", "Ovejero Alemán", "Husky", "Brasov",
"Constanta","Mestizo"];
////////barrios
static const List<String> _barrios = const
"Belgrano", "Caballito", "Colegiales", "Lugano", "Palermo","Pilar",
"Montserrat", "Vicente López" ];
static const List<String> _tamanio = const
["Chico", "Mediano", "Grande" ];
String tamanioValue = _tamanio[0];
String barriosValue = _barrios[0];
String razasValue = _razas[0];
Then use this Containers in your Scaffold
Container(
margin: EdgeInsets.only(top:20.0, bottom: 20.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.start,
children:<Widget> [
Text("RAZA "),
new DropdownButton(value:razasValue,
onChanged: (string) => setState(() => razasValue =
string),
items: _razas.map((string) {
return new DropdownMenuItem(
child: new Text(string),
value: string,
);
}).toList(),
),
]
)
),
Container(
margin: EdgeInsets.only(top:20.0, bottom: 20.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.start,
children:<Widget> [
Text("BARRIO "),
new DropdownButton(value:barriosValue,
onChanged: (string) => setState(() =>
barriosValue = string),
items: _barrios.map((string) {
return new DropdownMenuItem(
child: new Text(string),
value: string,
);
}).toList(),
),
]
)
),
Then just replace the values for you database or any file you store the data
raza: razasValue,
barrio: barriosValue,
I have a strange problem in my application I have a signup page with several textformfields inside a ListView and the ListView is a child to the Form.
when i reach the last formfield inside the ListView i need to scroll up the screen to actually see what i am typing inside the field. When i do scroll up the textformfields which go out of focus are losing the entered text and are initialised as null. I am not sure what i am missing or do i have to use some other way to make sure i retain the TextFormField values. I have tried to use the controller in every TextFormField then it is retaining the value even if I scroll up. But, the properties of the object e.g, hostelData.hostelName are only updated first time and the changes are not reflected in the object property from the second time. I am not so sure what i am missing? Please can someone help me in solving this.
Please excuse my mistakes in posting as i am new to programming and any help would be greatly appreciated.
I have posted the code below so that you can reproduce the same problem.
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:myhostel/theme.dart' as Theme;
import 'package:myhostel/globals.dart' as gl;
class SignUp extends StatefulWidget {
const SignUp({ Key key }) : super(key: key);
#override
_SignUpState createState() => new _SignUpState();
}
class HostelData{
String hostelName = '';
String ownersName = '';
String mobileNumber = '';
String emailId = '';
String password = '';
String city = '';
String hostelType= 'mens';
String confirmPassword ='';
}
class _SignUpState extends State<SignUp> {
HostelData hostelData = new HostelData();
void showInSnackBar(String value) {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text(value),
duration: const Duration(milliseconds: 3000),
));
}
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
final GlobalKey<FormFieldState<String>> _passwordFieldKey = new
GlobalKey<FormFieldState<String>>();
bool _autovalidate = false;
bool _formWasEdited = false;
void _handleSubmitted() {
final FormState form = _formKey.currentState;
if (!form.validate()) {
_autovalidate = true; // Start validating on every change.
showInSnackBar('Please fix the errors in red before submitting.');
} else {
form.save();
showInSnackBar('${hostelData.ownersName}\'s hostel name is
${hostelData.hostelName}');
_createuserwithemailandpassword();
}
}
String _validateName(String value) {
_formWasEdited = true;
if (value.isEmpty)
return 'Name is required.';
final RegExp nameExp = new RegExp(r'^[A-Za-z ]+$');
if (!nameExp.hasMatch(value))
return 'Please enter only alphabetical characters and spaces.';
return null;
}
String _validatePassword(String value) {
_formWasEdited = true;
final FormFieldState<String> passwordField =
_passwordFieldKey.currentState;
if (passwordField.value == null || passwordField.value.isEmpty)
return 'Please choose a password.';
if (passwordField.value != value)
return 'Passwords don\'t match';
return null;
}
#override
Widget build(BuildContext context){
return new MaterialApp(
theme: Theme.MyHostelThemeData,
home: new Scaffold(
key: _scaffoldKey,
body: new Container(
child: new Container(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage('assets/bg.png'),
fit: BoxFit.fill,
),
),
child: new Form(
key: _formKey,
autovalidate: _autovalidate,
child: new ListView(
children: <Widget>[
new Container(
margin: const EdgeInsets.only(top:32.0),
child: new Center(
child: new Text('SIGN UP',
style: new TextStyle(
color: const Color(0xFFF5FEFD),fontSize:24.0,fontWeight:
FontWeight.bold),
),
),
),
new Row(
children:[
new Expanded(
child: new Container(
margin: const EdgeInsets.only(left: 24.0,right: 12.0,),
child:new TextFormField(decoration: new InputDecoration(
/*hintStyle: new TextStyle(
fontSize: 20.0,color: const Color(0xFFF5FEFD),),*/
labelText: 'Hostel Name',
),
/*style: new TextStyle(
fontSize: 20.0,
color: Colors.white,
),*/
onSaved:(String value){ hostelData.hostelName = value; },
validator: _validateName,
),
),
),
],
),
new Row(
children:[
/*new Container(
margin: const EdgeInsets.all(8.0),
width: 24.0,
child: new Image.asset(
'assets/person_avatar.png',
fit: BoxFit.contain,
//alignment: FractionalOffset.center,
),
),*/
new Expanded(
child: new Container(
margin: const EdgeInsets.only(left: 24.0,right: 12.0,),
child:new TextFormField(
//controller: _ownersNameController,
decoration: new InputDecoration(//hintText: 'Mobile
Number',
/* hintStyle: new TextStyle(fontSize: 20.0,color: const
Color(0xFFF5FEFD),),*/
labelText: 'Owners Name',
),
/*style: new TextStyle(
fontSize: 20.0,
color: Colors.white,
),*/
onSaved: (String value) { hostelData.ownersName = value;
},
validator: _validateName,
),
),
),
],
),
new Row(
children:[
new Expanded(
child: new Container(
margin: const EdgeInsets.only(left: 24.0,right: 12.0,),
child:new TextFormField(
//controller: _mobileNumberController,
keyboardType: TextInputType.phone,
decoration: new InputDecoration(//hintText: 'Mobile
Number',
/* hintStyle: new TextStyle(fontSize: 20.0,color:
const Color(0xFFF5FEFD),),*/
labelText: 'Mobile Number',
//prefixText: '+91',
),
/*style: new TextStyle(
fontSize: 20.0,
color: Colors.white,
),*/
onSaved: (String value) { hostelData.mobileNumber = value; },
//validator: _validatePhoneNumber,
),
),
),
],
),
//implements EmailID Row
new Row(
children:[
new Expanded(
child: new Container(
margin: const EdgeInsets.only(left: 24.0,right: 12.0,),
child:new TextFormField(
//controller: _emailIdController,
decoration: new InputDecoration(
//hintText: 'Mobile Number',
/*hintStyle: new TextStyle(fontSize: 20.0,color: const
Color(0xFFF5FEFD),),*/
labelText: 'EMail Id',
helperText: 'Required',
),
/*style: new TextStyle(
fontSize: 20.0,
color: Colors.white,
),*/
onSaved: (String value){hostelData.emailId = value;},
),
),
),
],
),
//implements the password row
new Container(
padding: const EdgeInsets.only(left: 8.0,),
child: new Row(
children: <Widget>[
new Container(
margin: const EdgeInsets.only(top: 16.0),
//padding: const EdgeInsets.all(8.0),
child: new Icon(Icons.lock_outline,
color: const Color(0xFFF5FEFD)
),
),
new Expanded(
child: new Container(
margin: const EdgeInsets.only(
left: 28.0, right: 12.0,),
child: new TextFormField(
key: _passwordFieldKey,
//controller: _passwordController,
decoration: const InputDecoration(
hintText: 'min 8 characters',
labelText: 'Password',
helperText: 'Required',
),
autocorrect: false,
//obscureText: true,
//validator: 'Required',
/*style: DefaultTextStyle.of(context).style.merge(new
TextStyle(
fontSize: 16.0,
color: CustomColors.fontColor,
),
),*/
onSaved: (String value){hostelData.password = value;},
),
),
),
],
),
),
new Container(
padding: const EdgeInsets.only(left: 8.0,),
child: new Row(
children: <Widget>[
new Container(
margin: const EdgeInsets.only(top: 16.0),
//padding: const EdgeInsets.all(8.0),
child: new Icon(Icons.lock_outline,
color: const Color(0xFFF5FEFD)
),
),
new Expanded(
child: new Container(
margin: const EdgeInsets.only(
left: 28.0, right: 12.0,),
child: new TextFormField(
//controller: _confirmpasswordcontroller,
decoration: const InputDecoration(
labelText: 'Confirm Password',
helperText: 'Required'
),
autocorrect: false,
// obscureText: true,
//validator: 'Required',
/*style: DefaultTextStyle.of(context).style.merge(new
TextStyle(
fontSize: 16.0,
color: CustomColors.fontColor,
),
),*/
//onSaved: (String value){hostelData.confirmPassword =
value;},
validator: _validatePassword,
),
),
),
],
),
),
new Row(
children:[
new Expanded(
child: new Container(
margin: const EdgeInsets.only(left: 24.0,right: 12.0,),
child:new TextFormField(
controller: _cityController,
decoration: new InputDecoration(labelText: 'City*'),
onSaved: (String value){hostelData.city = value;},
validator: _validateName,
),
),
),
],
),
new Container(
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.only(left:24.0,right: 24.0),
decoration: new BoxDecoration(
border: new Border.all(
color: Colors.white,
width: 2.0,
),
borderRadius: new BorderRadius.all(const
Radius.circular(32.0),)
),
child: new FlatButton(
onPressed: ((){
print('SignUp Button Clicked');
_handleSubmitted();
}),
child: new Text (
'SIGN UP',
style: new TextStyle(fontSize: 24.0,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
new Row(
children:[
new Container(
margin: const EdgeInsets.only(left: 24.0,bottom: 12.0),
child:new Text(
'Already have an account?',
style: new TextStyle(
fontSize: 20.0,
color: Colors.white,
),
),
),
new Container(
padding: const EdgeInsets.only(right: 8.0,top:
4.0,bottom:16.0),
margin: const EdgeInsets.only(right: 12.0,left: 4.0),
child:
new MaterialButton(
onPressed: ((){
//Sign In Button pressed declaration here
print('sign in button clicked');
}),
child: new Text('SignIn',
style: new TextStyle(
fontSize: 16.0,
color: Colors.teal[200],
),
),
minWidth: 16.0,
),
),
],
),
new Container(
padding: const EdgeInsets.only(left: 16.0),
child: new Text('* indicates required field',
style: new TextStyle(
fontSize: 14.0,
color: Colors.white,
),),
),
]
),
),
),
),
),
);
} // widget ends here
globals.dart
library my_hostel.globals;
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
final FirebaseAuth auth = FirebaseAuth.instance;
final DatabaseReference messagesRef =FirebaseDatabase.instance.reference();
Here is an example of working code:
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String s1 = "";
String s2 = "";
String s3 = "";
String s4 = "";
String s5 = "";
final TextEditingController c1 = new TextEditingController();
final TextEditingController c2 = new TextEditingController();
final TextEditingController c3 = new TextEditingController();
final TextEditingController c4 = new TextEditingController();
final TextEditingController c5 = new TextEditingController();
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text("Example"),),
body: new ListView(
children: <Widget>[
new Padding(padding: new EdgeInsets.all(70.0),),
new TextField(
controller: c1,
onChanged: (String text) {
s1 = text;
},
),
new Padding(padding: new EdgeInsets.all(70.0),),
new TextField(
controller: c2,
onChanged: (String text) {
s2 = text;
},
),
new Padding(padding: new EdgeInsets.all(70.0),),
new TextField(
controller: c3,
onChanged: (String text) {
s3 = text;
},
),
new Padding(padding: new EdgeInsets.all(70.0),),
new TextField(
controller: c4,
onChanged: (String text) {
s4 = text;
},
),
new Padding(padding: new EdgeInsets.all(70.0),),
new TextField(
controller: c5,
onChanged: (String text) {
s5 = text;
},
),
],
)
);
}
}
You need to add a TextEditingController with your TextField. Listview removes invisible widgets because rendering a ton of widgets that are not being displayed would make it perform worse!