Как отключить кнопку, пока поле текстовой формы Flutter не будет иметь действительные данные

Я хочу отключить кнопку, пока поле текстовой формы не станет действительным. И затем, как только данные действительны, кнопка должна быть включена. Я получил помощь по SO ранее с аналогичным вопросом, но, похоже, не могу применить то, что я узнал, к этой проблеме. Данные действительны, когда пользователь добавляет более 3 символов и менее 20. Я создал логическое значение (_isValidated) и добавил его в метод validateUserName, вызывающий setState после того, как пользователь ввел действительные данные, но это определенно неправильно и генерирует сообщение об ошибке. . Сообщение об ошибке:

setState() или markNeedsBuild(), вызываемые во время сборки.

Я не могу понять, что я делаю неправильно. Любая помощь будет оценена по достоинству. Спасибо.

class CreateUserNamePage extends StatefulWidget {
  const CreateUserNamePage({
    Key? key,
  }) : super(key: key);

  @override
  _CreateUserNamePageState createState() => _CreateUserNamePageState();
}

class _CreateUserNamePageState extends State<CreateUserNamePage> {
  final _formKey = GlobalKey<FormState>();
  bool _isValidated = false;
  late String userName;
  final TextEditingController _userNameController = TextEditingController();

  @override
  void initState() {
    super.initState();
    _userNameController.addListener(() {
      setState(() {});
    });
  }

  void _clearUserNameTextField() {
    setState(() {
      _userNameController.clear();
    });
  }

  String? _validateUserName(value) {
    if (value!.isEmpty) {
      return ValidatorString.userNameRequired;
    } else if (value.trim().length < 3) {
      return ValidatorString.userNameTooShort;
    } else if (value.trim().length > 20) {
      return ValidatorString.userNameTooLong;
    } else {
      setState(() {
        _isValidated = true;
      });
      return null;
    }
  }

