Update and fix latest lints, bump min SDK to 3.1 (dart-lang/stream_transform#178)
diff --git a/pkgs/stream_transform/.github/workflows/test-package.yml b/pkgs/stream_transform/.github/workflows/test-package.yml
index 2690a5e..b23f0fc 100644
--- a/pkgs/stream_transform/.github/workflows/test-package.yml
+++ b/pkgs/stream_transform/.github/workflows/test-package.yml
@@ -47,8 +47,8 @@
matrix:
# Add macos-latest and/or windows-latest if relevant for this package.
os: [ubuntu-latest]
- # Bump SDK for Legacy tets when changning min SDK.
- sdk: [2.18.0, dev]
+ # Bump SDK for Legacy tests when changing min SDK.
+ sdk: [3.1, dev]
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d
diff --git a/pkgs/stream_transform/CHANGELOG.md b/pkgs/stream_transform/CHANGELOG.md
index 2ef6fdb..c1e2c0e 100644
--- a/pkgs/stream_transform/CHANGELOG.md
+++ b/pkgs/stream_transform/CHANGELOG.md
@@ -1,6 +1,6 @@
-## 2.1.1-dev
+## 2.1.1-wip
-- Require Dart 2.18 or greater
+- Require Dart 3.1 or greater
- Forward errors from the `trigger` future through to the result stream in
`takeUntil`. Previously an error would have not closed the stream, and instead
raised as an unhandled async error.
diff --git a/pkgs/stream_transform/analysis_options.yaml b/pkgs/stream_transform/analysis_options.yaml
index 9dc8788..05f1af1 100644
--- a/pkgs/stream_transform/analysis_options.yaml
+++ b/pkgs/stream_transform/analysis_options.yaml
@@ -1,4 +1,4 @@
-include: package:lints/recommended.yaml
+include: package:dart_flutter_team_lints/analysis_options.yaml
analyzer:
language:
@@ -7,24 +7,10 @@
linter:
rules:
- - always_declare_return_types
- avoid_bool_literals_in_conditional_expressions
- avoid_classes_with_only_static_members
- - avoid_returning_null
- avoid_returning_this
- avoid_unused_constructor_parameters
- cascade_invocations
- - comment_references
- - directives_ordering
- join_return_with_assignment
- no_adjacent_strings_in_list
- - omit_local_variable_types
- - only_throw_errors
- - prefer_const_constructors
- - prefer_single_quotes
- - test_types_in_equals
- - throw_in_finally
- - unawaited_futures
- - unnecessary_lambdas
- - unnecessary_parenthesis
- - unnecessary_statements
diff --git a/pkgs/stream_transform/lib/src/async_map.dart b/pkgs/stream_transform/lib/src/async_map.dart
index c2ea253..81a176f 100644
--- a/pkgs/stream_transform/lib/src/async_map.dart
+++ b/pkgs/stream_transform/lib/src/async_map.dart
@@ -59,8 +59,8 @@
/// pending event.
///
/// Errors from this stream are forwarded directly to the result stream.
- /// Errors during the conversion are also forwarded to the result stream and are
- /// considered completing work so the next values are let through.
+ /// Errors during the conversion are also forwarded to the result stream and
+ /// are considered completing work so the next values are let through.
///
/// The result stream will not close until this stream closes and all pending
/// conversions have finished.
@@ -72,7 +72,7 @@
trigger: workFinished.stream,
aggregate: _dropPrevious,
longPoll: true,
- onEmpty: _ignore)
+ onEmpty: _ignore<T>)
._asyncMapThen(convert, workFinished.add);
}
diff --git a/pkgs/stream_transform/lib/src/combine_latest.dart b/pkgs/stream_transform/lib/src/combine_latest.dart
index 5fbdbf5..15c1b68 100644
--- a/pkgs/stream_transform/lib/src/combine_latest.dart
+++ b/pkgs/stream_transform/lib/src/combine_latest.dart
@@ -152,11 +152,11 @@
/// at least once, the result stream will emit any time any source stream
/// emits.
///
- /// The result stream will not close until all source streams have closed. When
- /// a source stream closes, the result stream will continue to emit the last
- /// value from the closed stream when the other source streams emit until the
- /// result stream has closed. If a source stream closes without emitting any
- /// value, the result stream will close as well.
+ /// The result stream will not close until all source streams have closed.
+ /// When a source stream closes, the result stream will continue to emit the
+ /// last value from the closed stream when the other source streams emit until
+ /// the result stream has closed. If a source stream closes without emitting
+ /// any value, the result stream will close as well.
///
/// For example:
///
diff --git a/pkgs/stream_transform/lib/src/switch.dart b/pkgs/stream_transform/lib/src/switch.dart
index b0fbbad..d64fc07 100644
--- a/pkgs/stream_transform/lib/src/switch.dart
+++ b/pkgs/stream_transform/lib/src/switch.dart
@@ -19,7 +19,8 @@
///
/// This means that the source stream is not paused until a sub stream
/// returned from the [convert] callback is done. Instead, the subscription
- /// to the sub stream is canceled as soon as the source stream emits a new event.
+ /// to the sub stream is canceled as soon as the source stream emits a new
+ /// event.
///
/// Errors from [convert], the source stream, or any of the sub streams are
/// forwarded to the result stream.
@@ -36,7 +37,8 @@
///
/// See also:
/// - [concurrentAsyncExpand], which emits events from all sub streams
- /// concurrently instead of cancelling subscriptions to previous subs streams.
+ /// concurrently instead of cancelling subscriptions to previous subs
+ /// streams.
Stream<S> switchMap<S>(Stream<S> Function(T) convert) {
return map(convert).switchLatest();
}
diff --git a/pkgs/stream_transform/lib/src/where.dart b/pkgs/stream_transform/lib/src/where.dart
index 6d210ea..76aa28a 100644
--- a/pkgs/stream_transform/lib/src/where.dart
+++ b/pkgs/stream_transform/lib/src/where.dart
@@ -28,10 +28,10 @@
/// Events on the result stream will be emitted in the order that [test]
/// completes which may not match the order of this stream.
///
- /// If the source stream is a broadcast stream the result will be as well. When
- /// used with a broadcast stream behavior also differs from [where] in that
- /// the [test] function is only called once per event, rather than once per
- /// listener per event.
+ /// If the source stream is a broadcast stream the result will be as well.
+ /// When used with a broadcast stream behavior also differs from [where] in
+ /// that the [test] function is only called once per event, rather than once
+ /// per listener per event.
///
/// Errors from the source stream are forwarded directly to the result stream.
/// Errors from [test] are also forwarded to the result stream.
diff --git a/pkgs/stream_transform/pubspec.yaml b/pkgs/stream_transform/pubspec.yaml
index 4d1ffbe..9cd6584 100644
--- a/pkgs/stream_transform/pubspec.yaml
+++ b/pkgs/stream_transform/pubspec.yaml
@@ -1,13 +1,13 @@
name: stream_transform
-version: 2.1.1-dev
+version: 2.1.1-wip
description: A collection of utilities to transform and manipulate streams.
repository: https://github.com/dart-lang/stream_transform
environment:
- sdk: ">=2.18.0 <3.0.0"
+ sdk: ^3.1.0
dev_dependencies:
async: ^2.5.0
+ dart_flutter_team_lints: ^2.0.0
fake_async: ^1.3.0
- lints: ^2.0.0
test: ^1.16.0
diff --git a/pkgs/stream_transform/test/async_expand_test.dart b/pkgs/stream_transform/test/async_expand_test.dart
index 3794165..8d84300 100644
--- a/pkgs/stream_transform/test/async_expand_test.dart
+++ b/pkgs/stream_transform/test/async_expand_test.dart
@@ -13,7 +13,7 @@
test('forwards errors from the convert callback', () async {
var errors = <String>[];
var source = Stream.fromIterable([1, 2, 3]);
- source.concurrentAsyncExpand((i) {
+ source.concurrentAsyncExpand<void>((i) {
// ignore: only_throw_errors
throw 'Error: $i';
}).listen((_) {}, onError: errors.add);
diff --git a/pkgs/stream_transform/test/async_map_buffer_test.dart b/pkgs/stream_transform/test/async_map_buffer_test.dart
index b43c617..2386217 100644
--- a/pkgs/stream_transform/test/async_map_buffer_test.dart
+++ b/pkgs/stream_transform/test/async_map_buffer_test.dart
@@ -158,7 +158,7 @@
if (streamType == 'broadcast') {
test('multiple listeners all get values', () async {
- var otherValues = [];
+ var otherValues = <String>[];
transformed.listen(otherValues.add);
values.add(1);
await Future(() {});
diff --git a/pkgs/stream_transform/test/async_map_sample_test.dart b/pkgs/stream_transform/test/async_map_sample_test.dart
index 6cab7d0..62b1b92 100644
--- a/pkgs/stream_transform/test/async_map_sample_test.dart
+++ b/pkgs/stream_transform/test/async_map_sample_test.dart
@@ -158,7 +158,7 @@
if (streamType == 'broadcast') {
test('multiple listeners all get values', () async {
- var otherValues = [];
+ var otherValues = <String>[];
transformed.listen(otherValues.add);
values.add(1);
await Future(() {});
@@ -204,6 +204,6 @@
test('allows nulls', () async {
var stream = Stream<int?>.value(null);
- await stream.asyncMapSample(expectAsync1((_) async {})).drain();
+ await stream.asyncMapSample(expectAsync1((_) async {})).drain<void>();
});
}
diff --git a/pkgs/stream_transform/test/async_where_test.dart b/pkgs/stream_transform/test/async_where_test.dart
index 3a13a38..6ea4e76 100644
--- a/pkgs/stream_transform/test/async_where_test.dart
+++ b/pkgs/stream_transform/test/async_where_test.dart
@@ -37,8 +37,8 @@
test('forwards values to multiple listeners', () async {
var values = StreamController<int>.broadcast();
var filtered = values.stream.asyncWhere((e) async => e > 2);
- var firstValues = [];
- var secondValues = [];
+ var firstValues = <int>[];
+ var secondValues = <int>[];
filtered
..listen(firstValues.add)
..listen(secondValues.add);
@@ -53,7 +53,7 @@
});
test('closes streams with multiple listeners', () async {
- var values = StreamController.broadcast();
+ var values = StreamController<int>.broadcast();
var predicate = Completer<bool>();
var filtered = values.stream.asyncWhere((_) => predicate.future);
var firstDone = false;
@@ -73,15 +73,15 @@
});
test('forwards errors emitted by the test callback', () async {
- var errors = [];
- var emitted = [];
+ var errors = <Object>[];
+ var emitted = <Object>[];
var values = Stream.fromIterable([1, 2, 3, 4]);
var filtered = values.asyncWhere((e) async {
await Future(() {});
if (e.isEven) throw Exception('$e');
return true;
});
- var done = Completer();
+ var done = Completer<Object?>();
filtered.listen(emitted.add, onError: errors.add, onDone: done.complete);
await done.future;
expect(emitted, [1, 3]);
diff --git a/pkgs/stream_transform/test/audit_test.dart b/pkgs/stream_transform/test/audit_test.dart
index e2f8133..28537db 100644
--- a/pkgs/stream_transform/test/audit_test.dart
+++ b/pkgs/stream_transform/test/audit_test.dart
@@ -119,7 +119,7 @@
values.add(1);
async.elapse(const Duration(milliseconds: 3));
values.add(2);
- var otherValues = [];
+ var otherValues = <int>[];
transformed.listen(otherValues.add);
values.add(3);
async.elapse(const Duration(milliseconds: 3));
diff --git a/pkgs/stream_transform/test/buffer_test.dart b/pkgs/stream_transform/test/buffer_test.dart
index 1675c28..830f555 100644
--- a/pkgs/stream_transform/test/buffer_test.dart
+++ b/pkgs/stream_transform/test/buffer_test.dart
@@ -228,7 +228,7 @@
test('emits empty list before values', () async {
trigger.add(null);
await Future(() {});
- expect(emittedValues, [[]]);
+ expect(emittedValues, [<int>[]]);
});
test('emits empty list after emitting values', () async {
@@ -242,7 +242,7 @@
await Future(() {});
expect(emittedValues, [
[1, 2],
- []
+ <int>[]
]);
});
});
diff --git a/pkgs/stream_transform/test/combine_latest_test.dart b/pkgs/stream_transform/test/combine_latest_test.dart
index d2c6003..1985c75 100644
--- a/pkgs/stream_transform/test/combine_latest_test.dart
+++ b/pkgs/stream_transform/test/combine_latest_test.dart
@@ -127,7 +127,7 @@
var other = StreamController<int>();
int sum(int a, int b) => throw _NumberedException(3);
- var errors = [];
+ var errors = <Object>[];
source.stream
.combineLatest(other.stream, sum)
.listen(null, onError: errors.add);
diff --git a/pkgs/stream_transform/test/concurrent_async_map_test.dart b/pkgs/stream_transform/test/concurrent_async_map_test.dart
index 3c46a96..1807f9f 100644
--- a/pkgs/stream_transform/test/concurrent_async_map_test.dart
+++ b/pkgs/stream_transform/test/concurrent_async_map_test.dart
@@ -111,7 +111,7 @@
if (streamType == 'broadcast') {
test('multiple listeners all get values', () async {
- var otherValues = [];
+ var otherValues = <String>[];
transformed.listen(otherValues.add);
controller.add(1);
await Future(() {});
diff --git a/pkgs/stream_transform/test/debounce_test.dart b/pkgs/stream_transform/test/debounce_test.dart
index 6b9775f..19de055 100644
--- a/pkgs/stream_transform/test/debounce_test.dart
+++ b/pkgs/stream_transform/test/debounce_test.dart
@@ -100,7 +100,7 @@
test('multiple listeners all get values', () {
fakeAsync((async) {
listen();
- var otherValues = [];
+ var otherValues = <int>[];
transformed.listen(otherValues.add);
values
..add(1)
@@ -157,7 +157,7 @@
test('multiple listeners all get values', () {
fakeAsync((async) {
listen();
- var otherValues = [];
+ var otherValues = <int>[];
transformed.listen(otherValues.add);
values
..add(1)
@@ -221,7 +221,7 @@
test('multiple listeners all get values', () {
fakeAsync((async) {
listen();
- var otherValues = [];
+ var otherValues = <int>[];
transformed.listen(otherValues.add);
values
..add(1)
@@ -284,7 +284,7 @@
test('multiple listeners all get values', () {
fakeAsync((async) {
listen();
- var otherValues = [];
+ var otherValues = <List<int>>[];
transformed.listen(otherValues.add);
values
..add(1)
diff --git a/pkgs/stream_transform/test/followd_by_test.dart b/pkgs/stream_transform/test/followd_by_test.dart
index 436d613..d600d13 100644
--- a/pkgs/stream_transform/test/followd_by_test.dart
+++ b/pkgs/stream_transform/test/followd_by_test.dart
@@ -138,13 +138,13 @@
});
test('forwards values to multiple listeners', () async {
- var otherValues = [];
+ var otherValues = <int>[];
transformed.listen(otherValues.add);
first.add(1);
await first.close();
second.add(2);
await Future(() {});
- var thirdValues = [];
+ var thirdValues = <int>[];
transformed.listen(thirdValues.add);
second.add(3);
await Future(() {});
diff --git a/pkgs/stream_transform/test/merge_test.dart b/pkgs/stream_transform/test/merge_test.dart
index 7217945..ecbf97f 100644
--- a/pkgs/stream_transform/test/merge_test.dart
+++ b/pkgs/stream_transform/test/merge_test.dart
@@ -20,12 +20,12 @@
test('cancels both sources', () async {
var firstCanceled = false;
- var first = StreamController()
+ var first = StreamController<int>()
..onCancel = () {
firstCanceled = true;
};
var secondCanceled = false;
- var second = StreamController()
+ var second = StreamController<int>()
..onCancel = () {
secondCanceled = true;
};
@@ -36,8 +36,8 @@
});
test('completes when both sources complete', () async {
- var first = StreamController();
- var second = StreamController();
+ var first = StreamController<int>();
+ var second = StreamController<int>();
var isDone = false;
first.stream.merge(second.stream).listen((_) {}, onDone: () {
isDone = true;
@@ -49,9 +49,9 @@
});
test('can cancel and relisten to broadcast stream', () async {
- var first = StreamController.broadcast();
- var second = StreamController();
- var emittedValues = [];
+ var first = StreamController<int>.broadcast();
+ var second = StreamController<int>();
+ var emittedValues = <int>[];
var transformed = first.stream.merge(second.stream);
var subscription = transformed.listen(emittedValues.add);
first.add(1);
@@ -84,17 +84,17 @@
test('handles mix of broadcast and single-subscription', () async {
var firstCanceled = false;
- var first = StreamController.broadcast()
+ var first = StreamController<int>.broadcast()
..onCancel = () {
firstCanceled = true;
};
var secondBroadcastCanceled = false;
- var secondBroadcast = StreamController.broadcast()
+ var secondBroadcast = StreamController<int>.broadcast()
..onCancel = () {
secondBroadcastCanceled = true;
};
var secondSingleCanceled = false;
- var secondSingle = StreamController()
+ var secondSingle = StreamController<int>()
..onCancel = () {
secondSingleCanceled = true;
};
@@ -102,8 +102,8 @@
var merged =
first.stream.mergeAll([secondBroadcast.stream, secondSingle.stream]);
- var firstListenerValues = [];
- var secondListenerValues = [];
+ var firstListenerValues = <int>[];
+ var secondListenerValues = <int>[];
var firstSubscription = merged.listen(firstListenerValues.add);
var secondSubscription = merged.listen(secondListenerValues.add);
diff --git a/pkgs/stream_transform/test/scan_test.dart b/pkgs/stream_transform/test/scan_test.dart
index 3a126ec..a0311c5 100644
--- a/pkgs/stream_transform/test/scan_test.dart
+++ b/pkgs/stream_transform/test/scan_test.dart
@@ -18,7 +18,7 @@
});
test('can create a broadcast stream', () {
- var source = StreamController.broadcast();
+ var source = StreamController<int>.broadcast();
var transformed = source.stream.scan(null, (_, __) {});
@@ -30,7 +30,7 @@
int sum(int x, int y) => x + y;
- var errors = [];
+ var errors = <Object>[];
source.stream.scan(0, sum).listen(null, onError: errors.add);
@@ -95,7 +95,7 @@
Future<int> combine(int x, int y) async => throw StateError('fail');
- var errors = [];
+ var errors = <Object>[];
source.stream.scan(0, combine).listen(null, onError: errors.add);
diff --git a/pkgs/stream_transform/test/switch_test.dart b/pkgs/stream_transform/test/switch_test.dart
index d2f99a8..9e70c08 100644
--- a/pkgs/stream_transform/test/switch_test.dart
+++ b/pkgs/stream_transform/test/switch_test.dart
@@ -196,7 +196,7 @@
test('uses map function', () async {
var outer = StreamController<List<int>>();
- var values = [];
+ var values = <int>[];
outer.stream.switchMap(Stream.fromIterable).listen(values.add);
outer.add([1, 2, 3]);
@@ -207,9 +207,10 @@
});
test('can create a broadcast stream', () async {
- var outer = StreamController.broadcast();
+ var outer = StreamController<int>.broadcast();
- var transformed = outer.stream.switchMap((_) => const Stream.empty());
+ var transformed =
+ outer.stream.switchMap((_) => const Stream<int>.empty());
expect(transformed.isBroadcast, true);
});
@@ -217,7 +218,7 @@
test('forwards errors from the convert callback', () async {
var errors = <String>[];
var source = Stream.fromIterable([1, 2, 3]);
- source.switchMap((i) {
+ source.switchMap<int>((i) {
// ignore: only_throw_errors
throw 'Error: $i';
}).listen((_) {}, onError: errors.add);
diff --git a/pkgs/stream_transform/test/take_until_test.dart b/pkgs/stream_transform/test/take_until_test.dart
index 9c73d09..982b3da 100644
--- a/pkgs/stream_transform/test/take_until_test.dart
+++ b/pkgs/stream_transform/test/take_until_test.dart
@@ -80,7 +80,7 @@
await values.close();
closeTrigger.completeError('sad');
await Future(() {});
- expect(errors, []);
+ expect(errors, <Object>[]);
});
test('cancels value subscription when trigger fires', () async {
@@ -91,7 +91,7 @@
if (streamType == 'broadcast') {
test('multiple listeners all get values', () async {
- var otherValues = [];
+ var otherValues = <Object>[];
transformed.listen(otherValues.add);
values
..add(1)
diff --git a/pkgs/stream_transform/test/tap_test.dart b/pkgs/stream_transform/test/tap_test.dart
index a42bdc6..f2b4346 100644
--- a/pkgs/stream_transform/test/tap_test.dart
+++ b/pkgs/stream_transform/test/tap_test.dart
@@ -9,7 +9,7 @@
void main() {
test('calls function for values', () async {
- var valuesSeen = [];
+ var valuesSeen = <int>[];
var stream = Stream.fromIterable([1, 2, 3]);
await stream.tap(valuesSeen.add).last;
expect(valuesSeen, [1, 2, 3]);
@@ -23,7 +23,7 @@
test('calls function for errors', () async {
dynamic error;
- var source = StreamController();
+ var source = StreamController<int>();
source.stream.tap((_) {}, onError: (e, st) {
error = e;
}).listen((_) {}, onError: (_) {});
@@ -34,8 +34,9 @@
test('forwards errors', () async {
dynamic error;
- var source = StreamController();
- source.stream.tap((_) {}, onError: (e, st) {}).listen((_) {}, onError: (e) {
+ var source = StreamController<int>();
+ source.stream.tap((_) {}, onError: (e, st) {}).listen((_) {},
+ onError: (Object e) {
error = e;
});
source.addError('error');
@@ -45,7 +46,7 @@
test('calls function on done', () async {
var doneCalled = false;
- var source = StreamController();
+ var source = StreamController<int>();
source.stream.tap((_) {}, onDone: () {
doneCalled = true;
}).listen((_) {});
@@ -56,7 +57,7 @@
test('forwards only once with multiple listeners on a broadcast stream',
() async {
var dataCallCount = 0;
- var source = StreamController.broadcast();
+ var source = StreamController<int>.broadcast();
source.stream.tap((_) {
dataCallCount++;
})
@@ -71,7 +72,7 @@
'forwards errors only once with multiple listeners on a broadcast stream',
() async {
var errorCallCount = 0;
- var source = StreamController.broadcast();
+ var source = StreamController<int>.broadcast();
source.stream.tap((_) {}, onError: (_, __) {
errorCallCount++;
})
@@ -85,7 +86,7 @@
test('calls onDone only once with multiple listeners on a broadcast stream',
() async {
var doneCallCount = 0;
- var source = StreamController.broadcast();
+ var source = StreamController<int>.broadcast();
source.stream.tap((_) {}, onDone: () {
doneCallCount++;
})
@@ -96,9 +97,9 @@
});
test('forwards values to multiple listeners', () async {
- var source = StreamController.broadcast();
- var emittedValues1 = [];
- var emittedValues2 = [];
+ var source = StreamController<int>.broadcast();
+ var emittedValues1 = <int>[];
+ var emittedValues2 = <int>[];
source.stream.tap((_) {})
..listen(emittedValues1.add)
..listen(emittedValues2.add);
diff --git a/pkgs/stream_transform/test/where_not_null_test.dart b/pkgs/stream_transform/test/where_not_null_test.dart
index c7e4e9b..c9af794 100644
--- a/pkgs/stream_transform/test/where_not_null_test.dart
+++ b/pkgs/stream_transform/test/where_not_null_test.dart
@@ -23,8 +23,8 @@
test('forwards values to multiple listeners', () async {
var values = StreamController<Object?>.broadcast();
var filtered = values.stream.whereNotNull();
- var firstValues = [];
- var secondValues = [];
+ var firstValues = <Object>[];
+ var secondValues = <Object>[];
filtered
..listen(firstValues.add)
..listen(secondValues.add);
diff --git a/pkgs/stream_transform/test/where_type_test.dart b/pkgs/stream_transform/test/where_type_test.dart
index 1a65dd6..4cbea37 100644
--- a/pkgs/stream_transform/test/where_type_test.dart
+++ b/pkgs/stream_transform/test/where_type_test.dart
@@ -21,10 +21,10 @@
});
test('forwards values to multiple listeners', () async {
- var values = StreamController.broadcast();
+ var values = StreamController<Object>.broadcast();
var filtered = values.stream.whereType<String>();
- var firstValues = [];
- var secondValues = [];
+ var firstValues = <Object>[];
+ var secondValues = <Object>[];
filtered
..listen(firstValues.add)
..listen(secondValues.add);
@@ -39,7 +39,7 @@
});
test('closes streams with multiple listeners', () async {
- var values = StreamController.broadcast();
+ var values = StreamController<Object>.broadcast();
var filtered = values.stream.whereType<String>();
var firstDone = false;
var secondDone = false;