25 Essential RxDart Operators Every Flutter Engineer Should Know

In the previous article we explored Subjects in RxDart, including:

  • PublishSubject
  • BehaviorSubject
  • ReplaySubject

Subjects act as entry points for reactive streams, but they are not the real power of Rx.

The real strength of reactive programming lies in operators.

Operators allow you to build data pipelines where streams can be transformed, combined, filtered, or controlled.

Conceptually:

Input Stream

Operator

Operator

Operator

Output Stream

Example:

queryStream
.debounceTime(Duration(milliseconds: 300))
.distinct()
.switchMap(api.search)
.listen(showResults);

This simple pipeline solves multiple problems:

  • prevents excessive API calls
  • avoids duplicate searches
  • cancels outdated requests
  • ensures only the latest results are shown

In this article we will cover 25 essential operators grouped into five categories.


1. Stream Transformation Operators

These operators transform data values.

1. map

Transforms each event.

Example:

Stream.fromIterable([1,2,3])
.map((v) => v * 2)
.listen(print);

Output:

2
4
6

Use case:

  • converting API responses
  • mapping DTO → domain model

Example in Flutter:

api.getUsers()
.map((dto) => User.fromDto(dto));

2. where

Filters events.

Stream.fromIterable([1,2,3,4])
.where((v) => v.isEven)
.listen(print);

Output:

2
4

Use case:

  • filtering invalid input
  • removing unwanted events

3. scan

Accumulates values (like reduce but continuous).

Example:

Stream.fromIterable([1,2,3])
.scan((acc, value, _) => acc + value, 0)
.listen(print);

Output:

1
3
6

Use case:

  • cart totals
  • running counters
  • accumulating state

4. buffer

Collects events into batches.

Example:

stream.bufferCount(3)

Input:

1 2 3 4 5 6

Output:

[1,2,3]
[4,5,6]

Use case:

  • batching analytics
  • reducing API calls

5. distinct

Removes duplicate consecutive values.

Example:

Stream.fromIterable([1,1,2,2,3])
.distinct()
.listen(print);

Output:

1
2
3

Useful for preventing unnecessary UI updates.


2. Time-Based Operators

These control event timing.


6. debounceTime

Waits before emitting events.

Example:

queryStream.debounceTime(Duration(milliseconds: 300));

Typing:

A
Ap
App
Appl
Apple

Only emits:

Apple

Use case:

  • search boxes
  • typing events

7. throttleTime

Limits how frequently events emit.

Example:

scrollStream.throttleTime(Duration(milliseconds: 200));

Used for:

  • scroll events
  • drag gestures

8. delay

Delays event emission.

stream.delay(Duration(seconds: 1));

Useful for:

  • UI animations
  • loading indicators

9. interval

Forces a delay between events.

stream.interval(Duration(milliseconds: 100));

Use case:

  • rate limiting
  • controlled animations

10. timeout

Throws an error if no event occurs.

stream.timeout(Duration(seconds: 5));

Useful for:

  • network request monitoring

3. Combination Operators

These combine multiple streams.


11. combineLatest

Emits when any stream updates, using latest values.

Example:

Rx.combineLatest2(
emailStream,
passwordStream,
(email, password) => email.isNotEmpty && password.length > 6,
);

Used for:

  • form validation

12. zip

Pairs events in order.

Example:

Stream A: 1 2 3
Stream B: A B C

Output:

(1,A)
(2,B)
(3,C)

Useful when streams must stay synchronized.


13. merge

Merges multiple streams.

Rx.merge([
streamA,
streamB,
]);

Output contains events from both streams.

Use case:

  • merging UI events
  • combining notifications

14. concat

Processes streams sequentially.

Rx.concat([stream1, stream2]);

Stream2 starts only after Stream1 completes.

Useful for:

  • sequential network tasks

15. withLatestFrom

Combines with the latest value of another stream.

Example:

buttonClick.withLatestFrom(userState, (click, user) => user);

Used for:

  • sending requests with current state

4. Flattening Operators

These operators are critical for handling async tasks.

They transform:

Stream<Event> → Stream<Stream<Result>>

into:

Stream<Result>

16. switchMap

Cancels previous stream when a new event arrives.

Example:

queryStream.switchMap(api.search);

Scenario:

search: A
search: Ap
search: App

Only latest request survives.

Use case:

  • search APIs
  • autocomplete

17. flatMap (mergeMap)

Runs multiple streams concurrently.

Example:

stream.flatMap(api.request);

All requests continue simultaneously.

Use case:

  • sending multiple independent API calls

18. concatMap

Processes async tasks sequentially.

Example:

stream.concatMap(api.request);

Ensures order.

Use case:

  • file uploads
  • ordered transactions

19. exhaustMap

Ignores new events while processing.

Example:

buttonClicks.exhaustMap(api.submit);

If the user clicks repeatedly:

click click click click

Only the first request executes.

Useful for:

  • preventing duplicate submissions

20. asyncExpand

Expands each event into multiple events.

Example:

stream.asyncExpand((value) async* {
yield value;
yield value * 2;
});

5. Utility Operators

These help manage streams.


21. startWith

Adds an initial event.

stream.startWith(0);

Useful for:

  • initial UI state

22. doOnData

Side-effect operator.

stream.doOnData((value) {
log(value);
});

Useful for debugging.


23. shareReplay

Shares and caches latest values.

stream.shareReplay(maxSize: 1);

Prevents multiple API calls when multiple listeners subscribe.


24. retry

Retries on failure.

stream.retry(3);

Useful for:

  • unstable network requests

25. onErrorResume

Recovers from errors.

stream.onErrorResume((error, stack) {
return Stream.value([]);
});

Prevents stream termination.


Real Flutter Example: Reactive Search

Let’s combine multiple operators.

Requirements

User types

debounce

cancel previous request

show results

Implementation:

class SearchBloc {

final _query = BehaviorSubject<String>();

Sink<String> get query => _query.sink;

late final Stream<List<Result>> results =
_query
.debounceTime(Duration(milliseconds: 300))
.distinct()
.switchMap(repository.search)
.shareReplay(maxSize: 1);

void dispose() {
_query.close();
}
}

This pipeline handles:

debounce typing
cancel outdated API calls
avoid duplicate requests
cache latest results

Real Flutter Example: Infinite Scroll

scroll position

throttle

load next page

append items

Example:

scrollStream
.throttleTime(Duration(milliseconds: 200))
.where((pos) => pos > threshold)
.flatMap(repository.loadMore);

Operator Selection Guide

ProblemOperator
Cancel old requestsswitchMap
Run requests in parallelflatMap
Sequential tasksconcatMap
Ignore duplicatesdistinct
Debounce typingdebounceTime
Combine form inputscombineLatest
Prevent spam clickingexhaustMap

Common Mistakes

Overusing Subjects

Bad:

Subject → Subject → Subject

Good:

Subject → Operators → Stream

Not understanding flattening operators

Using flatMap when switchMap is needed causes race conditions.


Ignoring stream lifecycle

Always close streams.


Key Takeaways

Operators are the heart of reactive programming in RxDart.

In this article we covered 25 essential operators, including:

  • transformations
  • timing controls
  • stream combinations
  • async flattening
  • utility tools

Mastering these operators allows Flutter engineers to build powerful reactive pipelines.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

Lên đầu trang