Flutter 颤振集团冲突国家

Flutter 颤振集团冲突国家,flutter,bloc,flutter-bloc,flutter-state,Flutter,Bloc,Flutter Bloc,Flutter State,我正试图在上提供的教程的帮助下,使用Bloc构建一个登录活动。我已经成功地将表单验证和登录流程结合到一个工作解决方案中,但是当添加一个按钮来切换密码可见性时,事情发生了混乱 我想我会遵循验证和登录状态的相同格式(小部件的onPressed触发事件,bloc处理它并更改状态以更新视图),但由于状态是互斥的,切换密码可见性会导致其他信息(如验证错误或加载指示器)消失,因为它们显示所需的状态不再是活动状态 我认为避免这种情况的一种方法是使用一个单独的Bloc来处理密码切换,但我认为这涉及到在我的视图中

我正试图在上提供的教程的帮助下,使用Bloc构建一个登录活动。我已经成功地将表单验证和登录流程结合到一个工作解决方案中,但是当添加一个按钮来切换密码可见性时,事情发生了混乱

我想我会遵循验证和登录状态的相同格式(小部件的onPressed触发事件,bloc处理它并更改状态以更新视图),但由于状态是互斥的,切换密码可见性会导致其他信息(如验证错误或加载指示器)消失,因为它们显示所需的状态不再是活动状态

我认为避免这种情况的一种方法是使用一个单独的Bloc来处理密码切换,但我认为这涉及到在我的视图中嵌套第二个BlocBuilder,更不用说实现另一组Bloc+事件+状态,这听起来可能会使代码更难理解/导航,因为事情变得更复杂。这就是Bloc的使用方式,还是有一种更干净的方法可以更好地避免这种情况

class LoginForm extends StatefulWidget {
  @override
  State<LoginForm> createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
  final _usernameController = TextEditingController();
  final _passwordController = TextEditingController();

