5

Pretty much what I describe in the title. I have a pile of TextFormFields populated from Firebase when the app launches.

The user should be able to update these, and when they are done, click a submit button to update the database. The database code all works, however there is some bug which works as follows:

TextFormField1:   "New Text Entered"
TextFormField2:   "Some more text"
TextFormField3:   "Another update here"

Now we get to a point where we need to dismiss the keyboard, so that we can see the submit button underneath. As soon as you click the little down arrow to dismiss the keyboard, all the changes above revert back to their original state.

Anyone seen this?

I am prepopulating the data in these fields at runtime, and you can edit and update the text, and it all works fine... except if you minimise the keyboard.

Please tell me that Flutter isn't doing something fundamentally stupid like reloading the widget underneath from scratch every time you ask the keyboard to go away...... It sort of feels like it is.

Bisclavret
  • 1,327
  • 9
  • 37
  • 65

3 Answers3

5

This is my solution: move the TextEditingController variable from the inside of the "build" method to the outside of the "build" method. Ref in pic The solution

Tom.NT
  • 51
  • 1
  • 2
3

Yes. It happens to me all the time. It is because the screen rebuilds when the bottom insets (due to keyboard) changes.

  1. Enclose the TextFormField(s) inside a Form and give it a global key.
  2. Use a local variable to store the value of the TextFormField. Update it in onChanged method. All done!

I shall attach a code for easiness.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: LoginScreen(),
    );
  }
}

// Login Screen
class LoginScreen extends StatefulWidget {
  @override
  _LoginScreenState createState() => _LoginScreenState();
  static GlobalKey<FormState> _loginScreenFormKey = GlobalKey<FormState>();
}

class _LoginScreenState extends State<LoginScreen> {
  String username;
  String password;

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Form(
          key: LoginScreen._loginScreenFormKey,
          child: Column(
            children: [
              TextFormField(
                decoration: InputDecoration(
                  hintText: 'Enter username',
                ),
                onChanged: (value) {
                  setState(() {
                    username = value;
                  });
                },
              ),
              TextFormField(
                decoration: InputDecoration(
                  hintText: 'Enter username',
                ),
                onChanged: (value) {
                  setState(() {
                    password = value;
                  });
                },
                obscureText: true,
              ),
              RaisedButton(
                onPressed: () {
                  LoginScreen._loginScreenFormKey.currentState.save();
                },
                child: Text('submit'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Abhay V Ashokan
  • 320
  • 3
  • 9
  • Hi Abhay, I am trying to use your solution, but it fails and tells me "Multiple widgets used the same GlobalKey.". I am using a GlobalKey for my Sign in and Register forms. Am I not allowed to use a GlobalKey anywhere else in the app? – Bisclavret May 26 '20 at 09:03
  • This shouldn't happen. Usually LoginScreen.keyName and RegistrationScreen.keyName are recognized as two separate keys. That is, the key used for Login should not be used for Registration. A new key must be defined in the similar fashion in the Registration Screen Class. Try changing the names of both keys if the problem still persists.Else there might be some issue of redirecting between the widgets. I might want to see more code to understand that part. – Abhay V Ashokan May 26 '20 at 13:57
  • ``` class RegistrationScreen extends StatefulWidget { @override _RegistrationScreenState createState() => _RegistrationScreenState(); static GlobalKey _registrationScreenFormKey = GlobalKey(); }``` – Abhay V Ashokan May 26 '20 at 14:02
  • If the problem still persists. Just let me know. I shall edit my answer to include registration screen too. – Abhay V Ashokan May 26 '20 at 14:03
  • To let you know, the form works pretty well without the key too. – Abhay V Ashokan May 26 '20 at 14:29
  • Hi Abhay, if I don't use a key, the form still clears when I minimise the keyboard. I am still unable to use a key as it says I am already using a global key but I will try your new suggestions shortly. – Bisclavret Jun 06 '20 at 03:46
  • Hi Abhay, When I try your code I get "multiple widgets used the same GlobalKey", even though they do not, my other classes have different key names. Seems like just more of the same sloppy coding by Flutter, but not sure how to resolve this. – Bisclavret Jun 14 '20 at 02:28
  • Also, not sure if it's related but the original issue posed by this question still exists. If I start to fill out the TextFields with information, if I minimise the keyboard before I have hit the save to Firebase button, everything disappears. Whether I use a global key or not. – Bisclavret Jun 14 '20 at 02:32
  • One thing I have noticed is that when I minimise the keyboard one time, I get the Multiple Widgets used the same Globalkey message twice. So I am not sure how they are doing their error reporting here, but it is either saying it is in two widgets or three. The only thing I can think is that I assign my key for a form that is a child widget. I mean, it's still an issue with the platform being broken, as always, but not sure if this is causing it to trigger the issue. I can try move it back to the main build function, just as a test, but I am not sure I can leave it there. – Bisclavret Jun 14 '20 at 03:04
  • Accepting this as the answer because the trick in the end was to not use any globalkeys because the platform can't get it right. Instead, the reason the form fields seemed to be clearing was because I was using TextEditingControllers. This should be fine, but I found when using the onChanged method instead, the data no longer clears for some reason when I minimise keyboard. So.. yeah... a weird one but maybe this might help someone else. Thank you, Abhay. – Bisclavret Jun 14 '20 at 05:11
0

The class that includes those TextFormFields should extends State of StatefulWidget, the local state will be cleared if the dismiss of keyboard causes those fields re-render, hence you need StatefulWidget to save the local state so that it won't be re-rendered

Convert you StatelessWidget to StatefulWidget.

V-SHY
  • 3,925
  • 4
  • 31
  • 47