  void _createNewUserName() {
    final form = _formKey.currentState;
    if (form!.validate()) {
      form.save();
    }
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('Welcome $userName'),
      ),
    );
    Timer(const Duration(seconds: 2), () {
      Navigator.pop(context, userName);
    });
  }

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

  @override
  Widget build(BuildContext context) {
    final isPortrait =
        MediaQuery.of(context).orientation == Orientation.portrait;
    final screenHeight = MediaQuery.of(context).size.height;
    return WillPopScope(
      onWillPop: () async => false,
      child: Scaffold(
        appBar: CreateUserNameAppBar(
          preferredAppBarSize:
              isPortrait ? screenHeight / 15.07 : screenHeight / 6.96,
        ),
        body: ListView(
          children: [
            Column(
              children: [
                const CreateUserNamePageHeading(),
                CreateUserNameTextFieldTwo(
                  userNameController: _userNameController,
                  createUserFormKey: _formKey,
                  onSaved: (value) => userName = value as String,
                  suffixIcon: _userNameController.text.isEmpty
                      ? const EmptyContainer()
                      : ClearTextFieldIconButton(
                          onPressed: _clearUserNameTextField,
                        ),
                  validator: _validateUserName,
                ),
                CreateUserNameButton(
                  buttonText: ButtonString.createUserName,
                  onPressed: _isValidated ? _createNewUserName : null,
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

Вы можете использовать виджет, называемый поглощающим указателем, который указывает на функцию bool

Arbiter Chil 09.04.2022 18:11
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
0
1
35
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я думаю, что лучший способ — присвоить нулевое значение параметру onPressed кнопки. Пожалуйста, проверьте ссылку ниже. https://www.flutterbeads.com/disable-button-in-flutter/

Спасибо за ссылку @Archer. Я просмотрю его сейчас и посмотрю, поможет ли он мне в моей проблеме.

Carleton Y 09.04.2022 18:54

У вас есть пользовательские виджеты, поэтому я не знаю, как работают ваши виджеты, но здесь вы можете использовать AbsorbPointer, чтобы отключить кнопку и проверить текст поля текстовой формы в параметре onChange, как здесь;

  bool isDisabled = true;

  String _validateUserName(value) {
    if (value!.isEmpty) {
      return ValidatorString.userNameRequired;
    } else if (value.trim().length < 3) {
      return ValidatorString.userNameTooShort;
    } else if (value.trim().length > 20) {
      return ValidatorString.userNameTooLong;
    } else {
      setState(() {
        isDisabled = false;
      });
      return null;
    }
  }
  @override
  Widget build(BuildContext context) {
    final ButtonStyle style =
        ElevatedButton.styleFrom(textStyle: const TextStyle(fontSize: 20));

    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          ElevatedButton(
            style: style,
            onPressed: null,
            child: const Text('Disabled'),
          ),
          const SizedBox(height: 30),
          TextFormField(
            decoration: const InputDecoration(
              labelText: 'Label',
            ),
            onChanged: (String value) {
              _validateUserName(value);
            },
          ),
          AbsorbPointer(
            absorbing: isDisabled, // by default is true
            child: TextButton(
              onPressed: () {},
              child: Text("Button Click!!!"),
            ),
          ),
        ],
      ),
    );
  }

Я буду читать об AbsorbPointer, так как никогда о нем не слышал. Спасибо за ответ. Это может занять некоторое время, так как я все еще учусь.

Carleton Y 09.04.2022 18:52
Ответ принят как подходящий

Просто используйте проверку формы, внутри TextFormField отредактируйте функцию валидатора, добавьте функцию onChange и вызовите setState, чтобы получить inputtedValue, который также может держать кнопку отключения, если форма не проверена.

Ключевые моменты, на которые следует обратить внимание:

  1. Используйте final _formKey = GlobalKey<FormState>();
  2. String? inputtedValue; и !userInteracts() — это трюки, вы можете обратиться к коду;
  3. Когда метод ElevatedButton onPressed имеет значение null, кнопка будет отключена. Просто выполните условие !userInteracts() || _formKey.currentState == null || !_formKey.currentState!.validate()

Код здесь:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: _title,
      home: MyCustomForm(),
    );
  }
}

class MyCustomForm extends StatefulWidget {
  const MyCustomForm({Key? key}) : super(key: key);

  @override
  MyCustomFormState createState() {
    return MyCustomFormState();
  }
}

// Create a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
  // Create a global key that uniquely identifies the Form widget
  // and allows validation of the form.
  //
  // Note: This is a GlobalKey<FormState>,
  // not a GlobalKey<MyCustomFormState>.
  final _formKey = GlobalKey<FormState>();

  // recording fieldInput
  String? inputtedValue;

  // you can add more fields if needed
  bool userInteracts() => inputtedValue != null;

  @override
  Widget build(BuildContext context) {
    // Build a Form widget using the _formKey created above.
    return Scaffold(
      appBar: AppBar(
        title: const Text('Form Disable Button Demo'),
      ),
      body: Form(
        key: _formKey,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            TextFormField(
              // The validator receives the text that the user has entered.
              validator: (value) {
                if (inputtedValue != null && (value == null || value.isEmpty)) {
                  return 'Please enter some text';
                }
                return null;
              },
              onChanged: (value) => setState(() => inputtedValue = value),
            ),
            Padding(
              padding: const EdgeInsets.symmetric(vertical: 16.0),
              child: ElevatedButton(
                // return null will disable the button
                // Validate returns true if the form is valid, or false otherwise.
                onPressed: !userInteracts() || _formKey.currentState == null || !_formKey.currentState!.validate() ? null :() {
                  // If the form is valid, display a snackbar. In the real world,
                  // you'd often call a server or save the information in a database.
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('Processing Data: ' + inputtedValue!)),
                  );
                },
                child: const Text('Submit'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Другие вопросы по теме