Без состояния или с сохранением состояния?

#flutter

Вопрос:

Я попытался создать простую форму для проверки входных данных пользователя.

 class UserForm extends StatelessWidget {
    final _formKey = GlobalKey<FormState>();
    final TextEditingController usernameController = TextEditingController();
    
    UserForm({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
        return Form(
            key: _formKey,
            child: Column(
                TextFormField(
                    controller: usernameController,
                    validator: (value) {
                        if (value == null || value.isEmpty) {
                            return 'Please enter something';
                        }
                        return null;
                    },
                    autovalidateMode: AutovalidateMode.onUserInteraction,
                ),
                ElevatedButton(
                    onPressed: () {
                        print(usernameController.text);
                    },
                    child: const Text('Check'),
                ),
            ),
        );
    }
}
 

Когда я что-то печатаю и удаляю это после, я получаю сообщение об ошибке «Пожалуйста, введите что-то в текстовое поле».

Я видел официальный демонстрационный пример с проверкой формы здесь: https://flutter.dev/docs/cookbook/forms/validation .

Теперь мой вопрос: зачем нам нужен StatefulWidget для формы, если сообщение об ошибке отображается в StatelessWidget. Изменяется значение TextFormField, а не пользовательский виджет формы, который я создал.

Может кто-нибудь объяснить мне, когда использовать состояние без состояния, а когда состояние?

Комментарии:

1. Текстовые поля должны быть в виджете с полным состоянием

2. @GHPrakash Хорошо, но почему?

3. Ваш вопрос о том, почему в документации использовался виджет с сохранением состояния для форм или можно ли использовать текстовые поля в Statelesswidget?

4. @Balaji Вопрос в следующем: зачем вам использовать виджет с сохранением состояния для формы, если все, что вы делаете, это ввод данных в некоторые поля текстовой формы и их проверка?

5. @Ghost потому что текстовые данные хранятся внутри TextEditingController, а не сами поля, когда вы предоставляете параметр контроллера. И виджет без состояния потеряет данные контроллера при перестройке дерева. Я написал подробный ответ ниже, если вам интересно

Ответ №1:

На самом деле вы можете использовать виджет без состояния или с сохранением состояния для размещения ваших виджетов формы. Состояние будет сохранено в самих этих полях.

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

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

Также TextEditingController может привести к утечкам памяти в вашем приложении, поэтому рекомендуется уничтожать их после использования. Вы можете сделать это, переопределив dispose метод State класса.

 class MyWidgetState extends State<MyWidget> {
  final controller = TextEditingController();

  @override
  void dispose() {
    super.dispose();
    controller.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // ...
  }
}
 

Заключение

Используйте State class всякий раз, когда вам нужно сохранить значение / переменную. Класс widget будет создаваться заново после каждой сборки, поэтому все значения внутри него будут потеряны (или вызовут утечку памяти, если не будут очищены надлежащим образом) и заменены новым экземпляром. Обновление происходит нерегулярно, поэтому вероятность этого невелика, но если родительский виджет, на котором размещен ваш виджет формы, изменит свое состояние, то обновления повлияют на виджет формы, который также будет перестроен.

В общем, виджеты — это просто каркас для вашего дерева виджетов. Они должны просто содержать неизменяемые данные, и когда вам нужно их изменить, вы должны использовать mutable State или использовать другой подход к управлению состоянием. При использовании StatefulWidget экземпляр вашего State класса будет перенесен в «следующий кадр», где новый экземпляр вашего виджета может использовать состояние, в котором хранятся данные из «предыдущего кадра» для отображения пользовательского интерфейса.

Ссылки

Проверьте ссылки ниже, если вам интересно узнать больше:

Ответ №2:

Виджеты без состояния или с сохранением состояния — все зависит от ваших требований. Беря ваш пример, если ваше требование заключается только в проверке входного текста, то ваш пример идеален. Подумайте, если вы хотите отобразить введенный текст в текстовом виджете, тогда вам следует перейти к виджету с сохранением состояния.

В итоге, если вы хотите изменить пользовательский интерфейс в соответствии с взаимодействием с пользователем, используйте виджет с сохранением состояния и просто для отображения вещей без обработки изменений используйте виджет без состояния, такой как текст, значок и т. Д. Официальная документация

Тогда вы можете задаться вопросом, как средство проверки в вашем примере отображает ошибку, потому что оно находится внутри виджета без состояния. Это потому TextFormField , что это statefulwidget и изменяет пользовательский интерфейс в соответствии с взаимодействием с пользователем.

официальная документация показывает form в состоянии, потому что в большинстве случаев формы должны быть в виджете с сохранением состояния, потому что необходимо изменить пользовательские интерфейсы в соответствии с вводимыми пользователем данными. Ключи формы — еще одна важная причина использования виджета с сохранением состояния. Без виджета с сохранением состояния сложно однозначно идентифицировать форму в дереве виджетов с помощью ключей формы.

Ответ №3:

Он должен быть с сохранением состояния, поскольку речь идет не только о TextFormField , но у нас обычно есть и другие виджеты, которые enable/disable основаны на значениях TextFormField contains или нет.

Пример:

 bool _btnEnabled = false;


  TextFormField(

    ...
    autovalidate: true,
    validator: (String txt){
      if (txt.length == 10){
        setState((){
          _btnEnabled = true;
        });
      } else {
        setState((){
          _btnEnabled = false;
        });
      }
    }

    ...

  FlatButton(
    onPressed: _btnEnabled == true ? yourCallback : null,
    child: ...