tool_result 0.0.6 copy "tool_result: ^0.0.6" to clipboard
tool_result: ^0.0.6 copied to clipboard

Contains result pattern implementation

tool_result #

A Dart/Flutter library for handling Loading-Content-Error (LCE) state in a reactive, ergonomic, and type-safe way using the Result<T> and PaginationResult<T> patterns.

Features #

  • Simple and explicit LCE state management with Result<T> and PaginationResult<T>
  • Stream and Future extensions for easy LCE handling
  • Ergonomic API for pattern matching, mapping, and extracting content
  • RxDart and plain Dart Streams compatibility
  • Utilities for mapping to view models
  • Fetcher architecture for reactive, cache-aware data loading

Getting Started #

Add to your pubspec.yaml:

dependencies:
  tool_result:
    path: tools/tool_result

The LCE Pattern #

Result<T> and PaginationResult<T> represent one of three states:

  • Loading: The operation is in progress
  • Content: The operation succeeded and data is available
  • Error: The operation failed

Usage #

Creating Results #

final loading = Result<String>.loading();
final content = Result<String>.content('Hello');
final error = Result<String>.error(Exception('Oops'), StackTrace.current);

Creating PaginationResults #

final loading = PaginationResult<List<String>>.loading();
final content = PaginationResult<List<String>>.content(['A', 'B']);
final error = PaginationResult<List<String>>.error(Exception('Oops'), StackTrace.current);

Pattern Matching #

result.when(
  content: (data) => print('Content: $data'),
  loading: (_) => print('Loading...'),
  error: (err, _) => print('Error: $err'),
);

final value = result.maybeWhen(
  content: (data) => data,
  orElse: () => 'default',
);

paginationResult.when(
  content: (data) => print('Page Content: $data'),
  loading: (_) => print('Page Loading...'),
  error: (err, _) => print('Page Error: $err'),
);

Streams #

// Convert a stream to a Result stream
final resultStream = myStream.asResultStream();

// Listen for LCE events
resultStream.listenLce(
  onContent: (data) => print('Content: $data'),
  onError: (err, _) => print('Error: $err'),
  onLoading: (_) => print('Loading...'),
);

// Extract only content values
resultStream.extractContent().listen(print);

// Convert a stream to a PaginationResult stream
final pageStream = myPageStream.asPaginationResultStream();

// Listen for pagination LCE events
pageStream.listenLce(
  onContent: (data) => print('Page Content: $data'),
  onError: (err, _) => print('Page Error: $err'),
  onLoading: (_) => print('Page Loading...'),
);

// Extract only content values from pagination
pageStream.map((pr) => pr.content).listen(print);

Futures #

final resultFuture = myFuture.mapWithResult();

Utilities #

  • distinctContent() — Only emit when content changes
  • debounceContent(Duration) — Debounce content emissions
  • skipWhileLoading() — Skip loading states

Fetchers: Reactive Data Loading #

The fetcher architecture provides a unified, reactive, and cache-aware way to load data from network, memory, and storage. Fetchers expose a stream of LCE states and handle caching, storage sync, and network requests for you.

ResultFetcherDelegate #

For single-result data (not paginated):

final fetcher = ResultFetcherDelegate<MyParams, MyData>(
  fromNetwork: (params) => api.getData(params),
  observeFromStorage: (params) => db.observeData(params),
  toStorage: (params, data) => db.saveData(params, data),
);

fetcher.observe(params: ...).listen((result) {
  if (result.isContent) print(result.content);
});

PaginationResultFetcherDelegate #

For paginated data:

final fetcher = PaginationResultFetcherDelegate<MyParams, List<MyData>>(
  fromNetwork: (params) => api.getPage(params),
  concatContent: (prev, next) => [...?prev, ...next],
  contentLength: (content) => content?.length ?? 0,
);

fetcher.observe(params: ...).listen((result) {
  if (result.content != null) print(result.content);
});

PaginationListResultFetcherDelegate #

For paginated lists (convenience wrapper):

final fetcher = PaginationListResultFetcherDelegate<MyParams, MyData>(
  fromNetwork: (params) => api.getPage(params),
);

fetcher.observe(params: ...).listen((result) {
  if (result.content != null) print(result.content);
});

Best Practices #

  • Use fetchers for all async data sources to unify loading, content, and error handling.
  • Use storage and memory caching for performance and offline support.
  • Use stream extensions for reactive UIs.
  • Use pattern matching (when, maybeWhen) for ergonomic state handling.

License #

MIT