One source, two independent readers

Toss-up

Requirement

One reading source must feed two independent computations — the total and the peak — while running exactly once. The source increments a counter each time it runs; print the total, the peak, and the counter to prove the single pass. The data is in the code; both versions must print the lines shown under Expected output.

Expected output
total: 74
peak: 25
source runs: 1

Side by side

RxDart

FxDart

Why they differ

Both models hit the same wall here: their sources restart per consumer. Listening to a plain single-subscription stream twice is an error; iterating a sync* generator twice quietly runs it twice. And both libraries answer with the same idea — share one pass. RxDart makes the stream connectable: publish() defers the source, both reductions subscribe, and connect() starts the single subscription that feeds them. FxDart's tee keeps both reductions in step instead: each element advances the total and the peak before the next one is pulled, so the single pass never has to remember anything.

Both avoid a buffer, and for the same underlying reason — every reader sees each element while it is the current one. That is what connect() buys by making the readers attach first, and what tee buys by taking the readers as folds: a seed and a step, rather than two pipelines free to advance independently. The constraint is the price. publish() will feed any stream operators you care to subscribe; tee only feeds folds. When the two readers really are independent pipelines, FxDart's answer is fork — every fork of the same iterable object is a cursor over one shared, buffered pass — and there the buffer comes back, holding every value until the slowest cursor has consumed it. So this is a tie on capability: the general tool costs memory on both sides, and the specialised one is free on both. Pick the one matching the model the rest of your code already lives in.

Benchmark

Apple M1 Max, 32 GB RAM · Dart 3.12.2 (AOT-compiled) · 2026-08-18

N = 100

Time Tie

RxDart 20 µs
FxDart 2.3 µs

Peak memory Tie

RxDart 16.7 MB
FxDart 16.3 MB

N = 1,000,000

Time FxDart wins

RxDart 139.4 ms
FxDart 17.3 ms

Peak memory FxDart wins

RxDart 24.3 MB
FxDart 21.5 MB

Bars are medians of repeated timed iterations in fresh processes per side (small N is batched for timer resolution). Sides within 5% of each other — or within 0.6 ms, a difference no person can perceive — count as a tie; close relative races are re-measured up to 5 runs. In an app, anything under a few milliseconds is invisible to the user regardless of which bar is shorter. Memory is peak process RSS. The Dart VM and the dataset are identical on both sides, so the difference between the two bars is what the pipeline itself holds onto.