TextFormField validation in Flutter - flutter

I am working on Flutter TextFormField. and i want to display an error message below the TextFormField but when i debug i got this error
The method 'validate' was called on null.
Receiver: null
Tried calling: validate()
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
TextEditingController _titleController;
TextEditingController _descriptionController;
final _formKey = GlobalKey<FormState>();
#override
void initState() {
super.initState();
_titleController = new TextEditingController(text: widget.products.adTitle);
_descriptionController = new TextEditingController(text: widget.products.description); #override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Products")
),
body: Container(
margin: EdgeInsets.all(15.0),
alignment: Alignment.center,
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
controller: _titleController,
decoration: InputDecoration(labelText: 'Title'),
validator: (text) {
if (text == null || text.isEmpty) {
return 'Text is empty';
}
return null;
},
RaisedButton(
child: (widget.products.id != null)? Text('Update') : Text('Add'),
onPressed:(){
if (_formKey.currentState.validate()) {
child: Text('Submit');
}

in order to use the validate function, your Column should be wrap in Form
Container(
margin: EdgeInsets.all(15.0),
alignment: Alignment.center,
child: Form(
key: _formKey,
child: Column(children: <Widget>[
TextFormField(
controller: _titleController,
decoration:InputDecoration(labelText: 'Title'),
validator: (text) {
if (text == null || text.isEmpty) {
return 'Text is empty';
}
return null;
},
)
]))),

Related

How to have a unique error message for two textfields

Hi I have a little problem since a few days... I'm trying to find out how to make two fields display an error message at the same place.
I tried with a Formfield but it bugs me because the second textformfield falls in error since I have only one state. I also tried with a simple Formfield but I can't set my validator message in a variable and then display it in a padding.
Would you have an idea please?
Thanks in advance.
Example : I would like to display my error message instead of the Text widget "Error message".
I managed to solve your problem.
Take a look at this screenshot and code:
Screenshot -
Code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _errorText = '';
final GlobalKey<FormState> _key = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Form(
key: _key,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: 100,
width: 50,
child: TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
setState(() {
_errorText = 'Please enter some text';
});
}
return null;
},
),
),
const SizedBox(
width: 50,
),
SizedBox(
height: 100,
width: 50,
child: TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
setState(() {
_errorText = 'Please enter some text';
});
}
return null;
},
),
),
],
),
),
Text(_errorText),
ElevatedButton(
onPressed: () {
if (_key.currentState!.validate()) {}
},
child: const Text('Submit'),
),
],
),
),
),
);
}
}

mapEventToState not trigerring using BLOC pattern, Flutter

