Ian
Ian

Reputation: 13842

Streambuilder not receiving some snapshot data

I am just testing some streams from a GPS. I could plug in the gps stream directly, but I want to separate this for the moment. So I can using a StreamBuilder with my own created stream.

This all seems to be working, but the Streambuilder seems to 'miss' certain snapshots, which has me confused. Shouldn't it be guaranteed to receive all data from the stream (if it's not a broadcast)? Or am I using streams incorrectly ?

If you look at the data added/received, I can see count 5 & 7 were both added, but never received. If I just 'listen' the data without the StreamBuilder, all of the data seems to appear.

Streambuilder code:

Widget build(BuildContext context) {

    final ApplicationBloc bloc = BlocProvider.of<ApplicationBloc>(context);

    return StreamBuilder<Map<String, dynamic>>(
        stream: bloc.gps.stream,
        builder: (BuildContext context, AsyncSnapshot<Map<String, dynamic>> snapshot){
          if( snapshot.hasData ) {
            print("Receiving ${snapshot.data['count']}");
            return Text("${snapshot.data['timestamp']}\n${snapshot.data['latitude']},${snapshot.data['longitude']}\n",
              style: Theme.of(context).textTheme.display1,);
          } else {
            return Text('waiting for GPS...', style: Theme.of(context).textTheme.display1,);
          }
        }
    );

  }

Add to stream code:

var locationOptions = LocationOptions(accuracy: LocationAccuracy.bestForNavigation, distanceFilter: 0, timeInterval: 1, forceAndroidLocationManager: false);

    final Geolocator geoLocator = new Geolocator();
    geoLocator.getPositionStream( locationOptions ).listen(
            (Position position) {
              _count++;
              Map<String, dynamic> listenerCurrentLocation = {
                'latitude'  : position.latitude,
                'longitude' : position.longitude,
                'speed'     : position.speed,
                'accuracy'  : position.accuracy,
                'altitude'  : position.altitude,
                'timestamp' : position.timestamp,
                'count'     : _count,
              };
              print( "Adding: $listenerCurrentLocation");
              gpsController.sink.add( listenerCurrentLocation );
            });

Output from Run console

I/flutter (16345): Receiving 2
I/flutter (16345): Adding: {latitude: 50.829798333333336, longitude: -0.18484833333333334, speed: 0.0, accuracy: 20.0, altitude: 11.7, timestamp: 2019-01-13 14:21:02.000Z, count: 3}
I/flutter (16345): Receiving 3
I/flutter (16345): Adding: {latitude: 50.829798333333336, longitude: -0.18484833333333334, speed: 0.0, accuracy: 20.0, altitude: 11.7, timestamp: 2019-01-13 14:21:03.000Z, count: 4}
I/flutter (16345): Receiving 4
I/flutter (16345): Adding: {latitude: 50.829798333333336, longitude: -0.18484833333333334, speed: 0.0, accuracy: 20.0, altitude: 11.7, timestamp: 2019-01-13 14:21:04.000Z, count: 5}
I/flutter (16345): Adding: {latitude: 50.829798333333336, longitude: -0.18484833333333334, speed: 0.0, accuracy: 20.0, altitude: 0.0, timestamp: 2019-01-13 14:21:04.000Z, count: 6}
I/flutter (16345): Receiving 6
I/flutter (16345): Adding: {latitude: 50.829798333333336, longitude: -0.18484833333333334, speed: 0.0, accuracy: 20.0, altitude: 11.7, timestamp: 2019-01-13 14:21:05.000Z, count: 7}
I/flutter (16345): Adding: {latitude: 50.829798333333336, longitude: -0.18484833333333334, speed: 0.0, accuracy: 20.0, altitude: 0.0, timestamp: 2019-01-13 14:21:05.000Z, count: 8}
I/flutter (16345): Receiving 8

Upvotes: 6

Views: 1600

Answers (1)

R&#233;mi Rousselet
R&#233;mi Rousselet

Reputation: 276957

This is the expected behavior.

As opposed to using stream.listen, StreamBuilder calls its builder only when a new frame is requested.

It is fine because StreamBuilder should be used only to build the layout, in which case it doesn't matter if a value is missed.

If this behavior is undesired, consider switching to a manual listening instead:

Stream stream;
StreamSubscription sub;

initState() {
  super.initState();
  sub = stream.listen((value) {
    // do something with `value`
  });
}


dispose() {
  sub.cancel();
  super.dispose();
}

Upvotes: 6

Related Questions