stream_transform icon indicating copy to clipboard operation
stream_transform copied to clipboard

Broadcast stream to emit most recent event sent on listening

Open sbatezat opened this issue 3 years ago • 0 comments

It's more a feature request than an issue. I'm facing this issue: https://stackoverflow.com/questions/68590198/dart-get-the-last-or-the-first-value-of-a-stream

There is a suggestion to solve it (see bellow), and I would love to see it inside stream_transform library. Does it make sense?

// Copyright 2021 Google LLC.
// SPDX-License-Identifier: Apache-2.0
import "dart:async";

/// Listens to [source] to returned stream.
///
/// Each listener on the returned stream receives the most recent
/// event sent on [source] followed by all further events of [source]
/// until they stop listening.
/// If there has been no events on [source] yet, only the further events
/// are forwarded.
Stream<T> mostRecentStream<T>(Stream<T> source) {
  var isDone = false;
  var hasEvent = false;
  T? mostRecentEvent;
  List<MultiStreamController>? pendingListeners;
  var listeners = <MultiStreamController>[];

  void forEachListener(void Function(MultiStreamController) action) {
    var active = 0;
    var originalLength = listeners.length;
    for (var i = 0; i < listeners.length; i++) {
      var controller = listeners[i];
      if (controller.hasListener) {
        listeners[active++] = controller;
        if (i < originalLength) action(controller);
      }   
    }
    listeners.length = active;
  }

  source.listen((event) {
    mostRecentEvent = event;
    hasEvent = true;
    forEachListener((controller) {
      controller.addSync(event);
    });
  }, onError: (e, s) {
    forEachListener((controller) { 
      controller.addErrorSync(e, s);
    });
  }, onDone: () {
    isDone = true;
    for (var controller in listeners) {
      controller.close();
    }
    listeners.clear();
  });

  return Stream<T>.multi((controller) {
    if (hasEvent) controller.add(mostRecentEvent as T);
    if (isDone) {
      controller.close();
    } else {
      listeners.add(controller);
    }  
  });
}

sbatezat avatar Oct 15 '22 18:10 sbatezat