dart_observable 1.0.5 copy "dart_observable: ^1.0.5" to clipboard
dart_observable: ^1.0.5 copied to clipboard

A state management library using the Observer pattern, optimized for notifying changes in collections.

example/dart_observable_example.dart

import 'package:dart_observable/dart_observable.dart';

Future<void> main() async {
  final Controller controller = Controller();

  final Disposable counterListener = controller.rxCounter.listen(
    onChange: (final int value) {
      print('Counter changed to $value');
    },
  );

  controller.increment();
  final int rxCounter = controller.rxCounter.value;
  assert(rxCounter == 1);

  final Disposable listListener = controller.rxItems.listen(
    onChange: (final List<String> immutableList) {
      final ObservableListChange<String> change = controller.rxItems.change;

      print('list size: ${immutableList.length}');
      final Map<int, String> added = change.added;
      final Map<int, String> removed = change.removed;
      final Map<int, ObservableItemChange<String>> updated = change.updated;

      if (added.isNotEmpty) {
        for (final MapEntry<int, String> entry in added.entries) {
          print('added to position: ${entry.key} ${entry.value}');
        }
      }

      if (removed.isNotEmpty) {
        for (final MapEntry<int, String> entry in removed.entries) {
          print('removed from position: ${entry.key} ${entry.value}');
        }
      }

      if (updated.isNotEmpty) {
        for (final MapEntry<int, ObservableItemChange<String>> entry in updated.entries) {
          print('updated at position: ${entry.key} from ${entry.value.oldValue} to ${entry.value.newValue}');
        }
      }
    },
  );

  /// The observable is cold, so it will not compute the initial state until it is subscribed to.
  final Disposable mapListener = controller.rxItemsUppercasedMap.listen();
  final Disposable itemListener = controller.rxItemsUppercasedByItem.listen();

  /// The initial state from the source is mapped to the target.
  assert(controller.rxItemsUppercasedMap.value.length == 3);
  assert(controller.rxItemsUppercasedByItem.length == 3);

  controller.addItem('item4');
  assert(controller.rxItemsUppercasedByItem.length == 4);
  assert(controller.rxItemsUppercasedByItem[3] == 'ITEM4');

  assert(controller.rxItemsUppercasedMap.value.length == 4);
  // [] is not implemented on Observable<List>
  assert(controller.rxItemsUppercasedMap.value[3] == 'ITEM4');

  controller.updateItem(0, 'newItem');

  assert(controller.rxItemsUppercasedMap.value.length == 4);
  assert(controller.rxItemsUppercasedByItem.length == 4);

  assert(controller.rxItemsUppercasedByItem[0] == 'NEWITEM');
  assert(controller.rxItemsUppercasedMap.value[0] == 'NEWITEM');

  controller.removeItemAt(0);

  assert(controller.rxItemsUppercasedMap.value.length == 3);
  assert(controller.rxItemsUppercasedByItem.length == 3);

  assert(controller.rxItemsUppercasedByItem[0] == 'ITEM2');
  assert(controller.rxItemsUppercasedMap.value[0] == 'ITEM2');

  await listListener.dispose();
  await counterListener.dispose();
  await mapListener.dispose();
  await itemListener.dispose();
}

class Controller {
  /// Base mutable state, can hold any type.
  final Rx<int> _rxCounter = Rx<int>(0);
  final RxList<String> _rxItems = RxList<String>(<String>['item1', 'item2', 'item3']);

  /// Immutable state of the base mutable state.
  late final Observable<int> rxCounter = _rxCounter;

  /// Working with collection is always more complex because usually the returned value is mutable and modifications usually are not reactive.
  /// For collections (list, map, set), the returned value is always immutable.
  late final ObservableList<String> rxItems = _rxItems;

  /// Collection observables are used to handle changes within a collection.
  /// These changes are utilized in collection operators to compute only the differences.
  /// The base [Observable.map] operator does not handle the changes but can map to any type.
  late final Observable<List<String>> rxItemsUppercasedMap = _rxItems.map(
    (final List<String> immutableView) {
      return immutableView.map((final String item) => item.toUpperCase()).toList();
    },
  );

  /// [ObservableList.mapItem] does utilize the changes within the collection to compute the differences.
  /// So it is more efficient than [Observable.map] for collections because it will only compute the differences.
  /// On subscribing to a collection, the initial data is included in the first change.
  late final ObservableList<String> rxItemsUppercasedByItem =
      _rxItems.mapItem((final String item) => item.toUpperCase());

  void addItem(final String item) {
    _rxItems.add(item);
  }

  void increment() {
    _rxCounter.value++;
  }

  void removeItemAt(final int position) {
    _rxItems.removeAt(position);
  }

  void updateItem(final int position, final String newItem) {
    _rxItems[position] = newItem;
  }
}
copied to clipboard
6
likes
140
points
72
downloads

Publisher

unverified uploader

Weekly Downloads

2024.09.16 - 2025.03.31

A state management library using the Observer pattern, optimized for notifying changes in collections.

Repository (GitHub)
View/report issues

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

collection

More

Packages that depend on dart_observable