Why Flutter rerenders widget which contains a TextFormField with own key?

2,653

Solution 1

In flutter key attribute is used to compare existing instance of a widget to new instance and decide what to do next: create new state or use existing state, build new sub-tree or reuse existing. If a key is not given flutter will use widget's location in your widget tree as a key. If tree structure didn't change too much it's good chance that state or sub-tree would be reused if there is only a small change.

Idea of GlobalKey() would be that you create one instance of it and save it most probably somewhere very high up in your application tree hierarchy.

With help of GlobalKey() it's possible to reuse widget's state and subtree as long as:

  • you keep same instance of GlobalKey()
  • your widget is not removed completely from the tree. If widget is removed it's state and subtree will be gone, and next time it enters your application's tree state and subtree will be recreated.

In your code sample, you are not assigning GlobalKey() to a reusable variable. In your case new instance of Globalkey() is created inside your build function. This results in new unique key being created on every update. New unique key means that widget is not linked to previous instance of widget and so no state and no sub-tree is carried over.

Solution 2

i observe that you didn't use proper generic way in extending classes,

 class _QuichState extends State<Quich>{}

Try this one, Next am giving you a sample code , so that you can match your code from

   return Form(
  key: _formKey,
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: <Widget>[
      TextFormField(
        validator: (value) {
          if (value.isEmpty) {
            return 'Please enter some text';
          }
        },
      ),
      Padding(
        padding: const EdgeInsets.symmetric(vertical: 16.0),
        child: RaisedButton(
          onPressed: () {
            // Validate will return true if the form is valid, or false if
            // the form is invalid.
            if (_formKey.currentState.validate()) {
              // If the form is valid, we want to show a Snackbar
              Scaffold.of(context)
                  .showSnackBar(SnackBar(content: Text('Processing Data')));
            }
          },
          child: Text('Submit'),
        ),
      ),
    ],
  ),
);

After this, Create a global key that will uniquely identify the Form widget and allow us to validate the form or other activities

Note: This is a GlobalKey, not a GlobalKey!

  final _formKey = GlobalKey<FormState>();
Share:
2,653
Михаил А.
Author by

Михаил А.

Updated on December 09, 2022

Comments

  • Михаил А.
    Михаил А. over 1 year
    Doctor summary (to see all details, run flutter doctor -v):
    [v] Flutter (Channel dev, v1.2.0, on Microsoft Windows [Version 10.0.17763.253], locale ru-RU)
    [v] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
    [v] Android Studio (version 3.3)
    [v] VS Code, 64-bit edition (version 1.30.2)
    [v] Connected device (1 available)
    
    • No issues found!
    

    i'm learning flutter at home to create my app and faced with incomprehensibility of flutter; when I create a form with fields in any widget at any route of navigator, I have seen a rebuild of this route when tap at formfield and when close a keyboard. If I remove GlobalKey<FormState> and remove GlobalKey<FormFieldState> - widget do rebuild at tap and hide keyboard still and in this situation it not discomfort, but if I want to endow form and this fields with globalKeys - at any interaction with fields I see that form is destroying and building again and again.

    import 'package:flutter/material.dart';
    import 'package:flutter_mobx/flutter_mobx.dart';
    import 'package:quich/controllers/user_controller.dart';
    import 'package:quich/route/routes.dart';
    import 'package:quich/screens/login_screen.dart';
    import 'package:quich/screens/splash_screen.dart';
    import 'package:quich/store/app_store.dart';
    
    void main() async {
      runApp(Quich());
      await $store.storage.ready;
      var uc = UserController();
      var isValid = await uc.isTokenValid(token: 'token');
      $store.isAuth = isValid;
      $store.isLoading = false;
    }
    
    class Quich extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => _QuichState();
    }
    
    class _QuichState extends State<Quich> {
      final controller = TextEditingController();
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Регистрация',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          initialRoute: Routes.splash,
          routes: <String, WidgetBuilder>{
            Routes.splash: (context) => Observer(builder: (_) {
              return SplashScreen();
            }),
            Routes.login: (context) {
              print('SUPER PARENT BUILD');
              return LoginScreen();
              final formKey = GlobalKey<FormState>();
              final fieldKey = GlobalKey<FormFieldState>();
              return Scaffold(
                body: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Form(
                      child: Padding(
                        padding: const EdgeInsets.all(15.0),
                        child: TextFormField(
                          controller: controller,
                          key: fieldKey
                        ),
                      ),
                      key: formKey,
                    ),
                    ButtonBar(
                      children: <Widget>[
                        MaterialButton(
                          child: Text('Проверка', style: TextStyle(color: Colors.white)),
                          onPressed: () => Navigator.of(context).pushNamed(Routes.splash),
                          color: Colors.lightBlue,
                        )
                      ],
                    )
                  ],
                ),
              );
            }
          },
        );
      }
    }
    

    Can any help me to create a form with fields with global keys at inner of Navigator - I searched examples with forms and navigation, but forms with globalKeys in navigators not found by me.

    UPD: It seems that StackOverflow Code editor "eat" types definitions of some code parts. I attach a image with code, and please see video examples:

    Video 1

    Video 2

    Image with code

    Ru-copy of this topic

    P.S. Problem is solved, the solution of this, if you are interesting - can see at "P.P.S" block of Russian-language copy of this question: Russian Copy

  • Михаил А.
    Михаил А. about 5 years
    I tried to use GlobalKey as a global variable, that persist at upper level that this form, but in this situation on every tap at formField flutter throw a error. See example - link
  • Михаил А.
    Михаил А. about 5 years
    Sorry for misleading, but class extending is correct in real ( class _QuichState extends State<Quich>{} ) - i removed the excess part at this code example but in video is full code. And i dont understand, what you mean at "Note: This is a GlobalKey, not a GlobalKey!"
  • Ski
    Ski about 5 years
    you can only use one GlobalKey() instance for one widget, can't use it for more than one widget at a time. I can see that you are using the same global key in both: Form and also TextForrmField.
  • Михаил А.
    Михаил А. about 5 years
    Ok, i understand that you mean, it seems that StackOverflow code editor "eat" some parts of code... in real code globalKeys is different, of course: GlobalKey<FormState> and GlobalKey<FormField>, please see both examples of video to see that
  • Scaphae Studio
    Scaphae Studio about 5 years
    yeah, we can use both ways, but using generics is preferable. next thing about GlobalKey is to inform others to use this way, as some people use GlobalKey! causing error.