I'm using streaming to validate two textField and a button on my screen, my validators work fine with the bloc architecture that I built in the app, I use The rxdart combineLatest2 to combine my two TextFields validation to enable and disable my login button, everything works fine in the beginning but when I delete and refill fields combiner mix up and enable login button even one of the fields are not valid I show the problem in gif for better understanding of my problem. this is the link to gif: https://freeimage.host/i/SVjN1a
u can see the full code on my GitHub too, please help me fix this issue https://github.com/Manticodes/flutter_loginscreen_blocpattern
I use this code inside my bloc class to combine two validation stream
Stream<bool> get submitValid =>
Rx.combineLatest2(emailvalidation, passwordvalidation, (a, b) => true);
this is my bloc class that contains combiner
class Bloc with Validator {
final _emailController = BehaviorSubject<String>();
final _passwordController = BehaviorSubject<String>();
final _textController = BehaviorSubject<String>();
// add to stream
Function(String) get reciveText => _textController.sink.add;
Function(String) get changeEmail => _emailController.sink.add;
Function(String) get changePassword => _passwordController.sink.add;
// access to stream
Stream<String> get emailvalidation =>
_emailController.stream.transform(validateEmail);
Stream<String> get passwordvalidation =>
_passwordController.stream.transform(validatePassword);
Stream<bool> get submitValid =>
Rx.combineLatest2(emailvalidation, passwordvalidation, (a, b) => true);
Stream<String> get accesstext =>
_textController.stream.transform(validateText);
submit() {
final validemail = _emailController.value;
final validpass = _passwordController.value;
reciveText('user name : $validemail \n pass : $validpass ');
print('all valid');
}
dispose() {
_emailController.close();
_passwordController.close();
}
}
this is validator class
class Validator {
final validateEmail = StreamTransformer<String, String>.fromHandlers(
handleData: (data, sink) {
if (data.contains('@') && data.contains('.')) {
sink.add(data);
} else {
sink.addError('Enter Valid emaill');
}
},
);
final validatePassword = StreamTransformer<String, String>.fromHandlers(
handleData: (data, sink) {
if (data.length > 3) {
sink.add(data);
} else {
sink.addError('Password is too short');
}
},
);
final validateText = StreamTransformer<String, String>.fromHandlers(
handleData: (data, sink) {
sink.add(data);
},
);
}
and finally, this is the class that contains my screen
class LoginScreen extends StatelessWidget {
const LoginScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final bloc = Provider.of(context);
return Scaffold(
appBar: AppBar(title: Text('Log me in ')),
body: Container(
margin: EdgeInsets.all(20),
child: Column(children: [
emailField(bloc),
passwordField(bloc),
SizedBox(
height: 40,
),
submitbutton(bloc),
message(bloc),
]),
),
);
}
emailField(Bloc bloc) {
return StreamBuilder(
stream: bloc.emailvalidation,
builder: (context, snapshot) {
return TextField(
onChanged: bloc.changeEmail,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
hintText: '[email protected]',
labelText: 'Email Address',
hintStyle: TextStyle(color: Colors.grey),
errorText:
snapshot.error != null ? snapshot.error.toString() : null,
),
);
},
);
}
passwordField(Bloc bloc) {
return StreamBuilder(
stream: bloc.passwordvalidation,
builder: (context, snapshot) {
return TextField(
onChanged: bloc.changePassword,
obscureText: false,
decoration: InputDecoration(
hintText: 'use strong password',
labelText: 'Password',
errorText:
// ignore: prefer_null_aware_operators
snapshot.error != null ? snapshot.error.toString() : null),
);
},
);
}
submitbutton(Bloc bloc) {
return StreamBuilder(
stream: bloc.submitValid,
builder: (context, snapshot) {
return ElevatedButton(
onPressed: snapshot.hasData
? () {
bloc.submit();
}
: null,
style: ElevatedButton.styleFrom(primary: Colors.red),
child: const Text('login'),
);
},
);
}
message(Bloc bloc) {
return StreamBuilder(
stream: bloc.submitValid,
builder: (context, snapshot1) {
if (snapshot1.hasData) {
return StreamBuilder(
stream: bloc.accesstext,
builder: (context, snapshot2) {
return Text(snapshot2.hasData ? ' ${snapshot2.data} ' : '');
},
);
} else {
return Center(
child: Container(
child: Text('Please Enter valid User name & Password')),
);
}
},
);
}
}