颤振步进器小部件 - 在各个步骤中验证字段

Posted

技术标签:

【中文标题】颤振步进器小部件 - 在各个步骤中验证字段【英文标题】:flutter stepper widget - validating fields in individual steps 【发布时间】:2018-12-16 06:52:41 【问题描述】:

我正在使用步进器小部件从用户那里收集信息并对其进行验证,我需要在每个步骤中调用 API,因此在每个继续按钮的步骤中验证每个字段...我正在使用表单状态和表单小部件但问题是它验证步进器中所有步骤中的整个字段......我如何仅验证步进器中的单个步骤?我浏览了 stepper.dart 中的 Stepper 和 State 类中的文档,但那里没有支持功能

下面是代码

class SubmitPayment extends StatefulWidget 


 SubmitPayment(Key key, this.identifier, this.amount, this.onResendPressed)
      : super(key: key);

  final String identifier;
  final String amount;
  final VoidCallback onResendPressed;

  @override
  State<StatefulWidget> createState() 
    return _SubmitPaymentState();
  


class _SubmitPaymentState extends State<SubmitPayment> 
  final GlobalKey<FormState> _formKeyOtp = GlobalKey<FormState>();
  final FocusNode _otpFocusNode = FocusNode();
  final TextEditingController _otpController = TextEditingController();
  bool _isOTPRequired = false;

  @override
  Widget build(BuildContext context) 
    return Padding(
      padding: const EdgeInsets.only(top: 8.0),
      child: Form(
          key: _formKeyOtp,
          child: Column(children: <Widget>[
            Center(
                child: Padding(
                    padding:
                        EdgeInsets.symmetric(horizontal: 16.0, vertical: 5.0),
                    child: Text(
                      Translations.of(context).helpLabelOTP,
                      style: TextStyle(
                          color: Theme.of(context).primaryColor,
                          fontStyle: FontStyle.italic),
                    ))),
            CustomTextField(
              icon: Icons.***_key,
              focusNode: _otpFocusNode,
              hintText: Translations.of(context).otp,
              labelText: Translations.of(context).otp,
              controller: _otpController,
              keyboardType: TextInputType.number,
              hasError: _isOTPRequired,
              validator: (String t) => _validateOTP(t),
              maxLength: AppConstants.otpLength,
              obscureText: true,
            ),
            Center(
                child: ButtonBar(
              mainAxisSize: MainAxisSize.max,
              alignment: MainAxisAlignment.center,
              children: <Widget>[
                RaisedButton(
                  child: Text(Translations.of(context).resendOtpButton),
                  color: Colors.white,
                  textColor: Theme.of(context).primaryColor,
                  onPressed: widget.onResendPressed,
                ),
                RaisedButton(
                  child: Text(
                    Translations.of(context).payButton,
                  ),
                  onPressed: _doPullPayment,
                ),
              ],
            )),
          ])),
    );
  

  String _validateOTP(String value) 
    if (value.isEmpty || value.length < AppConstants.otpLength) 
      setState(() => _isOTPRequired = true);
      return Translations.of(context).invalidOtp;
    
    return "";
  

  bool _validateOtpForm() 
    _formKeyOtp.currentState.save();
    return this._formKeyOtp.currentState.validate();
  

  Future<void> _doPullPayment() async 
    setState(() 
      _isOTPRequired = false;
    );

    if (!_validateOtpForm()) return false;

    try 
      setState(() 
        _isOTPRequired = false;
      );
      showDialog(
        barrierDismissible: false,
        context: context,
        builder: (context) => AlertDialog(
              content: ListTile(
                leading: CircularProgressIndicator(),
                title: Text(Translations.of(context).processingPaymentDialog),
              ),
            ),
      );

      TransactionApi api =
          TransactionApi(httpDataSource, authenticator.sessionToken);
      String responseMessage = await api.doPullPayment(
          widget.identifier,
          widget.amount,
          _otpController.text,
          TransactionConstants.transactionCurrency);

      Navigator.of(context).pop();
      await showAlertDialog(
          context, Translations.of(context).pullPayment, '$responseMessage');
      Navigator.pop(context);
     catch (exception) 
      await showAlertDialog(context, Translations.of(context).pullPayment,
          '$exception.message');
      Navigator.of(context).pop();
    
  

【问题讨论】:

也没有找到是否可以在 form.dart 中的单个表单字段中检查验证 【参考方案1】:

为每个步骤使用单独的Form。使用GlobalKey&lt;FormState&gt; 的列表,您可以根据_currentStep 对其进行索引,然后在onStepContinue 中调用validate()

List<GlobalKey<FormState>> _formKeys = [GlobalKey<FormState>(), GlobalKey<FormState>(), …];
…

Stepper(
  currentStep: _currentStep,
  onStepContinue: () 
    setState(() 
      if (_formKeys[_currentStep].currentState.validate()) 
        _currentStep++;
      
    );
  ,
  steps: 
  Step(
    child: Form(key: _formKeys[0], child: …),

显然,您需要检查最后一步和save,而不是最后的validate :)

【讨论】:

我只是这样做,问题出在 validate 函数中,它返回一个空字符串而不是导致问题的 null,请查看我的答案以获取详细信息 正如您所推断的(如下),您需要从 validate 方法返回 null,而不是空字符串。如果您没有明确的 return 语句,这将达到相同的最终结果。【参考方案2】:

所以我解决了这个问题:

问题是,如果我的逻辑有效,我将返回一个 *empty string ("") *,其中 validate 方法是 FormState 期望与 TextFormField 关联的每个验证器方法在验证通过时返回 null

我改变了以下

 String _validateOTP(String value) 
    if (value.isEmpty || value.length < AppConstants.otpLength) 
      setState(() => _isOTPRequired = true);
      return Translations.of(context).invalidOtp;
    
    return "";
  

  String _validateOTP(String value) 
if (value.isEmpty || value.length < AppConstants.otpLength) 
  setState(() => _isOTPRequired = true);
  return Translations.of(context).invalidOtp;

return null;

然后一切正常。

详情请参考link "If there is an error with the information the user has provided, the validator function must return a String containing an error message. If there are no errors, the function should not return anything."

【讨论】:

您可以通过在有效情况下不包含任何 return 语句来实现相同的最终结果(即只需删除 return null; 部分):) @DerekLakin 这将导致丑陋的编译器警告:“警告:此函数声明返回类型为 'String',但不以 return 语句结尾。(file.dart:166 处的missing_return) " 对不起,是的。我在考虑内联案例,而不是单独的函数。老实说,我认为编译器也应该对 inline 给出相同的警告:)

以上是关于颤振步进器小部件 - 在各个步骤中验证字段的主要内容,如果未能解决你的问题,请参考以下文章

在颤动中将地图列表转换为步进器?

如何使用django管理员多对多个字段选择器小部件

无状态颤振小部件中的非最终字段

Flutter 中 ChangeNotifier 的构建器小部件

颤振小部件不显示

Kivy 日期选择器小部件