BehaviorSubject (RxDart)

Medium PriorityAsked in ~45% of mid-level interviews

4 min read

State Management

StreamController<T>BehaviorSubject<T>PublishSubject<T>ReplaySubject<T>
Initial / current value✅ (seeded)Replays N values
Late subscriber sees…NothingLatest valueFuture emissions onlyPast N + future
Synchronous .value getter
Single or broadcastEitherAlways broadcastAlways broadcastAlways broadcast

Mental model: BehaviorSubject is a hot variable + stream rolled into one.


Code in action — search with debounce and switchMap

import 'package:rxdart/rxdart.dart';

class SearchBloc {
  SearchBloc(this._api) {
    results = _query
        .debounceTime(const Duration(milliseconds: 300))   // wait for typing pause
        .distinct()                                         // ignore repeats
        .where((q) => q.length >= 2)
        .switchMap((q) => _api.search(q)                    // cancel in-flight on new query
            .asStream()
            .startWith(<Result>[])                          // show empty while loading
            .onErrorReturn(<Result>[]))
        .share();                                           // one upstream sub, many listeners
  }

  final ApiClient _api;
  final _query = BehaviorSubject<String>.seeded('');
  late final Stream<List<Result>> results;

  String get current => _query.value;                       // read synchronously
  void search(String q) => _query.add(q);
  void dispose() => _query.close();
}
// Combine multiple streams of state into one
final state = Rx.combineLatest3<User?, List<Notif>, Settings, DashboardState>(
  _user, _notifs, _settings,
  (u, n, s) => DashboardState(u, n, s),
);

When to use which stream type

GoalUse
Reactive state with a "current value"BehaviorSubject
Event stream — past events don't matterPublishSubject
Need to replay last N events to late subscribersReplaySubject
Simple async, no operatorsPlain Stream / StreamController
Combine / debounce / switchMapRxDart
Anything in pure Flutter without an extra dependencyPlain streams + manual logic, or Riverpod's stream providers

Common mistakes to avoid

// ❌ Forgetting to close — leaks
final _subject = BehaviorSubject<String>();
// dispose() override missing → controller lives forever

// ❌ Using mergeMap when switchMap is correct for cancellation
.mergeMap((q) => api.search(q))      // results come back out of order
// ✅ switchMap cancels stale requests when a new query arrives

// ❌ Skipping .share() when many widgets listen → each gets its own upstream
final r = results.where(...);        // every listener triggers the whole chain
// ✅ results = stream.share() or .shareReplay()

// ❌ Reading .value before any event was added
final s = BehaviorSubject<String>();
print(s.value);                      // throws — no value yet
// ✅ BehaviorSubject.seeded('initial')

// ❌ Adopting RxDart for simple state where setState/Provider would do
// RxDart shines for complex stream pipelines. For "is a dialog open" — overkill.

Interview follow-ups

  1. What's the difference between BehaviorSubject and ValueNotifier? Both expose a current value + change notifications. ValueNotifier ships with Flutter, is synchronous, and integrates with AnimatedBuilder/ValueListenableBuilder. BehaviorSubject is a stream — you get operator composition (debounce, combineLatest, switchMap) and can pass it across isolates of code that already speak streams. Use ValueNotifier for simple UI bindings, BehaviorSubject when you actually need stream operators.

  2. When does switchMap matter vs flatMap / mergeMap? switchMap cancels the previous inner stream when a new value arrives — essential for search, autocomplete, and rapid input where stale results would race. mergeMap (flatMap) runs all inner streams concurrently — fine for fire-and-forget side effects, dangerous for search.

  3. What does .share() actually do? It multicasts a stream — one subscription upstream, fanned out to N downstream listeners. Without it, each subscriber starts its own upstream pipeline. .shareReplay(maxSize: 1) is .share() + buffer of the last value, so late subscribers get the most recent emission.

  4. When would you NOT use RxDart in a Flutter app? When you'd add it just for BehaviorSubject (replace with ValueNotifier), when your state manager already covers stream-style flows (Riverpod's StreamProvider, BLoC's built-in stream handling), or when you're optimising for bundle size and don't actually use the operators. RxDart is a hammer — make sure the nail is there.


How helpful was this content?

Please sign in to rate this article.