Functional programming for Dart,
with laziness and concurrency built in.

FxDart is a Dart port of FxTS — a library for composing lazy pipelines over sync and async data, where turning six sequential 1-second requests into a 2-second concurrent batch is one method call.

📒 See it in action: Daily Ledger — a full app built with FxDart →

⚖️ Dart vs FxDart — 53 real tasks, side by side →

RxDart vs FxDart — push vs pull, 50 honest verdicts →

▲ This is live — edit the code and press Run. It compiles with the real Dart compiler and executes in your browser.

What is FxDart?

FxDart brings the FxTS programming model to Dart: a toolkit of ~120 small, composable functions for transforming collections and asynchronous data. Three ideas define it:

Events over time, and failures as values, live on the other two surfaces. Which surface? is the decision; this page stays the marketing one.

Why do we need it?

Dart already has Iterable and Stream. FxDart earns its place where those run out:

ProblemPlain DartFxDart
Limit concurrent API calls to n, keep order Manual queues, Completers, careful bookkeeping .map(fetch).concurrent(3)
Async transform pipelines Stream is push-based; mixing await, backpressure and laziness gets intricate Pull-based chain — each value is computed only when asked for
Data wrangling (group, index, count, partition, zip, chunk…) Hand-rolled loops each time One well-tested named function per concept
Readable multi-step transforms Nested calls or intermediate variables Left-to-right fx() chains, fully typed

Pros & Cons

✓ Pros

  • Laziness for free — pipelines short-circuit; only requested values are computed.
  • Order-preserving concurrency with a single operator: concurrent(n) / completion-order concurrentPool(n).
  • Fully typed chainsfx() keeps inference end-to-end; sync operators are plain functions over native Iterables, so everything interops with ordinary Dart.
  • Small, focused functions — ~120 operators covering transform / filter / slice / combine / aggregate / object / util.
  • Battle-tested semantics — behavior ported from FxTS along with 850+ of its tests.
  • Zero dependencies — pure Dart.

✗ Cons

  • No data-last currying — Dart lacks function overloads, so FxTS's curried pipe style becomes chains; the dynamic pipe() loses static types.
  • A second async abstractionFxAsyncIterable exists because Stream can't express the concurrency back-channel; bridging is easy but it is one more concept to learn.
  • Learning curve — thinking in lazy pipelines differs from imperative loops.
  • Not always fastest — for tiny hot loops, a hand-written for can beat operator composition; FxDart optimizes for clarity and I/O-bound work.
  • Some TS APIs don't port literally — they get Dart-native spellings instead: curry becomes the typed .curried extension getter, with the old names kept as deprecated stubs for migration.

Taste of concurrency

Six fake requests of 300 ms each — sequentially ~1.8 s, with concurrent(3) about 0.6 s. Try changing the number:

Start learning

FxDart 101 →

A guided course: lectures, live demos, and a playground for every function.

First lesson: map →

Start with the most fundamental operator.

Install →

Get fxdart into your pubspec and browse the source.