使用flutter_bloc提交时如何验证表单?

Posted

技术标签:

【中文标题】使用flutter_bloc提交时如何验证表单?【英文标题】:How to validate form when submit in flutter with flutter_bloc? 【发布时间】:2020-06-02 07:02:26 【问题描述】:

这是我的更改密码屏幕。我正在使用 flutter_bloc 来实现 mvvc 模式。此页面适用于 bloc。但我想要实现的是在提交表单时验证表单。由于我是新手,所以我不知道该怎么做。

更改密码事件

abstract class ChangePasswordEvent extends Equatable 
  const ChangePasswordEvent();


class SubmitButtonPressed extends ChangePasswordEvent 
  final String oldPassword;
  final String newPassword;

  const SubmitButtonPressed(@required this.oldPassword, this.newPassword);

  @override
  List<Object> get props => [oldPassword, newPassword];

更改密码状态

abstract class ChangePasswordState extends Equatable 
  const ChangePasswordState();

  @override
  List<Object> get props => [];


class ChangePasswordInitial extends ChangePasswordState 

class ChangePasswordLoading extends ChangePasswordState 

class ChangePasswordSuccess extends ChangePasswordState 

class ChangePasswordFailure extends ChangePasswordState 
  final String error;

  const ChangePasswordFailure(@required this.error);

  @override
  List<Object> get props => [error];

  @override
  String toString() => 'ChangePasswordFailure  error: $error ';

更改密码区

class ChangePasswordBloc
    extends Bloc<ChangePasswordEvent, ChangePasswordState> 
  final UserRepository userRepository;

  ChangePasswordBloc(
    @required this.userRepository,
  ) : assert(userRepository != null);

  @override
  ChangePasswordState get initialState => ChangePasswordInitial();

  @override
  Stream<ChangePasswordState> mapEventToState(
      ChangePasswordEvent event) async* 
    if (event is SubmitButtonPressed) 
      yield ChangePasswordLoading();

      try 
        final bool isPasswordChanged = await userRepository.changePassword(
          event.oldPassword,
          event.newPassword,
        );

        if (isPasswordChanged) 
          yield ChangePasswordSuccess();
        
       catch (error) 
        yield ChangePasswordFailure(error: error);
      
    
  

更改密码页面

class ChangePasswordPage extends StatelessWidget 
  final UserRepository userRepository;

  ChangePasswordPage(Key key, @required this.userRepository)
      : assert(userRepository != null),
        super(key: key);

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        title: Text('Change Password'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: BlocProvider(
          create: (context) 
            return ChangePasswordBloc(
              userRepository: userRepository,
            );
          ,
          child: ChangePasswordForm(),
        ),
      ),
    );
  

更改密码表格

class ChangePasswordForm extends StatefulWidget 
  @override
  _ChangePasswordFormState createState() => _ChangePasswordFormState();


class _ChangePasswordFormState extends State<ChangePasswordForm> 
  final userRepository = UserRepository();
  final _formKey = GlobalKey<FormState>();

  final _oldPassController = TextEditingController();
  final _newPassController = TextEditingController();
  final _confirmPassController = TextEditingController();

  @override
  Widget build(BuildContext context) 
    _onSubmitButtonPressed() 
      BlocProvider.of<ChangePasswordBloc>(context).add(
        SubmitButtonPressed(
          oldPassword: _oldPassController.text,
          newPassword: _newPassController.text,
        ),
      );
    

    return BlocListener<ChangePasswordBloc, ChangePasswordState>(
      listener: (context, state) 
        if (state is ChangePasswordFailure) 
          Scaffold.of(context).showSnackBar(
            SnackBar(
              content: Text('$state.error'),
              backgroundColor: Colors.red,
            ),
          );
        

        if (state is ChangePasswordSuccess) 
          Scaffold.of(context).showSnackBar(
            SnackBar(
              content: Text('Password Changed Successfully'),
              backgroundColor: Colors.green,
            ),
          );
        
      ,
      child: BlocBuilder<ChangePasswordBloc, ChangePasswordState>(
        builder: (context, state) 
          return Form(
            key: _formKey,
            child: Column(
              children: [
                TextFormField(
                  decoration: InputDecoration(labelText: 'Old Password'),
                  controller: _oldPassController,
                ),
                SizedBox(height: 20.0),
                TextFormField(
                  decoration: InputDecoration(labelText: 'New Password'),
                  controller: _newPassController,
                  obscureText: true,
                ),
                SizedBox(height: 20.0),
                TextFormField(
                  decoration: InputDecoration(labelText: 'Confirm Password'),
                  controller: _confirmPassController,
                  obscureText: true,
                  validator: (value) 
                    final String _newPassword = _newPassController.text;

                    if (_newPassword != value) 
                      return "Password Mismatch";
                    

                    return null;
                  ,
                ),
                SizedBox(height: 20.0),
                RaisedButton(
                  onPressed: () 
                    if (state is! ChangePasswordLoading) 
                      final form = _formKey.currentState;

                      if (form.validate()) 
                        return _onSubmitButtonPressed();
                      

                      return null;
                    
                  ,
                  child: Text('Submit'),
                ),
              ],
            ),
          );
        ,
      ),
    );
  