  @override
  Widget build(BuildContext context) {

    _onLoginButtonPressed() {
      BlocProvider.of<LoginBloc>(context).add(
        LoginButtonPressed(
          username: _usernameController.text,
          password: _passwordController.text,
        ),
      );
    }

    _onShowPasswordButtonPressed() {
      BlocProvider.of<LoginBloc>(context).add(
        LoginShowPasswordButtonPressed(),
      );
    }

    return BlocListener<LoginBloc, LoginState>(
      listener: (context, state) {
        if (state is LoginFailure) {
          Scaffold.of(context).showSnackBar(
            SnackBar(
              content: Text('${state.error}'),
              backgroundColor: Colors.red,
            ),
          );
        }
      },
      child: BlocBuilder<LoginBloc, LoginState>(
        builder: (context, state) {
          return Form(
            child: Padding(
              padding: const EdgeInsets.all(32.0),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  TextFormField(
                    decoration: InputDecoration(labelText: 'Username', prefixIcon: Icon(Icons.person)),
                    controller: _usernameController,
                    autovalidate: true,
                    validator: (_) {
                      return state is LoginValidationError ? state.usernameError : null;
                    },
                  ),
                  TextFormField(
                    decoration: InputDecoration(
                      labelText: 'Password',
                      prefixIcon: Icon(Icons.lock_outline),
                      suffixIcon: IconButton(
                        icon: Icon(
                          state is! DisplayPassword ? Icons.visibility : Icons.visibility_off,
                          color: ColorUtils.primaryColor,
                        ),
                        onPressed: () {
                          _onShowPasswordButtonPressed();
                        },
                      ),
                    ),
                    controller: _passwordController,
                    obscureText: state is! DisplayPassword ? true : false,
                    autovalidate: true,
                    validator: (_) {
                      return state is LoginValidationError ? state.passwordError : null;
                    },
                  ),
                  Container(height: 30),
                  ButtonTheme(
                    minWidth: double.infinity,
                    height: 50,
                    child: RaisedButton(
                      color: ColorUtils.primaryColor,
                      textColor: Colors.white,
                      onPressed: state is! LoginLoading ? _onLoginButtonPressed : null,
                      child: Text('LOGIN'),
                    ),
                  ),
                  Container(
                    child: state is LoginLoading
                      ? CircularProgressIndicator()
                      : null,
                  ),
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}
classloginform扩展StatefulWidget{
@凌驾
State createState()=>\u LoginFormState();
}
类_LoginFormState扩展状态{
final _usernameController=TextEditingController();
final _passwordController=TextEditingController();
@凌驾
小部件构建(构建上下文){
_onLoginButtonPressed(){
BlocProvider.of(上下文)。添加(
登录按钮按下(
用户名:_usernameController.text,
密码:_passwordController.text,
),
);
}
_onShowPasswordButtonPressed(){
BlocProvider.of(上下文)。添加(
LoginShowPasswordButtonPressed(),
);
}
返回BlocListener(
侦听器:(上下文、状态){
如果(状态为登录失败){
Scaffold.of(上下文).showSnackBar(
小吃条(
内容:文本(“${state.error}”),
背景颜色:Colors.red,
),
);
}
},
孩子:BlocBuilder(
生成器:(上下文、状态){
报税表(
孩子:填充(
填充:常数边集全部(32.0),
子:列(
mainAxisAlignment:mainAxisAlignment.center,
儿童:[
TextFormField(
装饰:输入装饰(标签文本:“用户名”,前缀:图标(Icons.person)),
控制器:\ u usernameController,
自动验证:true,
验证器:(){
返回状态为LoginValidationError?state.UserName错误:null;
},
),
TextFormField(
装饰:输入装饰(
labelText:“密码”,
前缀:图标(图标锁定轮廓),
后缀:图标按钮(
图标:图标(
状态为!DisplayPassword?Icons.visibility:Icons.visibility\u off,
颜色:ColorUtils.primaryColor,
),
已按下:(){
_onShowPasswordButtonPressed();
},
),
),
控制器:_passwordController,
蒙蔽文本:状态为!显示密码?真:假,
自动验证:true,
验证器:(){
返回状态为LoginValidationError?state.passwordError:null;
},
),
货柜(高度:30),
钮扣(
minWidth:double.infinity,
身高:50,
孩子:升起按钮(
颜色:ColorUtils.primaryColor,
textColor:Colors.white,
onPressed:状态为!LoginLoading?\u onLoginButtonPressed:空,
子项:文本('LOGIN'),
),
),
容器(
子:状态为LoginLoading
?循环压缩机指示器()
:null,
),
],
),
),
);
},
),
);
}
}
classloginbloc扩展了Bloc{
最终用户存储库用户存储库;
最终认证Bloc AuthenticationBloc;
bool isShowingPassword=false;
罗金布洛克({
@需要此.userRepository,
@需要此.authenticationBloc,
}):assert(userRepository!=null),
断言(authenticationBloc!=null);
LoginState get initialState=>LoginInitial();
@凌驾
流mapEventToState(LoginEvent事件)异步*{
if(事件为LoginShowPasswordButtonPressed){
isShowingPassword=!isShowingPassword;
产生isShowingPassword?DisplayPassword():LoginInitial();
}
如果(事件为LoginButtonPressed){
如果(!_isUsernameValid(event.username)| |!_isPasswordValid(event.password)){
收益率登录验证错误(
usernameError:_isUsernameValid(event.username)?null:“(测试)验证失败”,
passwordError:_isPasswordValid(event.password)?null:“(测试)验证失败”,
);//TODO更新此字段,以便在多个条件下验证字段(字段是必需的、最小字符大小等),并向用户显示相应的字段
}
否则{
收益率LoginLoading();
最终响应=等待userRepository.authenticate(
用户名:event.username,
密码:event.password,
);
如果(response.ok!=null){
authenticationBloc.add(LoggedIn(用户:response.ok));
}
否则{
屈服登录失败(错误:response.err
class LoginBloc extends Bloc<LoginEvent, LoginState> {
  final UserRepository userRepository;
  final AuthenticationBloc authenticationBloc;
  bool isShowingPassword = false;

  LoginBloc({
    @required this.userRepository,
    @required this.authenticationBloc,
  })  : assert(userRepository != null),
      assert(authenticationBloc != null);

  LoginState get initialState => LoginInitial();

  @override
  Stream<LoginState> mapEventToState(LoginEvent event) async* {

    if (event is LoginShowPasswordButtonPressed) {
      isShowingPassword = !isShowingPassword;
      yield isShowingPassword ?  DisplayPassword() : LoginInitial();
    }

    if (event is LoginButtonPressed) {
      if (!_isUsernameValid(event.username) || !_isPasswordValid(event.password)) {
        yield LoginValidationError(
          usernameError: _isUsernameValid(event.username) ? null : "(test) validation failed",
          passwordError: _isPasswordValid(event.password) ? null : "(test) validation failed",
        );  //TODO update this so fields are validated for multiple conditions (field is required, minimum char size, etc) and the appropriate one is shown to user
      }
      else {
        yield LoginLoading();

        final response = await userRepository.authenticate(
          username: event.username,
          password: event.password,
        );

        if (response.ok != null) {
          authenticationBloc.add(LoggedIn(user: response.ok));
        }
        else {
          yield LoginFailure(error: response.error.message);
        }
      }
    }
  }

  bool _isUsernameValid(String username) {
    return username.length >= 4;
  }

  bool _isPasswordValid(String password) {
    return password.length >= 4;
  }
}
abstract class LoginEvent extends Equatable {
  const LoginEvent();

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

class LoginButtonPressed extends LoginEvent {
  final String username;
  final String password;

  const LoginButtonPressed({
    @required this.username,
    @required this.password,
  });

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

  @override
  String toString() =>
    'LoginButtonPressed { username: $username, password: $password }';
}

class LoginShowPasswordButtonPressed extends LoginEvent {}
abstract class LoginState extends Equatable {
  const LoginState();

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

class LoginInitial extends LoginState {}

class LoginLoading extends LoginState {}

class LoginValidationError extends LoginState {
  final String usernameError;
  final String passwordError;

  const LoginValidationError({@required this.usernameError, @required this.passwordError});

  @override
  List<Object> get props => [usernameError, passwordError];
}

class DisplayPassword extends LoginState {}

class LoginFailure extends LoginState {
  final String error;

  const LoginFailure({@required this.error});

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

  @override
  String toString() => 'LoginFailure { error: $error }';
}
bloc
 - full
    - login_bloc.dart
    - login_event.dart
    - login_state.dart
 - single
    - password_visibility_bloc.dart
class PasswordVisibilityBloc extends Bloc<bool, bool> {
  @override
  bool get initialState => false;

  @override
  Stream<bool> mapEventToState(
    bool event,
  ) async* {
    yield !event;
  }
}