import 'dart:async'; extension StreamExtension on Stream { /// Returns a new Stream which outputs only `true` for every update of the original /// stream, ratelimited by the Duration t Stream rateLimit(Duration t) { final controller = StreamController(); Timer? timer; var gotMessage = false; // as we call our inline-defined function recursively we need to make sure that the // variable exists prior of creating the function. Silly dart. Function? onMessage; // callback to determine if we should send out an update onMessage = () { // do nothing if it is already closed if (controller.isClosed) { return; } if (timer == null) { // if we don't have a timer yet, send out the update and start a timer gotMessage = false; controller.add(true); timer = Timer(t, () { // the timer has ended...delete it and, if we got a message, re-run the // method to send out an update! timer = null; if (gotMessage) { onMessage?.call(); } }); } else { // set that we got a message gotMessage = true; } }; final subscription = listen((_) => onMessage?.call(), onDone: () => controller.close(), onError: (e, s) => controller.addError(e, s)); // add proper cleanup to the subscription and the controller, to not memory leak controller.onCancel = () { subscription.cancel(); controller.close(); }; return controller.stream; } }