19

I have a TextFormField that reloads the current screen when I tap on it to enter text. When I tap on the formfield the software keyboard is displayed briefly before the entire screen reloads and renders all the widgets again. I am running the app on an Android device.

Container(
        child: Form(
          key: _formKey,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              TextFormField(
                validator: (value) {
                  if (value.isEmpty) {
                    return 'Your input cannot be empty';
                  }
                },
              ),
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 16.0),
                child: RaisedButton(
                  onPressed: () {

                    if (_formKey.currentState.validate()) {
                      print('validated');
                    }
                  },
                  child: Text('Save'),
                ),
              ),
            ],
          ),
        ),
        margin: EdgeInsets.only(top:8.0),
  ),
Amit Maraj
  • 241
  • 3
  • 8

5 Answers5

2

The problem is that the controller of the TextFormField is rebuild when you click on the field, and that's the reason of your issue.

So to solve that, did you try to create a Statefull widget and then creating a TextEditingController in the State of this widget and passing it as an argument to the TextFormField ?

Gaspard Merten
  • 1,063
  • 7
  • 14
  • This solved it for me. Just convert to stateful widget and make sure that the `TextEditingController` is created in the state. Although I am not 100% I understand why the `TextEditingController` gets rebuilt and reinitialized to its original value... making it the widget stateful fixes it, thanks – lenz May 11 '21 at 05:30
  • 1
    That’s because a Stateless widget will be completely re-build whenever its parent call the build method. When I say completely I mean that the whole object is re-instantiated and by doing so, all its fields are re-created... it’s also true for the StatefullWidget class but not for its « child » class, the State class in which I you actually declare your build method which does not get re-instantiated! You can find great documentation, articles and videos on the web for that matter ! – Gaspard Merten May 12 '21 at 08:30
  • That's helpful clarification. I will look it up further, thanks! – lenz May 12 '21 at 18:03
1

I had the same Problem. this was my code

class MainPage extends StatefulWidget {
  @override
  _MainPageState createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  Model model = Model();

  @override
  Widget build(BuildContext context) {
  GlobalKey<FormState> _formKey = GlobalKey<FormState>();
    var mediaWidth = MediaQuery.of(context).size.width / 2.0;
    return Scaffold(
...

and I solved this problem by declaring the _formKey outside of build method. and this worked for me.

class MainPage extends StatefulWidget {
  @override
  _MainPageState createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  Model model = Model();
  GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    var mediaWidth = MediaQuery.of(context).size.width / 2.0;
    return Scaffold(
...

hope it will help you

0

Yes, that happens because when the keyboard appears, the flutter scaffold gets resize to the current available screen size. So, we can easily handle this by preventing the scaffold size change. I suggest to set scaffold resizeToAvoidBottomInset property false. If it's true the body and the scaffolds floating widgets should size themselves to avoid the onscreen keyboard whose height is defined by the ambient MediaQuery's, MediaQueryData,viewInsets bottom property.

Solution:

resizeToAvoidBottomInset: false,

Complete example:

@override
Widget build(BuildContext context) {
  setDisplayData();
  return Scaffold(
    resizeToAvoidBottomInset: false,
    appBar: getAppBar(),
    body: OrientationBuilder(
      builder: (context, orientation) {
        return orientation == Orientation.portrait
            ? _buildVerticalLayout()
            : _buildHorizontalLayout();
      },
    ),
  );
David Buck
  • 3,752
  • 35
  • 31
  • 35
0

Check if you are using MediaQueries wrongly in your project, I had similar issue and it stopped when I changed the MediaQuery in my case:

Size _size = MediaQuery.of(context).size;

removing this piece of code fixed my app.

Tarish
  • 468
  • 8
  • 8
0

When TextFormField focused the size of screen will changed because of the appearance of keyboard, that cause rebuild of state, you cant prevent re-build of state.

Instead of trying prevent re-build state, you need to solve problems which happen when state do re-build, one of common problem is declaration and initialization variables inside build(BuildContext context){ ... }' function.

The main problem, when you need to get some data related of context (like size of screen), in this case I prefer to pass this value from parent Widget...

For example this code will cause problem when re-build state:

  @override
  Widget build(BuildContext context) {
    double? _screenHeight =  MediaQuery.of(context).size.height;
    return Container();
  }

To solve problem get _screenHeight from parent, to know how to do that look at https://stackoverflow.com/a/50289032/2877427

AnasSafi
  • 5,353
  • 1
  • 35
  • 38