Reputation: 1079
I want to stop listening to snapshot updates. The snapshot keeps listening to updates even after the screen is closed. I am using the below code to listen to the updates.
CollectionReference reference = Firestore.instance.collection('Events');
reference.snapshots().listen((querySnapshot) {
querySnapshot.documentChanges.forEach((change) {
// Do something with change
});
})
Upvotes: 12
Views: 16448
Reputation: 1007
Since this is relating to Flutter and therefore probably to widgets changing on new snapshot events, please consider using StreamBuilder to easily delegate stream management and build great reactive UIs.
Container(
alignment: FractionalOffset.center,
color: Colors.white,
child: StreamBuilder<int>(
stream: _bids,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
List<Widget> children;
if (snapshot.hasError) {
children = <Widget>[
const Icon(
Icons.error_outline,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${snapshot.error}'),
),
];
} else {
switch (snapshot.connectionState) {
case ConnectionState.none:
children = const <Widget>[
Icon(
Icons.info,
),
Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Select a lot'),
)
];
break;
case ConnectionState.waiting:
children = const <Widget>[
SizedBox(
width: 60,
height: 60,
child: CircularProgressIndicator(),
),
Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Awaiting bids...'),
)
];
break;
case ConnectionState.active:
children = <Widget>[
const Icon(
Icons.check_circle_outline,
color: Colors.green,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('\$${snapshot.data}'),
)
];
break;
case ConnectionState.done:
children = <Widget>[
const Icon(
Icons.info,
color: Colors.blue,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('\$${snapshot.data} (closed)'),
)
];
break;
}
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children,
);
},
)
Or just use snapshot.hasData
and snapshot.hasError
to switch between showing Circle, error, and actual data.
Upvotes: 1
Reputation: 112
For me, I simply use take on the snapshot method. Basically, the same principle as rxjs with Firebase. You take one stream of data and stop listening.
return collection
.withConverter<Discount>(
fromFirestore: (snapshots, _) => Discount.fromMap(snapshots
.data()!),
toFirestore: (discount, _) => discount
.toMap(),
)
.snapshots()
.take(1); // Take 1 to stop listening to events
If you run and print out the ConnectionState, you'll see it goes to Done once it fetches all the docs in the collection.
Upvotes: 0
Reputation: 11
the correct way is stream.pause() so the listener will be on pause mode cancel() destroy the listener and his content
Upvotes: 0
Reputation: 265
Very late answer, but I thought I'd complete the previous answers with a code sample as it might be useful to others.
class EventsScreen extends StatefulWidget {
EventsScreen({Key key}) : super(key: key);
@override
_EventsScreenState createState() => _EventsScreenState();
}
class _EventsScreenState extends State<EventsScreen> {
StreamSubscription<QuerySnapshot> _eventsSubscription;
@override
void initState() {
// Initialise your stream subscription once
CollectionReference eventsReference = Firestore.instance.collection('Events');
_eventsSubscription = eventsReference.snapshots().listen((snapshot) => _onEventsSnapshot);
super.initState();
}
@override
Widget build(BuildContext context) {
// Build your widget here
return Container();
}
void _onEventsSnapshot(QuerySnapshot snapshot) {
// Remove the setState() call if you don't want to refresh your screen whenever you get a fresh snapshot
setState(() {
snapshot?.documentChanges?.forEach(
(docChange) => {
// If you need to do something for each document change, do it here.
},
);
// Anything you might do every time you get a fresh snapshot can be done here.
});
}
@override
void dispose() {
// Cancel your subscription when the screen is disposed
_eventsSubscription?.cancel();
super.dispose();
}
}
This approach leverages the StatefulWidget's state to handle documents changes.
A better approach would be to use a Provider or the BLoC pattern so that the subscription to your Firestore is not created and handled in the UI.
Upvotes: 4
Reputation: 53317
Your listener is of type StreamSubscription
, so you can call some helpful methods on your listener such as cancel()
CollectionReference reference = Firestore.instance.collection('Events');
StreamSubscription<QuerySnapshot> streamSub = reference.snapshots().listen((querySnapshot) {
querySnapshot.documentChanges.forEach((change) {
// Do something with change
});
});
//somewhere
streamSub.cancel();
Upvotes: 28
Reputation: 277047
The listen
method returns a Subscription
This class is used to cancel the listening.
You should store inside your state that object to cancel subscription on dispose.
Upvotes: 6