How do you do Rx.CombineLatestStream using the flutter_bloc package

1.5k Views Asked by At

I put together a sample app that implements an username and password field using streams with verification using validation transforms. I used RxDart for this and I hooked up the "Login" button to enable/disable based on 2 streams results being true.

bloc.dart

  Stream<bool> get submitValidWithCombineLatestStream =>
  CombineLatestStream([email, password], (list) => true);

login_page.dart

 Widget submitButton(Bloc bloc) {
 return StreamBuilder(
   stream: bloc.submitValidWithCombineLatestStream,
   builder: (_, snapshot) {
    return RaisedButton(
      color: Colors.deepPurpleAccent,
      child: Text(
        'Submit',
        style: TextStyle(color: Colors.white),
      ),
      onPressed: !snapshot.hasData ? null : bloc.submit,
    );
  },
 );
}

I have coded the same sample app using flutter_bloc but I'm trying to figure out how to enable/disable the "Login" button using whatever bloc has for CombineLatestStream. Does anyone know how to do this?

I realize that this is total overkill but I'm trying to figure this out using this simple example and I'm not sure how to access all the cool RxDart functionality once you convert to bloc (flutter_bloc). I'd like to have the best of both worlds WITHOUT importing/using RxDart.

Is that possible?

An example of what I’m looking for would be:

  • I’m building a form that requires 3 different backend calls. I’d prefer to show the progress indicator while all 3 calls are working and block until all 3 calls return. I’d like to do that via CombineLatestStreams since all 3 return Steam Futures.
2

There are 2 best solutions below

2
On

Problem in your main bloc class,check out this github code, https://github.com/hoc081098/flutter_validation_login_form_BLoC_pattern_RxDart

In short like this,you can use CombineLatestStream you can set in your main bloc class like this,

return LoginBloc._(
      emailChanged: emailS.add,
      passwordChanged: passwordS.add,
      submitLogin: () => submitLoginS.add(null),
      emailError$: emailError$,
      passwordError$: passwordError$,
      isLoading$: isLoadingS.stream,
      message$: message$,
      dispose: DisposeBag([...subjects, message$.connect()]).dispose,
    ); 
1
On

This is how you combine streams with bloc using RxDart, you have emit new values if there is something new in listener, that's all. You might create listener whenever you want to do calls to your backend.

import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:rxdart/rxdart.dart';

class TestState {
  final bool isValid;

  TestState(this.isValid);
}

class TestCubit extends Cubit<TestState> {
  final List<StreamSubscription> _ss = [];
  final _emailSubject = BehaviorSubject<String>.seeded(null);
  final _passwordSubject = BehaviorSubject<String>.seeded(null);

  TestCubit() : super(TestState(false)) {
    _ss.add(_subscribeToEmailAndPassword());
  }

  StreamSubscription _subscribeToEmailAndPassword() {
    return Rx.combineLatest([_emailSubject, _passwordSubject], (list) {
      if (list[0] != null && list[1] != null) {
        return true;
      }
      return false;
    }).listen((value) {
      emit(TestState(value));
    });
  }

  @override
  Future<void> close() async {
    _ss.forEach((e) async => await e.cancel());
    await super.close();
  }

  void emailFieldChange(String email) {
    _emailSubject.add(email);
  }

  void passwordFieldChange(String password) {
    _passwordSubject.add(password);
  }
}