Skip to content

Latest commit

 

History

History

rxdart_flutter

RxDart Flutter

RxDart

Pub Pub Version (including pre-releases) Gitter Flutter website Build Flutter example License Hits

rxdart_flutter is a Flutter package that provides a set of widgets for working with rxdart. This package is officially maintained in the ReactiveX/rxdart repository. These widgets are specifically designed to work with ValueStreams, making it easier to build reactive UIs in Flutter.

Overview

This package provides three main widgets:

  • ValueStreamBuilder: A widget that rebuilds UI based on ValueStream updates
  • ValueStreamListener: A widget that performs side effects when ValueStream values change
  • ValueStreamConsumer: A widget combining both builder and listener capabilities for ValueStreams

All widgets require a ValueStream that always has a value and never emits errors. If these conditions are not met, appropriate error widgets will be displayed.

ValueStreamBuilder

ValueStreamBuilder is a widget that builds itself based on the latest value emitted by a ValueStream. It's similar to Flutter's StreamBuilder but specifically optimized for ValueStreams.

Features

  • Always has access to the current value (no AsyncSnapshot needed)
  • Optional buildWhen condition for controlling rebuilds
  • Proper error handling for streams without values or with errors
  • Efficient rebuilding only when necessary

Example

final counterStream = BehaviorSubject<int>.seeded(0); // Initial value required

ValueStreamBuilder<int>(
  stream: counterStream,
  buildWhen: (previous, current) => current != previous, // Optional rebuild condition
  builder: (context, value, child) {
    return Column(
      children: [
        Text(
          'Counter: $value',
          style: Theme.of(context).textTheme.headlineMedium,
        ),
        if (child != null) child, // Use the stable child widget if provided
      ],
    );
  },
  child: const Text('This widget remains stable'), // Optional stable child widget
)

ValueStreamListener

ValueStreamListener is a widget that executes callbacks in response to stream value changes. It's perfect for handling side effects like showing snackbars, dialogs, or navigation.

Features

  • Access to both previous and current values in the listener
  • No rebuilds on value changes (unlike ValueStreamBuilder)
  • Child widget is preserved across stream updates
  • Guaranteed to only call listener once per value change
  • Optional child for stable widgets that remain unchanged across stream updates

Example

final authStream = BehaviorSubject<AuthState>.seeded(AuthState.initial);

ValueStreamListener<AuthState>(
  stream: authStream,
  listener: (context, previous, current) {
    if (previous.isLoggedOut && current.isLoggedIn) {
      Navigator.of(context).pushReplacementNamed('/home');
    } else if (previous.isLoggedIn && current.isLoggedOut) {
      Navigator.of(context).pushReplacementNamed('/login');
    }
  },
  child: MyApp(), // Child widget remains stable
)

ValueStreamConsumer

ValueStreamConsumer combines the functionality of both ValueStreamBuilder and ValueStreamListener. Use it when you need to both rebuild the UI and perform side effects in response to stream changes.

Features

  • Combined builder and listener functionality
  • Optional buildWhen condition for controlling rebuilds
  • Access to previous and current values in listener
  • Efficient handling of both UI updates and side effects
  • Optional child for stable widgets that remain unchanged across stream updates

Example

final cartStream = BehaviorSubject<Cart>.seeded(Cart.empty());

ValueStreamConsumer<Cart>(
  stream: cartStream,
  buildWhen: (previous, current) => current.itemCount != previous.itemCount,
  listener: (context, previous, current) {
    if (current.itemCount > previous.itemCount) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Item added to cart')),
      );
    }
  },
  builder: (context, cart, child) {
    return Column(
      children: [
        Text('Total items: ${cart.itemCount}'),
        Text('Total price: \$${cart.totalPrice}'),
        if (child != null) child, // Use the stable child widget if provided
      ],
    );
  },
  child: const Text('This widget remains stable'), // Optional stable child widget
)

Error Handling

All widgets in this package handle two types of errors:

  1. ValueStreamHasNoValueError: Thrown when the stream doesn't have an initial value
  2. UnhandledStreamError: Thrown when the stream emits an error

To avoid these errors:

  • Always use BehaviorSubject or another ValueStream with an initial value
  • Handle stream errors before they reach these widgets
  • Consider using stream.handleError() to transform errors if needed

Example of proper stream initialization:

// Good - stream has initial value
final goodStream = BehaviorSubject<int>.seeded(0);

// Bad - stream has no initial value
final badStream = BehaviorSubject<int>(); // Will throw ValueStreamHasNoValueError

// Bad - stream with error
final errorStream = BehaviorSubject<int>.seeded(0)..addError(Exception()); // Will throw UnhandledStreamError