A lightweight and complete functional error-handling toolkit for Dart, featuring Result, Option, Validation, AsyncResult, stream utilities, and a minimal Loadable state.
Tiny, complete, and functional.
English | δΈζ
π High Performance - Optimized for speed and memory efficiency
π Type Safe - Full null safety and strong typing
π Chainable - Fluent API with method chaining
π¦ Batch Operations - Process collections efficiently
π― Validation - Accumulate multiple errors elegantly
β‘ Async Support - First-class async/await integration
π§© Extensions - Native Dart type integrations
Type-safe error handling inspired by Rust's Result type.
import 'package:resx/resx.dart';
// Create results
final success = Result.ok(42);
final failure = Result.err('Something went wrong');
// Chain operations
final result = Result.ok(5)
.map((x) => x * 2)
.flatMap((x) => x > 5 ? Result.ok(x) : Result.err('Too small'));
// Handle both cases
final message = result.fold(
(value) => 'Success: $value',
(error) => 'Error: $error',
);
// Exception catching
final parsed = Result.from(() => int.parse('42')); // Ok(42)
final failed = Result.from(() => int.parse('abc')); // Err(FormatException)
// Guards and swap
final guarded = Result.ok(10).ensure((v) => v > 5, 'Too small');
final swapped = Result.ok(1).swap(); // Err(1)
Safe nullable value handling with Some/None variants.
// Create options
final some = Option.some('Hello');
final none = Option<String>.none();
// Safe operations
final result = Option.some('world')
.map((s) => s.toUpperCase())
.filter((s) => s.length > 3)
.unwrapOr('DEFAULT');
// From nullable
String? nullable = getValue();
final option = Option.fromNullable(nullable);
Collect multiple validation errors instead of failing fast.
// Built-in validators
final emailValidation = Validators.email('[email protected]', 'Invalid email');
final rangeValidation = Validators.range(25, 18, 65, 'Age out of range');
// Accumulate errors
final userValidation = Validators.notEmpty('John', 'Name required')
.and(Validators.email('invalid-email', 'Email invalid'))
.and(Validators.range(150, 0, 120, 'Age invalid'));
// Result: Invalid(['Email invalid', 'Age invalid'])
First-class async support for Result operations.
// Create async results
final asyncResult = AsyncResult.ok(42);
final fromFuture = AsyncResult.from(fetchData());
// Chain async operations
final result = await AsyncResult.ok(5)
.map((x) => x * 2)
.flatMap((x) => AsyncResult.ok(x + 1));
// Handle async errors safely
final safeResult = await AsyncResult.from(riskyOperation())
.orElse((error) => AsyncResult.ok('fallback'));
// Guard async values
final ensured = await AsyncResult.ok<int, String>(10)
.ensure((v) => v > 0, 'non-positive');
// Parsing with results
final number = '42'.parseInt(); // Ok(42)
final invalid = 'abc'.parseInt(); // Err(FormatException)
// Validation
final email = '[email protected]'.validateEmail(); // Valid(...)
final url = Validators.url('https://example.com'); // Valid(...)
final numbers = [1, 2, 3, 4, 5];
final results = numbers.map((x) => x.isEven ? Result.ok(x) : Result.err('odd'));
final combined = Results.sequence(results); // Ok([...]) or first Err
final (oks, errs) = Results.partition(results);
// Stream helpers
final stream = Stream.fromIterable([1,2,3]);
final asResults = stream.toResultStream<Object>();
final collected = await stream.collectToResult<Object>();
String? nullable = getValue();
final option = nullable.toOption(); // Option<String>
final result = nullable.toResult('Value is null'); // Result<String, String>
// Universal: wrap any value fast
final r1 = 42.ok<String>(); // Result<int, String>::Ok(42)
final r2 = 'boom'.err<int>(); // Result<int, String>::Err('boom')
final o1 = 'hello'.some(); // Option<String>::Some('hello')
// Combine multiple results
final results = [Result.ok(1), Result.ok(2), Result.ok(3)];
final combined = Results.combine(results); // Ok([1, 2, 3])
// Partition successes and errors
final mixed = [Result.ok(1), Result.err('error'), Result.ok(3)];
final (values, errors) = Results.partition(mixed); // ([1, 3], ['error'])
// Applicative operations
final sum = Results.lift2(
Result.ok(2),
Result.ok(3),
(a, b) => a + b,
); // Ok(5)
// Using built-in predicate helper
final validation = Validators.predicate<String, String>(
'test',
(v) => v.startsWith('prefix_'),
'Must start with prefix_',
); // Invalid(['Must start with prefix_'])
final result = Result.ok(42);
final message = result.match(
(value) => 'Got value: $value',
(error) => 'Got error: $error',
);
final pipeline = (String input) => Result.ok(input)
.flatMap(validateInput)
.flatMap(processData)
.flatMap(saveToDatabase)
.map(formatResponse);
final result = pipeline('user input');
Focused API, minimal indirections, idiomatic Dart sealed classes and extension types. No magic.
Add to your pubspec.yaml
:
dependencies:
resx: any
Then run:
dart pub get
Check out the example directory for comprehensive usage examples:
- Basic Usage - Core functionality
- Enhanced Features - All features showcase
Complete API documentation with examples is available at pub.dev.
Contributions are welcome! Please read our contributing guidelines and feel free to submit issues and pull requests.
This project is licensed under the MIT License - see the LICENSE file for details.
Made with π by FlutterCandies