I'm using flutter to develop an application and decides to use the bloc pattern as recommended by google, however the event I defined is never getting triggered. I defined the event to be when a check for connectivity happens a boolean is returned from the controller to the view.
Below is the relevant code
login controller.dart
import 'package:XXXXX/connection.dart';
import 'package:bloc/bloc.dart';
import 'package:connectivity/connectivity.dart';
class LoginBloc extends Bloc<Connectivity,bool> {
#override
bool get initialState => false;
#override
Stream<bool> mapEventToState(Connectivity event) async*{
// TODO: implement mapEventToState
switch(await event.checkConnectivity()){
case ConnectivityResult.mobile:
yield true;
break;
case ConnectivityResult.wifi:
yield true;
break;
case ConnectivityResult.none:
yield false;
}
}
}
class LoginWidget extends StatefulWidget {
#override
LoginWidgetState createState() {
return LoginWidgetState();
}
}
class LoginWidgetState extends State<LoginWidget> {
#override
Widget build(BuildContext context) {
//final _loginBloc = BlocProvider.of<LoginBloc>(context);
const oneSec = const Duration(seconds: 1);
//_loginBloc.add(ConnectivityResult.checkConnectivity());
new Timer.periodic(oneSec, (Timer t) => Connectivity().checkConnectivity());
return Scaffold(
body: BlocProvider(
builder: (BuildContext context) => LoginBloc(),
child: new Form(
key: _formKey,
child: ListView(
padding: EdgeInsets.only(top: 50.0),
children: <Widget>[
Image.asset(
'assets/images/XXXXX_logo.jpg',
height: 70,
width: 100,
alignment: Alignment.center,
),
_buildTextFields(),
_buildButtons(),
],
),
),
));
}
Widget _buildTextFields() {
return new Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0),
child: new Column(
children: <Widget>[
new Container(
child: new TextFormField(
controller: _userFilter,
decoration: new InputDecoration(labelText: 'Username'),
validator: (value) {
if (value.isEmpty) {
return 'Username cannot be empty';
}
return null;
},
),
),
new Container(
child: new TextFormField(
controller: _passwordFilter,
decoration: new InputDecoration(labelText: 'Password'),
obscureText: true,
validator: (value) {
if (value.isEmpty) {
return 'Password cannot be empty';
}
return null;
}),
)
],
),
);
}
Widget _buildButtons() {
return new Container(
child: new Column(
children: <Widget>[
new RaisedButton(
child: new Text('Login'),
onPressed: () {
if (_formKey.currentState.validate()) {
_loginPressed();
}
}),
new BlocBuilder<LoginBloc, bool>(
builder: (BuildContext context, bool state) {
return Container(
color: state ? Colors.greenAccent : Colors.redAccent,
padding: EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
state ? "Connected" : "No Internet Connection",
textAlign: TextAlign.center,
)
],
),
);
}),
],
),
);
}
As stated in the name of the function mapEventToState what actually does is in the response of an event added to the Bloc sink will be called and then you will be able to yield a state out, this will be received by the BlocBuilder in your UI when this happens, so in order to make this work as you expected you should probably create an Event, then instantiate your Bloc using for instance BlocProvider and dispatch an event from there.
So based on your code
final _loginBloc = BlocProvider.of<LoginBloc>(context);
_loginBloc.add(YourEvent());
Then in the mapEventToState
Stream<bool> mapEventToState(LoginEvent event) async*{
if (event is YourEvent) {
yield YourState();
}

Keyboard keeps disappearing while entering data in Flutter TextFormField

I've this simple login screen with two TextFormField for email and password. When I try to enter text in any of these text boxes, keyboard appears momentarily and disappears every time I focus on text field to enter data.
class LogInPage extends StatefulWidget {
final String title;
LogInPage({Key key, this.title}) : super(key: key);
#override
_LogInPageState createState() => new _LogInPageState();
}
class _LogInPageState extends State<LogInPage> {
static final formKey = new GlobalKey<FormState>();
String _email;
String _password;
Widget padded({Widget child}) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 8.0),
child: child,
);
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
padding: const EdgeInsets.all(16.0),
child: Column(children: [
Card(
child:
Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Container(
padding: const EdgeInsets.all(16.0),
child: Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
padded(
child: TextFormField(
key: Key('email'),
decoration: InputDecoration(labelText: 'Email'),
autocorrect: false,
validator: (val) => val.isEmpty
? 'Email can\'t be empty.'
: null,
onSaved: (val) => _email = val,
)),
padded(
child: TextFormField(
key: Key('password'),
decoration:
InputDecoration(labelText: 'Password'),
obscureText: true,
autocorrect: false,
validator: (val) => val.isEmpty
? 'Password can\'t be empty.'
: null,
onSaved: (val) => _password = val,
)),
]))),
])),
])));
}
}
This is the form:
EDIT
I think the problem lies in the way I'm calling this page like below. Is it okay to call another page from FutureBuilder ?
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("LogIn Demo"),
),
body: FutureBuilder<FirebaseUser>(
future: Provider.of<FireAuthService>(context).currentUser(),
builder: (context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.error != null) {
return Text(snapshot.error.toString());
}
return snapshot.hasData
? UserProfilePage(snapshot.data)
: LogInPage(title: 'Login');
} else {
return Container(
child: CircularProgressIndicator(),
);
}
},
),
);
}
Clean your code first and rebuild, perform testing with real device as well.
static GlobalKey<FormState> _formKey = new GlobalKey<FormState>();

Flutter : TextEditingController Only Static member can be accessed in initializer error

