草庐IT

mobile - Flutter:根据验证更改状态

coder 2023-07-23 原文

我正在为一个演示应用构建一个简单的忘记密码表单,该表单由一个 TextFormFields 和一个用于提交数据的 FloatingActionButton 组成。我已经意识到 FloatingActionButton 没有这样的禁用 bool 状态,所以我想尝试通过将状态更改为 _isValid: true/false 来复制它,具体取决于TextFormField 验证函数,然后我可以将一些三元运算符放在 FloatingActionButton 上以根据此小部件的状态更改颜色和功能。

您将能够看到我在安装小部件时将 _autoValidate 设置为 true,然后我尝试在 _validateForgetEmail 函数中触发 UI 重新加载。当我触发这些状态更改时,我会收到一个很大的 UI 错误提示

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown building Form-[LabeledGlobalKey<FormState>#0a40e](dirty, state:
flutter: FormState#59216):
flutter: setState() or markNeedsBuild() called during build.
flutter: This ForgotPasswordForm widget cannot be marked as needing to build because the framework is already
flutter: in the process of building widgets. A widget can be marked as needing to be built during the build
flutter: phase only if one of its ancestors is currently building. This exception is allowed because the
flutter: framework builds parent widgets before children, which means a dirty descendant will always be
flutter: built. Otherwise, the framework might not visit this widget during this build phase.

代码如下:

class ForgotPasswordForm extends StatefulWidget {
  @override
  _ForgotPasswordFormState createState() => _ForgotPasswordFormState();
}
Class _ForgotPasswordFormState extends State<ForgotPasswordForm> {
  final _emailController = TextEditingController();
  final _formKey = GlobalKey<FormState>();
  final bool _autoValidate = true;

  bool _isLoading = false;
  bool _isValid = false;

  String email;

  @override
  Widget build(BuildContext context) {
    // Build a Form widget using the _formKey created above.
    return Form(
      key: _formKey,
      child: _isLoading
          ? _buildLoadingSpinner(context)
          : _buildPasswordForm(context),
      autovalidate: _autoValidate,
    );
  }

  Widget _buildLoadingSpinner(BuildContext context) {
    return (Center(child: CircularProgressIndicator()));
  }

  Widget _buildPasswordForm(BuildContext context) {
    print('isValid: ' + _isValid.toString());

    return Column(
      children: <Widget>[
        Text(
          'Please enter your email address.',
          style: TextStyle(fontSize: 14.0),
          textAlign: TextAlign.center,
        ),
        Text(
          'You will recieve a link to reset your password.',
          style: TextStyle(fontSize: 14.0),
          textAlign: TextAlign.center,
        ),
        SizedBox(height: 32.0),
        TextFormField(
          controller: _emailController,
          validator: _validateForgetEmail,
          keyboardType: TextInputType.emailAddress,
          autovalidate: _autoValidate,
          style: TextStyle(fontSize: 14.0),
          onSaved: (String val) {
            email = val;
          },
          decoration: InputDecoration(
            filled: true,
            contentPadding: EdgeInsets.symmetric(horizontal: 15, vertical: 8),
            labelText: 'Email',
            border: InputBorder.none,
            labelStyle: TextStyle(fontSize: 14.0, color: Colors.lightBlueAccent),
            errorStyle: TextStyle(fontSize: 10.0, height: 0.5),
            focusedBorder: UnderlineInputBorder(
              borderSide: BorderSide(color: Colors.lightGreenAccent, width: 2.0),
            ),
          ),
        ),
        SizedBox(height: 24.0),
        FloatingActionButton(
          backgroundColor: _isValid ? Colors.lightBlue : Colors.grey,
          onPressed: () {
            _submitPasswordReset();
          },
          child: Icon(Icons.arrow_forward_ios, size: 14.0),
        )
      ],
      mainAxisAlignment: MainAxisAlignment.center,
    );
  }

  void _submitPasswordReset() async {
    if (_formKey.currentState.validate()) {
      setState(() {
        _isLoading = true;
      });

      UserPasswordResetRequest newPasswordRequest =
          new UserPasswordResetRequest(email: _emailController.text);

      http.Response response = await ApiService.queryPost(
          '/api/users/password-forgot',
          body: newPasswordRequest.toJson());

      final int statusCode = response.statusCode;

      if (statusCode == 400) {
        Scaffold.of(context).showSnackBar(SnackBar(
            content: Text('Wrong email or password'),
            duration: Duration(seconds: 3),
            backgroundColor: Colors.red));

        setState(() {
          _isLoading = false;
        });
      }

      if (statusCode == 200) {
        // setState(() {
        //   _isLoading = false;
        // });

        Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => UserBackToLogin()),
        );
      }

      setState(() {
        _isLoading = false;
      });
    }
  }

  String _validateForgetEmail(String value) {
    String patttern =
        r"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$";
    RegExp regExp = new RegExp(patttern);
    if (value.length == 0) {
      return "Email is Required";
    } else if (!regExp.hasMatch(value)) {
      setState(() {
        _isValid = false;
      });

      return "Must be a valid email address";
    }

    print('value' + value);

    setState(() {
      _isValid = true;
    });

    return null;
  }
}

