riverpod
riverpod copied to clipboard
Stream from StreamProvider is not unsubscribed to when widgets get disposed
Describe the bug When a widget that watches a stream provider gets disposed, it should unsubscribe to the stream returned by the stream provider. This is not the case.
Real problem: I need to dispose of some resources related to a stream controller when there are no longer any listeners to its stream. But even though the widget watching the stream provider has been disposed, the onCancel
method on the stream controller does not get called. This prevents me from disposing the resources.
To Reproduce
Here is a test showing that even though the TestWidget
is disposed, the stream returned from the stream provider still has listeners and that the onCancel
method does not get called.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import '../../utils/riverpod_test_utils.dart';
final realStreamProvider = StreamProvider<int>((ref) => const Stream.empty());
class TestWidget extends ConsumerWidget {
const TestWidget({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return ref.watch(realStreamProvider).when(
data: (value) => Container(),
loading: () => Container(),
error: (error, stack) => Container());
}
}
void main() {
testWidgets('verify widget unsubscribes to real stream', (tester) async {
final controller = StreamController<int>(sync: true);
bool onCancelHasBeenCalled = false;
controller.onCancel = () {
onCancelHasBeenCalled = true;
};
await tester.pumpRiverpodWidget(const TestWidget(),
override: realStreamProvider.overrideWith((ref) => controller.stream));
await tester.pumpAndSettle();
expect(controller.hasListener, true);
await tester.pumpWidget(const SizedBox());
await tester.pumpAndSettle();
expect(false, controller.hasListener); // FAILS
expect(true, onCancelHasBeenCalled); // FAILS
await controller.close();
});
}
Expected behavior
There should be no listeners to the stream after the widget has been disposed.