代码之家  ›  专栏  ›  技术社区  ›  dragonfly02

对身份验证窗体使用BLOC模式

  •  1
  • dragonfly02  · 技术社区  · 7 年前

    Confirm Password 也有助于 Signup 按钮应启用。

    我有两个问题: 1这是个问题。当前,如果我通过 Login 注册 形式 注册 按钮已启用,这是错误的,因为 确认密码 2我觉得有比我所做的更好的方法来实现目标 验证和 按钮验证。我最初尝试为创建一个验证器 确认密码 但这需要时间 二者都 密码和确认密码作为输入,但无法使其正常工作 StreamTransformer 只取一个输入参数。有什么更好的办法?

    import 'package:flutter/material.dart';
    import 'dart:async';
    import 'package:rxdart/rxdart.dart';
    
    
    void main() => runApp(AuthProvider(child: MaterialApp(home: Auth())));
    
    enum AuthMode { Signup, Login }
    
    class Auth extends StatefulWidget {
      @override
      _AuthState createState() => _AuthState();
    }
    
    class _AuthState extends State<Auth> {
      AuthMode authMode = AuthMode.Login;
      bool get _isLoginMode => authMode == AuthMode.Login;
      TextEditingController confirmPasswordCtrl = TextEditingController();
    
      @override
      Widget build(BuildContext context) {
        final bloc = AuthProvider.of(context);
        return Scaffold(
          body: Container(
            margin: EdgeInsets.all(20.0),
            child: Column(
              children: <Widget>[
                emailField(bloc),
                passwordField(bloc),
                confirmPasswordField(bloc),
                Container(
                  margin: EdgeInsets.only(top: 40.0),
                ),
                FlatButton(
                  child: Text('Switch to ${_isLoginMode ? 'Signup' : 'Login'}'),
                  onPressed: swithAuthMode,
                ),
                loginOrSignupButton(bloc),
              ],
            ),
          ),
        );
      }
    
      void swithAuthMode() {
        setState(() {
          authMode = authMode == AuthMode.Login ? AuthMode.Signup : AuthMode.Login;
        });
      }
    
      Widget confirmPasswordField(AuthBloc bloc) {
        return _isLoginMode
            ? Container()
            : StreamBuilder(
                stream: bloc.passwordConfirmed,
                builder: (context, snapshot) {
                  return TextField(
                    obscureText: true,
                    onChanged: bloc.changeConfirmPassword,
                    keyboardType: TextInputType.text,
                    decoration: InputDecoration(
                      labelText: 'Confirm Password',
                      errorText: snapshot.hasData && !snapshot.data ? 'password mismatch' : null,
                    ),
                  );
                },
              );
      }
    
      Widget emailField(AuthBloc bloc) {
        return StreamBuilder(
          stream: bloc.email,
          builder: (context, snapshot) {
            return TextField(
              keyboardType: TextInputType.emailAddress,
              onChanged: bloc.changeEmail,
              decoration: InputDecoration(
                hintText: 'your email',
                labelText: 'Email',
                errorText: snapshot.error,
              ),
            );
          },
        );
      }
    
      Widget loginOrSignupButton(AuthBloc bloc) {
        return StreamBuilder(
          stream: _isLoginMode ? bloc.submitValid : bloc.signupValid,
          builder: (context, snapshot) {
            print('hasData: ${snapshot.hasData}, data: ${snapshot.data}');
            return RaisedButton(
              onPressed: // The problem is, after entering some login details then switching from login to signup, the Signup button is enabled.
                  !snapshot.hasData || !snapshot.data ? null : () => onSubmitPressed(bloc, context),
              color: Colors.blue,
              child: Text('${_isLoginMode ? 'Log in' : 'Sign up'}'),
            );
          },
        );
      }
    
      void onSubmitPressed(AuthBloc bloc, BuildContext context) async {
        var response = await bloc.submit(_isLoginMode);
        if (response.success) {
          Navigator.pushReplacementNamed(context, '/home');
        } else {
          showDialog(
              context: context,
              builder: (context) {
                return AlertDialog(
                  title: Text('Error'),
                  content: Text(response.message),
                  actions: <Widget>[
                    FlatButton(
                      child: Text('Ok'),
                      onPressed: () {
                        Navigator.of(context).pop();
                      },
                    ),
                  ],
                );
              });
        }
      }
    
      Widget passwordField(AuthBloc bloc) {
        return StreamBuilder(
          stream: bloc.password,
          builder: (_, snapshot) {
            return TextField(
              obscureText: true,
              onChanged: bloc.changePassword,
              decoration: InputDecoration(
                labelText: 'Password',
                errorText: snapshot.error,
                hintText: 'at least 6 characters',
              ),
            );
          },
        );
      }
    }
    
    class AuthProvider extends InheritedWidget {
      final bloc;
    
      AuthProvider({Key key, Widget child}) :
        bloc = AuthBloc(), super(key:key, child: child);
    
      @override
      bool updateShouldNotify(InheritedWidget oldWidget) => true;
    
      static AuthBloc of(BuildContext context) => (context.inheritFromWidgetOfExactType(AuthProvider) as AuthProvider).bloc;
    
    }
    
     class Repository {
       // this will call whatever backend to authenticate users.
      Future<AuthResult> signupUser(String email, String password) => null;
      Future<AuthResult> loginUser(String email, String password) => null;
    }
    
    
    class AuthBloc extends Object with AuthValidator {
      final _emailController = BehaviorSubject<String>();
      final _passwordController = BehaviorSubject<String>();
      final _confirmPasswordController = BehaviorSubject<String>();
      final _signupController = PublishSubject<Map<String, dynamic>>();
      final Repository _repository = Repository();
    
      Stream<String> get email => _emailController.stream.transform(validateEmail);
    
      Stream<String> get password =>
          _passwordController.stream.transform(validatePassword);
    
      Stream<bool> get submitValid =>
          Observable.combineLatest2(email, password, (e, p) => true);
    
      // Is there a better way of doing passwordConfirmed and signupValid?
      Stream<bool> get passwordConfirmed =>
          Observable.combineLatest2(password, _confirmPasswordController.stream, (p, cp) => p == cp);
    
      Stream<bool> get signupValid =>
          Observable.combineLatest2(submitValid, passwordConfirmed, (s, p) => s && p);
    
    
      // sink
      Function(String) get changeEmail => _emailController.sink.add;
      Function(String) get changePassword => _passwordController.sink.add;
      Function(String) get changeConfirmPassword =>
          _confirmPasswordController.sink.add;
    
      Future<AuthResult> submit(bool isLogin) async {
        final validEmail = _emailController.value;
        final validPassword = _passwordController.value;
        if (!isLogin)
          return await _repository.signupUser(validEmail, validPassword);
    
        return await _repository.loginUser(validEmail, validPassword);
      }
    
      void dispose() {
        _emailController.close();
        _passwordController.close();
        _signupController.close();
        _confirmPasswordController.close();
      }
    }
    
    class AuthResult {
      bool success;
      String message;
      AuthResult(this.success, this.message);
    }
    
    // demo validator
    class AuthValidator {
      final validateEmail = StreamTransformer<String, String>.fromHandlers(
        handleData: (email, sink) {
          if (email.contains('@')) sink.add(email);
          else sink.addError('Email is not valid');
        }
      );
    
      final validatePassword = StreamTransformer<String, String>.fromHandlers(
        handleData: (password, sink) {
          if (password.length >= 6) sink.add(password);
          else sink.addError('Password must be at least 6 characters');
        }
      );
    }
    
    2 回复  |  直到 7 年前
        1
  •  1
  •   ssiddh    7 年前

    在尝试复制这种行为之后,我可以证实这一点 signupValid 流有一个 true 值if submitValid 有一个 是的 价值,似乎是

        2
  •  1
  •   pat64j    7 年前

    密码已确认 可能是:

    Stream<String> get passwordConfirmed => _confirmPasswordController.stream
        .transform(validatePassword).doOnData((String confirmPassword){
            if(0 != _passwordController.value.compareTo(confirmPassword)){
                _confirmPasswordController.addError("Passwords do not match");
            }
    });
    

    根据建议 博莱迪 here .