Enable submit when the form is valid
Requirement
A signup form has two fields. The email field emits
nam, then nam@fx.dev; the password field
emits hunter2, then box-belt-42 — at fixed
interleaved offsets. After every change once both fields have
emitted, re-evaluate the pair of latest values (valid = email
contains @, password ≥ 8 chars) and print the combined
state — three lines from the four field events, because the first
change lands before the password has spoken; finish with the submit
button's final state. Both versions must print the lines shown under
Expected output.
Expected output
email=nam password=hunter2 -> disabled email=nam@fx.dev password=hunter2 -> disabled email=nam@fx.dev password=box-belt-42 -> enabled submit: enabled
Side by side
RxDart
FxDart
Why they differ
They don't. Latest-value-per-source state is the
defining combinator of the push model, and both panels declare it
in one line: hold the newest value from each field, wait until both
have spoken, re-emit the pair on every change from either side.
RxDart writes Rx.combineLatest2(emails(), passwords(), ...);
fxdart writes fxEvents(emails()).combineLatest(passwords(),
...). Same waiting rule, same re-emission on either side, same
close-when-both-close — the tagged-merge-and-fold the fxdart panel
used to hand-roll is gone.
fxdart's events layer absorbs the Rx approach for exactly
this kind of job: a deliberate wrapper chain over plain
Streams — not an extension, so it coexists with rxdart or
any other stream library without conflicts — carrying the latest-value
combinators the pull pipelines cannot have. One honest caveat stands:
RxDart's operator catalog remains far larger than fxdart's events
layer. When the combined form state should drive typed, demand-driven
work — a validated submit call with typed errors, say —
.pull() crosses from the live pairs into the
FxAsync pipeline.
Benchmark
No benchmark for this example. This operator is defined by elapsed time, not by work done: both sides wait out the same windows, so a bar would be measuring Dart's timers rather than either library. Shrinking the windows does not help either — how many values a burst produces would then depend on how fast the machine is, and the two sides could no longer be checked against an identical result. The examples built on timing operators are left unmeasured for that reason; the ones whose delays merely stand in for I/O are benchmarked with those delays set to zero.