任何洞察力都会很高兴看到我做错了什么 - flutter 非常新。如果您需要更多信息,我可以提供。

山姆干杯

最佳答案

你可以这样做:
_validateForgetEmail 方法一分为二:

String _validateForgetEmail(String value) {
  if (value.length == 0) {
    return "Email is Required";
  } else if (!_isEmailValid(value)) {
    return "Must be a valid email address";
  }

  print('value' + value);

  return null;
}

bool _isEmailValid(String value) {
  String pattern =
      r"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$";
  RegExp regExp = new RegExp(pattern);

  return regExp.hasMatch(value);
}

现在这些方法只验证值而不影响任何状态。

监听 _emailController 变化

@override
void initState() {
  super.initState();
  _emailController.addListener(() {
    final isEmailValid = _isEmailValid(_emailController.value.text);
    if(isEmailValid != _isValid) {
      setState(() {
        _isValid = isEmailValid;
      });
    }
  });
}

也不要忘记处理 _emailController

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

异常说明:
TextFormField 扩展了 FormField 类。如果 autovalidate 开启,则作为 validator 传递的函数将在 FormFieldState.build 方法中调用以更新错误文本。
所以它导致 setStatebuild 调用,这是框架不允许的

关于mobile - Flutter:根据验证更改状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57141105/

有关mobile - Flutter:根据验证更改状态的更多相关文章

  1. ruby-on-rails - Ruby on Rails 迁移,将表更改为 MyISAM - 2

    如何正确创建Rails迁移,以便将表更改为MySQL中的MyISAM?目前是InnoDB。运行原始执行语句会更改表,但它不会更新db/schema.rb,因此当在测试环境中重新创建表时,它会返回到InnoDB并且我的全文搜索失败。我如何着手更改/添加迁移,以便将现有表修改为MyISAM并更新schema.rb,以便我的数据库和相应的测试数据库得到相应更新? 最佳答案 我没有找到执行此操作的好方法。您可以像有人建议的那样更改您的schema.rb,然后运行:rakedb:schema:load,但是,这将覆盖您的数据。我的做法是(假设

  2. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  3. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  4. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  5. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  6. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  7. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  8. ruby - Capistrano 3 在任务中更改 ssh_options - 2

    我尝试使用不同的ssh_options在同一阶段运行capistranov.3任务。我的production.rb说:set:stage,:productionset:user,'deploy'set:ssh_options,{user:'deploy'}通过此配置,capistrano与用户deploy连接,这对于其余的任务是正确的。但是我需要将它连接到服务器中配置良好的an_other_user以完成一项特定任务。然后我的食谱说:...taskswithoriginaluser...task:my_task_with_an_other_userdoset:user,'an_othe

  9. ruby-on-rails - 如何将验证与模型分开 - 2

    我有一些非常大的模型,我必须将它们迁移到最新版本的Rails。这些模型有相当多的验证(User有大约50个验证)。是否可以将所有这些验证移动到另一个文件中?说app/models/validations/user_validations.rb。如果可以,有人可以提供示例吗? 最佳答案 您可以为此使用关注点:#app/models/validations/user_validations.rbrequire'active_support/concern'moduleUserValidationsextendActiveSupport:

  10. ruby-on-rails - 跳过状态机方法的所有验证 - 2

    当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested

随机推荐