0

Steps to Reproduce

I am trying to use the bloc pattern with firebase and having hard time since there is not good tutorial out there which uses firebase and bloc pattern.

I've faced strange thing while doing this and worked hard to find out the problem but couldn't.

I've created UserBloc and tried to set UserModel after listening to Firestore snapshot.

class UserBloc extends Object {
    final _user = StreamController<UserModel>.broadcast();
    final _uid = StreamController<String>.broadcast();

    Stream<UserModel> get user => _user.stream;
    Stream<String> get uid => _uid.stream;

    Function(UserModel) get setUser => _user.sink.add;
    Function(String) get setUID => _uid.sink.add;

    Stream<DocumentSnapshot> userStream;

    UserBloc() {
        uid.listen((uid) {
            print('user : $uid');
            if (uid != null) {
                fromUID(uid);
            }
        });
    }

    fromUID(String uid) {
        print('fromUID');
        userStream = Firestore.instance.collection('users').document(uid).snapshots();

        /// works ok when I delete below subscription
        userStream.listen((data) {
        });
    }

    dispose() {
        _user.close();
        _uid.close();
    }
}

Then I used the StreamBuilder inside my widget like below.

StreamBuilder(
    stream: userBloc.userStream,
    builder: (context, snapshot) {
        print(snapshot.connectionState);
        print(snapshot.data);
        return Text('testing');
    },
);

The problem is the snapshot.connectionState** is always **waiting. However, when I delete the subscription code which is userStream.listen((data) {, I was able to get this working. All the example do the listen and set the data in the constructor of bloc. I have no idea why this is happening.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Hyo
  • 1
  • 2
  • 13

2 Answers2

0

I think you must delete listen-sentence for stream controller in Bloc when you are using Streambuilder. Because Streambuilder is listening stream controller in Bloc, there is a duplication problem between Streambuilder and listen-sentence in Bloc.

문준석
  • 33
  • 3
0

The problem is, that .snapshot() returns a BroadcastStream.

https://www.dartlang.org/articles/libraries/broadcast-streams

After the first subscription (.listen in constructor) the stream is active and the onData callback is called when data is available/changed.

When the UI subscribes to the stream, the stream is already active (the read operation is done), so the ConnectionState will always return waiting till next document update.

Try to modify your code like this, to reproduce:

/// works ok when I delete below subscription
userStream.listen((data) => print(data));

Future.delayed(Duration(seconds: 5),
        () => userStream.listen((onData) => print(onData))
);

The second callback will NOT fire.

To avoid this behavior, you have to do some caching.

Add rxdart to your dependencies https://pub.dev/packages/rxdart

Modify your code like this

userStream = Observable(Firestore.instance.collection('users').document(uid).snapshots())
.shareReplay(maxSize: 1);

It will replay the emitted values to any new listener, up to a given [maxSize].