Почему Flutter перерисовывает виджет, который содержит TextFormField с собственным ключом?



    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!

Всем привет! Я изучаю флаттер дома, чтобы создать свое приложение, и столкнулся с непонятностью флаттера - когда я создаю форму с полями в любом виджете на любом маршруте навигатора, я видел перестроение этого маршрута при нажатии на поле формы и при закрытии клавиатуры. Если я удаляю GlobalKey и удаляю GlobalKey - виджет перестраивается при нажатии и скрывается клавиатура до сих пор, и в этой ситуации это не вызывает дискомфорта, но если я хочу наделить форму и эти поля глобальными ключами - при любом взаимодействии с полями, я вижу, что форма разрушает и строить снова и снова.



    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 createState() => _QuichState();
    }

    class _QuichState extends State {
      final controller = TextEditingController();
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Регистрация',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          initialRoute: Routes.splash,
          routes: {
            Routes.splash: (context) => Observer(builder: (_) {
              return SplashScreen();
            }),
            Routes.login: (context) {
              print('SUPER PARENT BUILD');
              return LoginScreen();
              final formKey = GlobalKey();
              final fieldKey = GlobalKey();
              return Scaffold(
                body: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Form(
                      child: Padding(
                        padding: const EdgeInsets.all(15.0),
                        child: TextFormField(
                          controller: controller,
                          key: fieldKey
                        ),
                      ),
                      key: formKey,
                    ),
                    ButtonBar(
                      children: [
                        MaterialButton(
                          child: Text('Проверка', style: TextStyle(color: Colors.white)),
                          onPressed: () => Navigator.of(context).pushNamed(Routes.splash),
                          color: Colors.lightBlue,
                        )
                      ],
                    )
                  ],
                ),
              );
            }
          },
        );
      }
    }

Может ли кто-нибудь помочь мне создать форму с полями с глобальными ключами внутри Navigator - я искал примеры с формами и навигацией, но формы с globalKeys в навигаторах не найдены мной;

UPD: Кажется, что редактор кода Stackru "ест" определения типов некоторых частей кода. Я прилагаю изображение с кодом, и, пожалуйста, посмотрите примеры видео:

Видео 1

Видео 2

Изображение с кодом

Ру-копия этой темы

PS Проблема решена, решение этого, если вам интересно - можете посмотреть на "PPS" блок русскоязычной копии этого вопроса: Russian Copy

2 ответа

В трепетании key Атрибут используется для сравнения существующего экземпляра виджета с новым экземпляром и определения того, что делать дальше: создать новое состояние или использовать существующее состояние, построить новое поддерево или повторно использовать существующее. Если ключ не указан, флаттер будет использовать местоположение виджета в дереве виджетов в качестве ключа. Если древовидная структура не сильно изменилась, вполне вероятно, что состояние или поддерево будет повторно использовано, если есть только небольшое изменение.

Идея о GlobalKey() будет то, что вы создадите один его экземпляр и, скорее всего, сохраните его где-то очень высоко в иерархии дерева приложений.

С помощью GlobalKey() можно повторно использовать состояние и поддерево виджета, если:

  • вы держите тот же экземпляр GlobalKey()
  • Ваш виджет не полностью удален из дерева. Если виджет удален, его состояние и поддерево исчезнут, и в следующий раз, когда он войдет в древовидное состояние вашего приложения, поддерево будет воссоздано.

В вашем примере кода вы не назначаете GlobalKey() к многоразовой переменной. В вашем случае новый экземпляр Globalkey() создается внутри вашей функции сборки. Это приводит к созданию нового уникального ключа при каждом обновлении. Новый уникальный ключ означает, что виджет не связан с предыдущим экземпляром виджета, поэтому состояние и поддерево не переносятся.

Я заметил, что вы не использовали правильный общий способ в расширении классов,

 class _QuichState extends State<Quich>{}

Попробуйте это, Далее я дам вам пример кода, чтобы вы могли соответствовать свой код из

   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'),
        ),
      ),
    ],
  ),
);

После этого создайте глобальный ключ, который будет однозначно идентифицировать виджет "Форма" и позволит нам проверить форму или другие действия.

Примечание: это GlobalKey, а не GlobalKey!

  final _formKey = GlobalKey<FormState>();
Другие вопросы по тегам