| // Copyright (c) 2021, 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'; |
| |
| class MoreTypedStreamController<T, ListenData, PauseData> { |
| final StreamController<T> controller; |
| |
| /// A wrapper around [StreamController] that statically guarantees its |
| /// clients that [onPause] and [onCancel] can only be invoked after |
| /// [onListen], and [onResume] can only be invoked after [onPause]. |
| /// |
| /// There is no static guarantee that [onPause] will not be invoked twice. |
| /// |
| /// Internally the wrapper is not safe, and uses explicit null checks. |
| factory MoreTypedStreamController({ |
| required ListenData Function(StreamController<T>) onListen, |
| PauseData Function(ListenData)? onPause, |
| void Function(ListenData, PauseData)? onResume, |
| FutureOr<void> Function(ListenData)? onCancel, |
| bool sync = false, |
| }) { |
| ListenData? listenData; |
| PauseData? pauseData; |
| var controller = StreamController<T>( |
| onPause: () { |
| if (pauseData != null) { |
| throw StateError('Already paused'); |
| } |
| if (onPause != null) { |
| pauseData = onPause(listenData as ListenData); |
| } |
| }, |
| onResume: () { |
| if (onResume != null) { |
| var currentPauseData = pauseData as PauseData; |
| pauseData = null; |
| onResume(listenData as ListenData, currentPauseData); |
| } |
| }, |
| onCancel: () { |
| if (onCancel != null) { |
| var currentListenData = listenData as ListenData; |
| listenData = null; |
| onCancel(currentListenData); |
| } |
| }, |
| sync: sync, |
| ); |
| controller.onListen = () { |
| listenData = onListen(controller); |
| }; |
| return MoreTypedStreamController._(controller); |
| } |
| |
| MoreTypedStreamController._(this.controller); |
| } |