10

I am trying to solve form validation in my app. Every time I click to TextFromField, the focus is lost and keyboard hide. I found that problem is with "_formKey". But I need it to validation. How to solve it?

code snippet:

  class _TodoCreateDetailPageState extends State<TodoPage> {
  @override
  Widget build(BuildContext context) {
    final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();

    String _title = widget.todo == null ? "New TODO" : widget.todo.title;
    String _message;

    return new Scaffold(
      appBar: new AppBar(
        title: new Text(_title),
      ),
      floatingActionButton: new FloatingActionButton(
          child: new Icon(Icons.save), onPressed: null),
      body: new Padding(
          padding: new EdgeInsets.all(20.0),
          child: new Form(
              key: _formKey,
              child: new Column(
                children: <Widget>[
                  new TextField(
                    decoration: new InputDecoration(labelText: 'Title'),
                    onChanged: (String value) {
                      _title = value;
                    },
                  ),
                  new TextField(
                    decoration: new InputDecoration(labelText: 'Message'),
                    onChanged: (String value) {
                      _message = value;
                    },
                  )
                ],
              ))),
    );
  }
Stepan
  • 1,041
  • 5
  • 23
  • 35
  • I'm having the same problem. Did you ever work out what was happening and how to fix it? – Derek Lakin Jul 19 '18 at 13:15
  • 4
    This appears to be caused by the FormKey (https://github.com/flutter/flutter/issues/9282), which is actually essential for doing form validation. In my case, I wasn't using a StatefulWidget, which also appears to be a requirement for successfully using FormKey and a Form (I was using StatelessWidget and a view model). Having moved to StatefulWidget, the FormKey and focus works fine for me now. – Derek Lakin Jul 19 '18 at 13:23
  • You need to move _formKey outside your build method. Set that as a class level variable. – Hosar Apr 02 '19 at 20:24
  • Actually your build method creating every time a new `GlobalKey`. Initialize this `_formKey` before `build` method. – Abdul Qadir Aug 31 '21 at 23:53

5 Answers5

8

Change StatelessWidget to StatefulWidget works for me, as Derek Lakin said in comments.

Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
Will
  • 99
  • 1
  • 6
  • Did you try to `@override initState()` method and init `_title` and `_message` there, and on `onChanged` use `setState(String value) {_title = value}`? – Will Nov 08 '18 at 02:34
  • This won't fix it for the OP, but for someone Googling this and realizing that they can't have a Form/TextFormField in a Stateless widget will solve their problem. #facePalm This answer should be upvoted even if it isn't the accepted answer. – Rap Mar 26 '19 at 22:28
8

this issue is due to GlobalKey Initialization with in build()

@override
Widget build(BuildContext context){
    //here is the reason of losing focus.
   final GlobalKey<FormState> _formKey = new GlobalKey<FormState>()
}

Remove it from build and you are all good.

    final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
    //this will work fine when you move key outside the build();
    @override
    Widget build(BuildContext context){

    }
5

You seem to know that the problem is in the key itself, as in #6783. The solution is to avoid constructing your validation key every time. So, you may do it like this (or even make it a widget's property):

class _TodoCreateDetailPageState extends State<TodoPage> {
  final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    String _title = widget.todo == null ? "New TODO" : widget.todo.title;
    String _message;

    return new Scaffold(
      appBar: new AppBar(
        title: new Text(_title),
      ),
      floatingActionButton: new FloatingActionButton(
          child: new Icon(Icons.save), onPressed: null),
      body: new Padding(
          padding: new EdgeInsets.all(20.0),
          child: new Form(
              key: _formKey,
              child: new Column(
                children: <Widget>[
                  new TextField(
                    decoration: new InputDecoration(labelText: 'Title'),
                    onChanged: (String value) {
                      _title = value;
                    },
                  ),
                  new TextField(
                    decoration: new InputDecoration(labelText: 'Message'),
                    onChanged: (String value) {
                      _message = value;
                    },
                  )
                ],
              ))),
    );
  }
Smily
  • 2,732
  • 1
  • 15
  • 20
2

I had the same problem, and found a solution for that, just define a controller variable outside the build method. Use TextEditingController()

2

I think there's a much simpler solution that hasn't been mentioned yet and it has to do with the difference between GlobalKey and GlobalObjectKey.

A GlobalKey is only equal to itself and so each time your stateless widget is rebuilt, a new GlobalKey is created and the form is reset.

A GlobalObjectKey on the other hand is equal to any other GlobalObjectKey that has the same object:

GlobalObjectKey key1 = GlobalObjectKey('test');
GlobalObjectKey key2 = GlobalObjectKey('test');
key1 == key2; // True

So in this case, you should initialize your formKey as a GlobalObjectKey like this:

GlobalObjectKey<FormState> formKey = const GlobalObjectKey<FormState>('form');
Code on the Rocks
  • 11,488
  • 3
  • 53
  • 61