Search code examples
dartflutterstreamdart-pubrxdart

Button stays disabled after changing StreamController<String>.broadcast() to BehaviorSubject<String>()


I was using Streams and rxdart for form validation. When I was using StreamController<String>.broadcast(), button enable-disable logic was working fine.

Changing that to BehaviorSubject<String>() button stays disabled even though the logic is correct.

bloc.dart

import 'dart:async';

import 'package:login_validation_with_bloc/blocs/validator.dart';
import 'package:rxdart/rxdart.dart';

class Bloc with Validator {
  final _emailController = StreamController<String>.broadcast();
  final _passwordController = StreamController<String>.broadcast();

  Stream<String> get email => _emailController.stream.transform(validateEmail);

  Stream<String> get password =>
      _passwordController.stream.transform(validatePassword);

  Stream<bool> get validForm =>
      Observable.combineLatest2(email, password, (e, p) => true);

  Function(String) get changeEmail => _emailController.sink.add;

  Function(String) get changePassword => _passwordController.sink.add;

  dispose() {
    _emailController.close();
    _passwordController.close();
  }
}

final bloc = Bloc();

home.dart, Two TextFields and Submit button

Widget emailTextField(context, Bloc bloc) => StreamBuilder(
        stream: bloc.email,
        builder: (context, snapshot) => TextField(
              decoration: InputDecoration(
                  labelText: "Email",
                  hintText: "Enter your email address",
                  errorText: snapshot.error,
                  border: UnderlineInputBorder()),
              keyboardType: TextInputType.emailAddress,
              onChanged: bloc.changeEmail,
            ),
      );

  Widget passwordTextField(context, Bloc bloc) => StreamBuilder(
        stream: bloc.password,
        builder: (context, snapshot) => TextField(
              decoration: InputDecoration(
                  labelText: "Password",
                  hintText: "Enter your password",
                  errorText: snapshot.error,
                  border: UnderlineInputBorder()),
              keyboardType: TextInputType.text,
              obscureText: true,
              onChanged: bloc.changePassword,
            ),
      );

  Widget submitButton(context, Bloc bloc) => StreamBuilder(
        stream: bloc.validForm,
        builder: (context, snapshot) {
          return Container(
            child: RaisedButton(
              color: Colors.blue,
              disabledColor: Colors.grey,
              textColor: Colors.white,
              disabledTextColor: Colors.white70,
              onPressed: snapshot.hasData && !snapshot.hasError
                  ? () {
                      print("Submit button pressed");
                    }
                  : null,
              elevation: 5,
              disabledElevation: 0,
              child: Text("Submit"),
            ),
          );
        },
      );

Changes

In bloc.dart

final _emailController = BehaviorSubject<String>();
final _passwordController = BehaviorSubject<String>();

submit() {
    final validEmail = _emailController.value;
    final validPassword = _passwordController.value;

    print("\n\tEmail: $validEmail\n\tPassword: $validPassword");
}

In home.dart

snapshot.hasData && !snapshot.hasError ? bloc.submit() : null,

I expect the button should stay enabled when the logic is correct, and should not call the method until and unless the button is pressed


Solution

  • In home.dart:

    Change this:

    snapshot.hasData && !snapshot.hasError ? bloc.submit() : null,
    

    to This

    snapshot.hasData && !snapshot.hasError ? bloc.submit : null,
    

    Explanation: With bloc.submit you refer to method and with bloc.submit() you refer to return value of that method.

    What you were doing is that you were referring to return value of that method and as you were not returning any thing that method was just executing but as the value returned was null the button was remaining disabled.