RxDart Documentation

repository·master·Indexed 25 days ago

https://github.com/reactivex/rxdart

A ReactiveX implementation for Dart that extends the native Stream API with powerful operators, Subjects (BehaviorSubject and ReplaySubject), and specialized Stream classes. Includes the rxdart_flutter package providing ValueStreamBuilder, ValueStreamListener, and ValueStreamConsumer for integrating reactive streams into Flutter UIs.

Tokens
11.2K
Snippets
38
Records
54
Agent score
85%

What's inside RxDart

  1. Overview of RxDart

    master
    RxDart is an implementation of the ReactiveX API for asynchronous programming in Dart. It leverages the native Dart Stream API to provide advanced operators and functional programming capabilities for handling asynchronous data streams.
  2. Run Command Line Examples

    master

    To run the command line examples (such as the Fibonacci example), follow these steps:

    1. Clone the repository and enter the root directory.
    2. Fetch dependencies using pub get.
    3. Execute the specific example script using dart, passing any required arguments (e.g., a number for the Fibonacci sequence).
    pub get
    dart examples/fibonacci/lib/example.dart 10
  3. Run Web Examples

    master

    To run the web-based examples provided in the repository, follow these steps in your terminal:

    1. Clone the repository and navigate to the examples/web directory.
    2. Fetch dependencies using dart pub get.
    3. Ensure webdev is installed globally via dart pub global activate webdev.
    4. Start the local server with webdev serve.
    5. Open your browser and navigate to http://localhost:8080/.
    cd examples/web
    dart pub get
    dart pub global activate webdev
    webdev serve
  4. Avoid hanging `async*` generators during cancellation

    master

    When using async* generators, avoid awaiting a never-completing Future immediately after a yield if downstream code relies on the stream's cancellation settling (e.g., using Stream.first, Stream.single, or await for with a break).

    If a generator enters an await that never completes, it cannot be cancelled. The cancellationFuture will never complete because the generator body never returns or throws, causing the calling code to hang indefinitely. This is a property of the Dart async* runtime, not a bug in RxDart.

    Best Practices:

    • Ensure async* generators can eventually return or throw an error so cancellation can complete.
    • For long-lived or open-ended sources, use a StreamController-based implementation with explicit onCancel cleanup instead of parking an async* generator on an infinite await. This makes cancellation semantics explicit and ensures the cancellation future settles predictably.
  5. Run Flutter Examples

    master

    To run the Flutter examples (e.g., github_search), ensure you have Flutter installed and an emulator or device connected. Follow these steps:

    1. Navigate to the examples/flutter/github_search directory.
    2. Verify your environment with flutter doctor.
    3. Fetch Flutter dependencies with flutter packages get.
    4. Launch the application with flutter run.
    cd examples/flutter/github_search
    flutter doctor
    flutter packages get
    flutter run
  6. Handle errors in ValueStream widgets

    master

    The rxdart_flutter widgets will display error widgets if the provided stream violates certain conditions. To prevent errors, ensure your stream meets these requirements:

    1. Always has an initial value: Use BehaviorSubject.seeded(value) instead of a plain BehaviorSubject(). If a stream has no initial value, it will throw ValueStreamHasNoValueError.
    2. Never emits errors: Ensure errors are handled before they reach the widget (e.g., using stream.handleError()). If a stream emits an error, it will throw UnhandledStreamError.

    Correct Initialization Examples:

    // Good - stream has initial value
    final goodStream = BehaviorSubject<int>.seeded(0);
    
    // Bad - stream has no initial value (throws ValueStreamHasNoValueError)
    final badStream = BehaviorSubject<int>();
    
    // Bad - stream with error (throws UnhandledStreamError)
    final errorStream = BehaviorSubject<int>.seeded(0)..addError(Exception());
  7. Avoid hanging when using `Stream.first` with `async*` generators and `combineLatest`

    master

    A known issue exists when composing async* generators that contain infinite await calls (e.g., await Completer<void>().future) using operators like Rx.combineLatest2 and switchMap.

    When you call await stream.first, the stream attempts to cancel its subscriptions once the first value is received. However, if one of the source streams is an async* generator suspended at an await (rather than a yield), Dart's runtime does not resume the generator upon cancellation. This causes the generator's cancellationFuture to remain incomplete. Because Rx.combineLatest waits for all source subscriptions to cancel via Future.wait, the entire cancellation chain hangs, and await stream.first will never complete (or will time out).

    To avoid this:

    • Ensure async* generators do not stay suspended at an infinite await if they are part of a combined stream that needs to be canceled.
    • If using long-lived watchers (like Drift watchers) wrapped in async*, ensure they can be properly closed or that the composition doesn't rely on Stream.first which triggers a full subscription cancellation.
    import 'dart:async';
    import 'dart:io';
    import 'package:rxdart/rxdart.dart';
    
    Future<void> main() async {
      final stream = Rx.combineLatest2(
        _emitOnceAndNeverClose('left'),
        Stream.value('right').switchMap(_emitOnceAndNeverClose),
        (left, right) => '$left|$right',
      );
    
      // This will hang or timeout if the generators are stuck at an await
      final value = await stream.first.timeout(const Duration(milliseconds: 200));
      stdout.writeln(value);
    }
    
    Stream<T> _emitOnceAndNeverClose<T>(T value) async* {
      yield value;
      await Completer<void>().future; // This infinite await causes the hang on cancellation
    }
  8. Transform Streams using Extension Methods

    master

    RxDart provides extension methods that can be called on any Dart Stream to transform it into a new Stream with different capabilities, such as throttling, buffering, or delaying events.

    Example of using throttleTime to limit event frequency:

    Stream.fromIterable([1, 2, 3])
      .throttleTime(Duration(seconds: 1))
      .listen(print); // prints 1