ReactiveX / rxdart

The Reactive Extensions for Dart
http://reactivex.io
Apache License 2.0
3.37k stars 270 forks source link

Observable.combineLatest2 is not streaming when page is loaded through navigation #223

Closed pravinarr closed 5 years ago

pravinarr commented 5 years ago

I am creating a flutter app with blocs.

I followed the code available in Flutter login with blocs

It works as expected, if my app has no routes defined

class App extends StatelessWidget {
  Widget build(BuildContext context) {
    return Provider(
      child: MaterialApp(
        title: 'Log Me In!',
        home: Scaffold(
          body: LoginScreen(),
        ),
      ),
    ); 
  }
}

but when I change my app to use routes

class App extends StatelessWidget {
  Widget build(BuildContext context) {
    return  MaterialApp(
        title: 'Log Me In!',
        routes: {
          '/':(context) => Provider(
            child: Scaffold(
              body: LoginScreen(),
            ),
          )
        },
       );
  }
}

bloc code

class Bloc extends Object with Validators {
  final _email = BehaviorSubject<String>();
  final _password = BehaviorSubject<String>();

  // retrieve data from stream
  Stream<String> get email    => _email.stream.transform(validateEmail);
  Stream<String> get password => _password.stream.transform(validatePassword);
  Stream<bool>   get submitValid => Observable.combineLatest2(email, password, (e, p) => true);

  // add data to stream
  Function(String) get changeEmail    => _email.sink.add;
  Function(String) get changePassword => _password.sink.add;

  submit() {
    final validEmail    = _email.value;
    final validPassword = _password.value;

    print('$validEmail and $validPassword');
  }

  dispose() {
    _email.close();
    _password.close();
  }
}

Observable.combileLatest2 is not streaming the data (but it streams error though).

Using Rxdart version 0.19.0 and

Flutter 1.0.0 • channel beta •https://github.com/flutter/flutter.git Framework • revision 5391447fae (6 days ago) • 2018-11-29 19:41:26-0800 Engine • revision 7375a0f414Tools • Dart 2.1.0 (build 2.1.0-dev.9.4 f9ebf21297)

Am I doing something wrong here? thanks in advance

frankpepermans commented 5 years ago

Hi there, this keeps returning as an issue, see https://github.com/ReactiveX/rxdart/issues/199

TL;DR That example is wrong and we need to the author to update it.

pravinarr commented 5 years ago

@frankpepermans thank you for your reply.

Yes, I understand that the submitValid logic is wrong as it will return true always. But my issue is, it is not returning any value. For example, in the following code

 Observable.combineLatest2(email, password, (e, p) {
 //debug pointer here
     if (e == _email.value && p == _password.value) {
        return true;
      }
      return false;
    }).listen(print, onError: (data) {

      print(data);
    });

If I have routes configured, the control flow is not reaching to the debug point even when both fields are valid. If I remove the routes and put the login page directly on the Material Home , it works perfectly.

frankpepermans commented 5 years ago

Ok, to figure out what is going on:

Can you try to listen to the streams directly and then debug in their respective listen handlers?

pravinarr commented 5 years ago

@frankpepermans , yes I tried to listen and it worked perfectly.

both


email.listen(handler);
password.listen(handler);

handler(data){
print (data);
}

were working and was able to print the data that was streaming.

pravinarr commented 5 years ago

After lot of trial, I found that when I used routes for the navigation, flutter will build the page multiple times and thats the expected behavior refer here

So when it builds the page multiple times, it was creating multiple Observables on the bloc as it was creating new instance of Bloc every time it creates the Page route.

So when I modify the code

class App extends StatelessWidget {
  final login = Provider(
            child: Scaffold(
              body: LoginScreen(),
            ),
          );
  Widget build(BuildContext context) {
    return  MaterialApp(
        title: 'Log Me In!',
        routes: {
          '/':(context) => login,
        },
       );
  }
}

it worked perfectly.

The other way is to achieve is to create a stateful widget and do the initialization in the init method.

ianemv commented 5 years ago

@pravinarr You mentioned that it can be achieved by creating stateful widget? Do you have sample? I experienced same with yours, when I use home the submitValid is streaming but not if I use routes