I added a float button in my application while clicking on it two text-field appear , without adding formKey the button works but when i use it to controle if the field text are empty or not by if statement if (formKey.currentState!.validate()) in the Debug Console i get this error
════════ Exception caught by gesture ═══════════════════════════════════════════
Unexpected null value.
═══════════════════════════════════════════════════════════════════════════════
this the code of floating button
floatingActionButton: FloatingActionButton(
onPressed: () {
if (isBottomSheetShown) {
if (formKey.currentState!.validate()) {
Navigator.pop(context);
isBottomSheetShown = false;
setState(() {
fabIcon = Icons.edit;
});
}
} else {
scaffoldKey.currentState!.showBottomSheet(
(context) => Container(
color: Colors.grey[100],
padding: const EdgeInsets.all(20),
child: Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: titleController,
decoration: InputDecoration(
prefixIcon: Icon(Icons.title),
hintText: 'write a title',
labelText: 'Title',
border: OutlineInputBorder(),
),
),
SizedBox(
height: 15,
),
TextField(
controller: timeController,
decoration: InputDecoration(
prefixIcon: Icon(Icons.watch_later_outlined),
hintText: 'write a time',
labelText: 'Time',
border: OutlineInputBorder(),
),
onTap: () {
showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
).then((value) {
timeController.text =
value!.format(context).toString();
print(value!.format(context));
});
},
),
],
),
),
),
);
}
isBottomSheetShown = true;
setState(() {
fabIcon = Icons.add;
});
},
child: Icon(
fabIcon,
)),
The Unexpected null value is occurring because you're asserting that formKey.currentState is not null before you validate it, so if currentState equals null, it will throw that error.
I'd suggest first checking if formKey.currentState != null before calling the validate method.
Related
class login extends StatelessWidget {
var emailController = TextEditingController();
var PasswordController = TextEditingController();
var _formKey = GlobalKey<FormState>();
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Login"),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Center(
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Login",
style: TextStyle(
fontSize: 40, fontWeight: FontWeight.bold)),
SizedBox(height: 40,),
TextFormField(
controller: emailController,
onFieldSubmitted: (String value) {
print(value);
},
onChanged: (String value) {
print(value);
},
validator: (String ?value) {
if (value == null || value.isEmpty) {
return 'the password must not be Empty';
}
return null;
},
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: "E-mail Address",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)),
prefixIcon: Icon(Icons.email),
),
),
SizedBox(height: 20,),
TextFormField(
controller: PasswordController,
obscureText: true,
keyboardType: TextInputType.visiblePassword,
validator: (String ?value) {
if (value == null || value.isEmpty) {
return 'the password must not be Empty';
}
return null;
},
onFieldSubmitted: (String value) {
print(value);
},
onChanged: (String value) {
print(value);
},
decoration: InputDecoration(
labelText: "Password",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)),
prefixIcon: Icon(Icons.lock),
suffixIcon: Icon(Icons.remove_red_eye_rounded)
)
),
SizedBox(height: 20,),
defaultButton(
background: Colors.black26,
isUpperCase: true,
text: "Login",
function: () {
if (_formKey.currentState!.validate()) ==> // Null check operator
used on a null value.
{
print(emailController);
print(PasswordController);
}
}
),
SizedBox(height: 20,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Already you Have account?"),
TextButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(
builder: (context) => Registry()));
},
child: Text("Register now "))
],
),
],
),
),
),
),
),
);
}
}
Hey what you're using is called the non-null assertion operator, it actually means you are saying that value can't be null. If you want to make sure it's not null before trying to access validate(), you should use optional chaining _formKey.currentState?.validate()
What you can also try to do is check that it's not null before you call validate().
if (!!_formkey.currentState && _formKey.currentState.validate())
I'm new to flutter and following a tutorial where using an icon, the visibility of a password changes from hidden to visible.
I saw some cases where IconButton is used but even though I tried that method I'm still getting red lines everywhere. I wish to know where exactly the problem is unless it's a version related issue.
the variables are defined as such:
late String _password, _email;
bool _isObscure = true;
The Form Field
Widget _showPasswordInput() {
return TextFormField(
onSaved: (val) => _password = val!,
obscureText: _isObscure,
decoration: const InputDecoration(
// border: OutlineInputBorder(),
suffixIcon: GestureDetector(
onTap: () {
setState(() {
_isObscure = !_isObscure;
});
},
child: Icon(
_isObscure ? Icons.visibility : Icons.visibility_off,
),
),
labelText: 'Password',
hintText: 'Enter valid password, min: 6 chars',
icon: Icon(Icons.lock),
),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter some text';
}
return null;
},
);
}
How it's used in the build
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Center(
child: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(children: <Widget>[
_showRegisterButton(),
]))),
)),
);
}
Inside InputDecoration, data is changing on runtime, therefore it cannot be const
decoration: InputDecoration(
// border: OutlineInputBorder(),
suffixIcon: GestureDetector(
onTap: () {
setState(() {
_isObscure = !_isObscure;
});
},
child: Icon(
_isObscure ? Icons.visibility : Icons.visibility_off,
),
),
and create a method like myTextFiled(BuildContext context) => TextFormField(....)
I have a form in flutter and when you press the button it will call a post function that register a user but i can't acces to the variables in the form.
I want to acces to the username, email and password input values to make a post and register a user
I have divide it in diferents widgets, here the code:
The form
This is the form widget that I have in my register screen
Form(
key: _formKey,
child: SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.symmetric(
horizontal: 40.0,
vertical: 60.0,
),
child: Column(
children: <Widget>[
new Container(
child: Image.asset(
'../../assets/logo-feec.png',
width: 200,
),
padding: EdgeInsets.symmetric(vertical: 15),
alignment: Alignment.topCenter,
),
buildUsernameTF(),
SizedBox(
height: 30.0,
),
buildEmailTF(),
SizedBox(
height: 30.0,
),
buildPasswordTF(),
SizedBox(
height: 30.0,
),
buildConfirmPasswordTF(),
Container(
padding: EdgeInsets.symmetric(vertical: 25.0),
width: 200,
child: ElevatedButton.icon(
onPressed: () async {
if (_formKey.currentState.validate()) {
futureString = await resource.MyHttpService().registerUser(username, email, password);
/* Here we want to access the variables */
}
},
label: Text('Envia'),
icon: Icon(Icons.login),
style: ElevatedButton.styleFrom(
primary: Colors.white,
onPrimary: Colors.black,
shape: const BeveledRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(3))),
)),
),
Container(
padding: EdgeInsets.all(5),
width: 200,
child: TextButton(
onPressed: () => RouterConfig.router.navigateTo(
context,
"login",
transition: TransitionType.nativeModal,
),
child: Text("Inicia sessió"),
),
)
],
)),
)
The widgets
These are the widgets called in the form
import 'package:flutter/material.dart';
import 'package:email_validator/email_validator.dart';
final TextEditingController _confirmPass = TextEditingController();
final TextEditingController _pass = TextEditingController();
Widget buildPasswordTF() {
return Container(
width: 600,
child: TextFormField(
controller: _pass,
obscureText: true,
decoration: const InputDecoration(
icon: Icon(Icons.lock),
hintText: 'Enter password',
labelText: 'Password *'),
validator: (password) {
if (password == null || password.isEmpty) {
return 'invalid Password';
}
return null;
},
),
);
}
Widget buildConfirmPasswordTF() {
return Container(
width: 600,
child: TextFormField(
controller: _confirmPass,
obscureText: true,
decoration: const InputDecoration(
icon: Icon(Icons.lock),
hintText: 'Enter password',
labelText: 'Password *'),
validator: (password) {
if (password == null || password.isEmpty) {
return 'Invalid Password';
}
if (password != _pass.text) {
return 'Passwords don\'t match';
}
return null;
},
),
);
}
Widget buildUsernameTF() {
return Container(
width: 600,
child: TextFormField(
decoration: const InputDecoration(
icon: Icon(Icons.person),
hintText: 'Enter Username',
labelText: 'Username'),
validator: (username) {
if (username == null || username.isEmpty) {
return 'Invalid Username';
}
return null;
},
),
);
}
Widget buildEmailTF() {
return Container(
width: 600,
child: TextFormField(
decoration: const InputDecoration(
icon: Icon(Icons.mail),
hintText: 'Enter email',
labelText: 'Email *'),
validator: (email) {
if (EmailValidator.validate(email) != true) {
return 'Invalid Email';
}
if (email == null || email.isEmpty) {
return 'Invalid Email';
}
return null;
},
),
);
}
The answer to this will depend on your widget tree but ideally, you would be able to access the TextEditingControllers inside your TextFormFields.
You will need to create TextEditingControllers for each TextFormField:
TextEditingController emailController = TextEditingController();
And then when you submit, you need to access the controller and get the text.
onPressed: () async {
if (_formKey.currentState.validate()) {
futureString = await resource.MyHttpService().registerUser(username, email, password);
/* Here we want to access the variables */
print('Email text: ' + emailController.text);
}
},
Container(
padding: EdgeInsets.all(15),
child: Column(
children: [
TextFormField(
onTap: () =>
Navigator.of(context).pushNamed(AirportSearchScreen.id).then(
(value)
{
setState(() {
_initValues['departureCity'] = value;
print(_initValues['departureCity']);
});
},
),
decoration: InputDecoration(
labelText: 'Departure City',
),
initialValue: _initValues['departureCity'],
),
],
),
);
When I am printing the value, it is give right result. But I am not able to get the result on TextFormField.
Try the code below :
child: Column(
children: [
TextFormField(
onTap: () =>
Navigator.of(context).pushNamed(AirportSearchScreen.id).then(
(value)
{
setState(() {
_initValues['departureCity'] = value;
print(_initValues['departureCity']);
});
},
),
decoration: InputDecoration(
labelText: 'Departure City',
),
controller: TextEditingController(text: _initValues['departureCity']),
),
],
),
I am trying to catch the tap event on TextFormField into a flutter Form.
I use a GestureDetector to do that with the TextFormField as child but nothing is firing when a click on it :
#override
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
appBar: new AppBar(title: const Text('Recherche de sorties')),
body: new DropdownButtonHideUnderline(
child: new Form(
key: _formKey,
autovalidate: _autovalidate,
child: new ListView(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
children: <Widget>[
new DatePicker(
labelText: 'Date',
selectedDate: widget.request.dateDebut,
initialDate: widget.request.dateDebut,
firstDate: new DateTime.now().add(new Duration(days: -1)),
lastDate: new DateTime.now().add(new Duration(days: 365 * 4)),
selectDate: (DateTime value) {
setState(() {
widget.request.dateDebut = value;
});
},
datePickerMode: DatePickerMode.day,
icon: const Icon(Icons.date_range),
),
new InputDecorator(
decoration: const InputDecoration(
labelText: 'Rayon',
hintText: '-- Choisissez un rayon --',
icon: const Icon(Icons.settings_backup_restore),
),
isEmpty: widget.request.rayon == null,
child: new DropdownButton<String>(
value: widget.request.rayon.toString(),
isDense: true,
onChanged: (String newValue) {
setState(() {
widget.request.rayon = int.parse(newValue);
});
},
items: _rayons.keys.map((int key) {
return new DropdownMenuItem<String>(
value: key.toString(),
child: new Text(_rayons[key]),
);
}).toList(),
),
),
new GestureDetector(
onTap: () async {
print("Container clicked");
Prediction p = await showGooglePlacesAutocomplete(
context: context,
apiKey: Consts.googlePlacesApiKey,
mode: Mode.fullscreen,
language: "fr",
components: [new Component(Component.country, "fr")]);
if (p != null) {
(_scaffoldKey.currentState).showSnackBar(
new SnackBar(content: new Text(p.description)));
}
},
child: new TextFormField(
// controller: controller,
decoration: const InputDecoration(
icon: const Icon(Icons.room),
hintText: 'Où êtes vous ?',
labelText: 'Localisation',
),
),
),
new Container(
padding: const EdgeInsets.all(20.0),
alignment: Alignment.center,
child: new Align(
alignment: const Alignment(0.0, -0.2),
child: new ButtonBar(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new RaisedButton(
child: const Text('ANNULER'),
onPressed: _fermerCritereRecherche,
),
new RaisedButton(
child: const Text('VALIDER'),
onPressed: _valider,
),
],
),
)),
]),
),
),
);
}
If i replace :
new GestureDetector(
onTap: () async {
print("Container clicked");
Prediction p = await showGooglePlacesAutocomplete(
context: context,
apiKey: Consts.googlePlacesApiKey,
mode: Mode.fullscreen,
language: "fr",
components: [new Component(Component.country, "fr")]);
if (p != null) {
(_scaffoldKey.currentState).showSnackBar(
new SnackBar(content: new Text(p.description)));
}
},
child: new TextFormField(
// controller: controller,
decoration: const InputDecoration(
icon: const Icon(Icons.room),
hintText: 'Où êtes vous ?',
labelText: 'Localisation',
),
),
),
By a simple Container it is working :
new GestureDetector(
onTap: () async {
print("Container clicked");
Prediction p = await showGooglePlacesAutocomplete(
context: context,
apiKey: Consts.googlePlacesApiKey,
mode: Mode.fullscreen,
language: "fr",
components: [new Component(Component.country, "fr")]);
if (p != null) {
(_scaffoldKey.currentState).showSnackBar(
new SnackBar(content: new Text(p.description)));
}
},
child: new Container(
width: 80.0,
height: 80.0,
margin: new EdgeInsets.all(10.0),
color: Colors.black),
),
Do you have any ideas how to make GestureDetector work with TextFormField ? Maybe with a controller but i have tried without any success
Thanks in advance
Simply use onTap Method of TextFormField:
TextFormField(
onTap: () {
print("I'm here!!!");
}
)
Wrap TextFormField widget With AbsorbPointer widget , then OnTap() works definitely
here is an example:-
GestureDetector(
onTap: () => dialog(),
child: AbsorbPointer(
child: TextFormField(
textInputAction: TextInputAction.newline,
decoration: new InputDecoration(
fillColor: Colors.grey,
border: OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(6.0)),
borderSide:
BorderSide(color: Colors.grey[100]),
gapPadding: 4),
labelText: "Enter your mood",
labelStyle: TextStyle(
letterSpacing: 1,
color: Colors.grey,
fontSize: 13),
hintMaxLines: 1),
validator: (val) {
if (val == "") return "Field can't be empty";
},
keyboardType: TextInputType.text,
enabled: true,
textAlign: TextAlign.justify,
minLines: 3,
autofocus: false,
style: new TextStyle(
fontSize: 16.0,
color: Colors.black,
),
maxLines: 10,
),
),
),
Wrap AbsorbPointer Widget with Gesture Detector, and then work in onTap(). it will work fine.
I have found a solution by using the InputDecorator (from the flutter gallery) :
child: new InputDecorator(
decoration: const InputDecoration(
labelText: 'Localisation',
icon: const Icon(Icons.room),
),
child: widget.request.localisationLibelle != null
? new Text(widget.request.localisationLibelle)
: new Text("-- Choisissez un lieu --"),
),
Instead of using a TextFormField that catch the tap at the place of the GestureDetector I use a simple child Text of the InputDecorator widget.
I just solved this myself using Flutter 0.6.0.
The GestureDetector object takes in a behavior property from this enum to determine how to defer actions.
Small snippet of the GestureDetector taking priority over a TextFormField:
new GestureDetector(
onTap: onTap,
behavior: HitTestBehavior.opaque,
child: new TextFormField(
enabled: onTap == null,
*other stuff here*
),
)
The onTap object is a Function object I declare outside of this. I also set the enabled property based on my onTap object, so I can ensure that if I want to capture a tap, the text form field is disabled.
onTap function is working but if you use it with InputDecoration { enabled: false }, it will not working.
decoration: InputDecoration(
enabled: false, // not working when on tap
isDense: true,
filled: true,
fillColor: _listRadioItemPosition.isEmpty ? Colors.grey[100] : Colors.white,
border: UnderlineInputBorder(borderSide: BorderSide(color: Color(0xFFC4C4C4), width: 1)),
),
onTap: (){
displayBottomSheetPosition(context);
},