I'm passing data from listmosque.dart to update_screen.dart for updating data.
Anything it's work but i want set default value of textedittingcontroller.
TextEditingController _txtnrp = TextEditingController(text: "${widget.nrpUpdate}"); <- this error
I'm getting message Only static members can be accessed in initializers. in ${widget.nrpUpdate}
How to fix this ?
It's my update_screen.dart
import 'package:flutter/material.dart';
class UpdateScreen extends StatefulWidget {
final String idUpdate;
final String nrpUpdate;
final String namaUpdate;
final String emailUpdate;
final String jurusanUpdate;
UpdateScreen(
{this.idUpdate,
this.nrpUpdate,
this.namaUpdate,
this.emailUpdate,
this.jurusanUpdate});
#override
_UpdateScreenState createState() => _UpdateScreenState();
}
class _UpdateScreenState extends State<UpdateScreen> {
TextEditingController _txtnrp = TextEditingController(text: "${widget.nrpUpdate}"); <- In this line error
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_left),
onPressed: () => Navigator.pop(context, false),
),
centerTitle: true,
title: Text('Update ${widget.namaUpdate}'),
),
body: Container(
child: Center(
child: SingleChildScrollView(
padding: EdgeInsets.all(20),
scrollDirection: Axis.vertical,
child: Column(
children: <Widget>[
TextField(
controller: _txtnrp,
keyboardType: TextInputType.number,
decoration: InputDecoration(hintText: "NRP"),
textAlign: TextAlign.center,
),
],
),
),
),
),
);
}
}
How to fix this ?
Thanks
Move it inside initState:
class _UpdateScreenState extends State<UpdateScreen> {
TextEditingController _txtnrp;
#override
void initState() {
_txtnrp = TextEditingController(text: "${widget.nrpUpdate}");
super.initState();
}
Andrey's answer is correct, but just wanted to add some details:
Problem is indeed that you're trying to access State.widget before it is initialized, and accessing these parameters through State.widget is the recommended way (instead of passing through State constructor).
build() method is always called when state changes, so setting controller to ${widget.nrpUpdate} would actually make the text field behave strangely (resetting it) every time a state changed occured. So, that's another reason why you should set this value inside initState() instead. This, eg would clear the text field every time you clicked the button, even though that's not desired:
class _UpdateScreenState extends State<UpdateScreen> {
TextEditingController _txtnrp;
int _count = 1;
Widget GetColumn(someText) {
print(someText);
return
}
#override
void initState() {
_txtnrp = TextEditingController(text: "${widget.nrpUpdate}");
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_left),
onPressed: () => Navigator.pop(context, false),
),
centerTitle: true,
title: Text('Update ${widget.namaUpdate}'),
),
body: Container(
child: Center(
child: SingleChildScrollView(
padding: EdgeInsets.all(20),
scrollDirection: Axis.vertical,
child: Column(
children: <Widget>[
TextField(
controller: TextEditingController(text: someText),
keyboardType: TextInputType.number,
decoration: InputDecoration(hintText: "NRP"),
textAlign: TextAlign.center,
),
FlatButton(
child: Text("Click"),
onPressed: () => setState(() {_count += 1;})
),
],
),
),
),
),
);
}
}
Other than that, you might want to organize your code when it gets bigger, breaking it into functions and variables. When doing so, you could run into similar "only static objects" errors. One way to avoid this would be:
class _UpdateScreenState extends State<UpdateScreen> {
TextEditingController _txtnrp;
Widget getColumn(someText)
return Column(
children: <Widget>[
TextField(
controller: _txtnrp,
keyboardType: TextInputType.number,
decoration: InputDecoration(hintText: "NRP"),
textAlign: TextAlign.center,
),
],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: ...,
body: Container(
child: Center(
child: SingleChildScrollView(
padding: EdgeInsets.all(20),
scrollDirection: Axis.vertical,
child: GetColumn("${widget.nrpUpdate}").
),
),
),
),
);
#override
void initState() {
_txtnrp = TextEditingController(text: "${widget.nrpUpdate}");
super.initState();
}

How to bind a textfield's text to a simple text label in flutter

For the most straightforward solution:
class _GreetingPageState extends State<GreetingPage> {
final TextEditingController _nameController = new TextEditingController(text: 'Anonymous');
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hello World'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_name),
TextField(
autofocus: true,
controller: _nameController,
),
],
),
),
);
}
}
This does not work as expected, while typing in TextField, Text's content doesn't get updated.
I have to change to manage another state for Text:
class _GreetingPageState extends State<GreetingPage> {
String _name = '';
final TextEditingController _nameController = new TextEditingController(text: 'Anonymous');
#override
void initState() {
super.initState();
_nameController.addListener(
() {
setState(() {_name = _nameController.text;});
}
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hello World'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_name),
TextField(
autofocus: true,
controller: _nameController,
decoration: new InputDecoration(
hintText: 'Type your name',
),
),
],
),
),
);
}
}
This adds too much complexity for such a simple "bind a value to a text" situation.
I tried search for it, official document only give a use case where the _nameController.text is only used when a button is pressed, however I'd like to handle text update on user typing.
I think your problem is that until setState is not called the Text widget doesn't know it has to update. So you always need to call it.
You could do something like this:
class __GreetingPageStateState extends State<_GreetingPageState> {
final TextEditingController _nameController =
new TextEditingController(text: 'Anonymous');
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_nameController.text),
TextField(
autofocus: true,
controller: _nameController,
onChanged: (text) {
setState(() {});
}),
],
);
}
}