| // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'dart:async'; | |
| /// A utility to end a stream based on an external trigger. | |
| extension TakeUntil<T> on Stream<T> { | |
| /// Takes values from this stream which are emitted before [trigger] | |
| /// completes. | |
| /// | |
| /// Completing [trigger] differs from canceling a subscription in that values | |
| /// which are emitted before the trigger, but have further asynchronous delays | |
| /// in transformations following the takeUtil, will still go through. | |
| /// Cancelling a subscription immediately stops values. | |
| /// | |
| /// If [trigger] completes as an error, the error will be forwarded through | |
| /// the result stream before the result stream closes. | |
| /// | |
| /// If [trigger] completes as a value or as an error after this stream has | |
| /// already ended, the completion will be ignored. | |
| Stream<T> takeUntil(Future<void> trigger) { | |
| var controller = isBroadcast | |
| ? StreamController<T>.broadcast(sync: true) | |
| : StreamController<T>(sync: true); | |
| StreamSubscription<T>? subscription; | |
| var isDone = false; | |
| trigger.then((_) { | |
| if (isDone) return; | |
| isDone = true; | |
| subscription?.cancel(); | |
| controller.close(); | |
| }, onError: (Object error, StackTrace stackTrace) { | |
| if (isDone) return; | |
| isDone = true; | |
| controller | |
| ..addError(error, stackTrace) | |
| ..close(); | |
| }); | |
| controller.onListen = () { | |
| if (isDone) return; | |
| subscription = | |
| listen(controller.add, onError: controller.addError, onDone: () { | |
| if (isDone) return; | |
| isDone = true; | |
| controller.close(); | |
| }); | |
| if (!isBroadcast) { | |
| controller | |
| ..onPause = subscription!.pause | |
| ..onResume = subscription!.resume; | |
| } | |
| controller.onCancel = () { | |
| if (isDone) return null; | |
| var toCancel = subscription!; | |
| subscription = null; | |
| return toCancel.cancel(); | |
| }; | |
| }; | |
| return controller.stream; | |
| } | |
| } |