【问题讨论】:

您在提交 _formKey.currentState.validate() 时对其进行验证。你的问题是什么? 结帐flutter_form_bloc,它有一个专为表单设计的bloc,它将为您节省大量代码。 【参考方案1】:

你去看看他们的例子:Form Validation

该示例验证电子邮件和密码格式,您应该相应地更改它。你的状态应该是这样的:

class MyFormState extends Equatable 
  final String email;
  final bool isEmailValid;
  final String password;
  final bool isPasswordValid;
  final bool formSubmittedSuccessfully;

  bool get isFormValid => isEmailValid && isPasswordValid;
  //....

要验证的 BLoC:

class MyFormBloc extends Bloc<MyFormEvent, MyFormState> 
  final RegExp _emailRegExp = RegExp(
    r'^[a-zA-Z0-9.!#$%&’*+/=?^_`|~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$',
  );
  final RegExp _passwordRegExp = RegExp(
    r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]8,$',
  );

  @override
  MyFormState get initialState => MyFormState.initial();

  @override
  void onTransition(Transition<MyFormEvent, MyFormState> transition) 
    print(transition);
  

  @override
  Stream<MyFormState> mapEventToState(
    MyFormEvent event,
  ) async* 
    if (event is EmailChanged) 
      yield state.copyWith(
        email: event.email,
        isEmailValid: _isEmailValid(event.email),
      );
    
    if (event is PasswordChanged) 
      yield state.copyWith(
        password: event.password,
        isPasswordValid: _isPasswordValid(event.password),
      );
    
    if (event is FormSubmitted) 
      yield state.copyWith(formSubmittedSuccessfully: true);
    
    if (event is FormReset) 
      yield MyFormState.initial();
    
  

  bool _isEmailValid(String email) 
    return _emailRegExp.hasMatch(email);
  

  bool _isPasswordValid(String password) 
    return _passwordRegExp.hasMatch(password);
  

以及Form构建方法:

  @override
  Widget build(BuildContext context) 
    return BlocBuilder<MyFormBloc, MyFormState>(
      builder: (context, state) 
        if (state.formSubmittedSuccessfully) 
          return SuccessDialog(onDismissed: () 
            _emailController.clear();
            _passwordController.clear();
            _myFormBloc.add(FormReset());
          );
        
        return Form(
          child: Column(
            children: <Widget>[
              TextFormField(
                controller: _emailController,
                decoration: InputDecoration(
                  icon: Icon(Icons.email),
                  labelText: 'Email',
                ),
                keyboardType: TextInputType.emailAddress,
                autovalidate: true,
                validator: (_) 
                  return state.isEmailValid ? null : 'Invalid Email';
                ,
              ),
              TextFormField(
                controller: _passwordController,
                decoration: InputDecoration(
                  icon: Icon(Icons.lock),
                  labelText: 'Password',
                ),
                obscureText: true,
                autovalidate: true,
                validator: (_) 
                  return state.isPasswordValid ? null : 'Invalid Password';
                ,
              ),
              RaisedButton(
                onPressed: state.isFormValid ? _onSubmitPressed : null,
                child: Text('Submit'),
              ),
            ],
          ),
        );
      ,
    );
  

【讨论】:

【参考方案2】:

我认为最好不要将 flutter_bloc 包用于验证目的,因为它会导致所有控件的重建。而是使用像这里描述的 Stream 和 Provider 这样的原始对象。 https://www.youtube.com/watch?v=JqWK4oitJFs

附加链接:https://medium.com/swlh/how-to-create-a-simple-login-form-in-flutter-using-bloc-pattern-b55ad52a2a10

【讨论】:

以上是关于使用flutter_bloc提交时如何验证表单?的主要内容,如果未能解决你的问题,请参考以下文章

即使在表单活动验证状态下,如何仅在提交时验证角度表单

当引导验证成功时,如何使用 $.post 提交表单而不进行页面重定向?

如何通过表单外的按钮提交表单?

如何使用 flutter_bloc 添加 CircularProgressIndicator

如何使用 jQuery 对表单提交进行验证

Angular 2 提交时触发表单验证