A simple and powerful library for handling results (successes and failures) in Dart, inspired by Either
from functional programming libraries like dartz
. This library uses Success
and Failure
classes to represent outcomes.
map
, mapFailure
, flatMap
, bind
and when
for chaining and transforming results in a concise and readable way.getOrElse
and getOrThrow
for gracefully handle errorsisSuccess
/isFailure
getters and safe value access with getOrNull
/getErrorOrNull
tap
and tapError
methodsorElse
methodfilterOrElse
methodsequence
static methodtryCatch
, tryCatchAsync
, and fromNullable
zipWith
methodUnit
type for operations that don’t return meaningful valuesHere’s how to use the library:
import 'package:result_handler/result_handler.dart';
// Using the new tryCatchAsync factory method
Future<Result<String, int>> fetchData() async {
return Result.tryCatchAsync(
() async {
await Future.delayed(const Duration(seconds: 1)); // Simulate work
if (DateTime.now().second % 2 == 0) {
return 42; // Success case
} else {
throw Exception("Data fetch failed"); // This will be caught
}
},
(error, stackTrace) => "Network error: ${error.toString()}",
);
}
// Example of a validation function
Result<String, int> validateAge(int? age) {
return Result.fromNullable(age, () => "Age cannot be null")
.filterOrElse(
(value) => value >= 0 && value <= 150,
(value) => "Invalid age: $value",
);
}
void main() async {
final result = await fetchData();
// Basic pattern matching
result.when(
success: (value) => print('Success: $value'),
failure: (error) => print('Error: $error'),
);
// Using convenience getters
if (result.isSuccess) {
print('Operation succeeded with value: ${result.getOrNull()}');
}
// Chain operations with tap for side effects
final processedResult = result
.tap((value) => print('Processing value: $value')) // Side effect
.map((value) => value * 2)
.tapError((error) => print('Logging error: $error')); // Error side effect
// Error recovery
final recoveredResult = result.orElse((error) => Success(0));
print('Recovered value: ${recoveredResult.getOrElse((_) => -1)}');
// Working with multiple results
final results = [Success<String, int>(1), Success<String, int>(2), Success<String, int>(3)];
final sequenceResult = Result.sequence(results);
sequenceResult.when(
success: (values) => print('All values: $values'),
failure: (error) => print('One failed: $error'),
);
// Combining results
final result1 = Success<String, int>(10);
final result2 = Success<String, int>(20);
final combined = result1.zipWith(result2, (a, b) => a + b);
print('Combined: ${combined.getOrElse((_) => 0)}');
// Validation example
final ageValidation = validateAge(25);
ageValidation.when(
success: (age) => print('Valid age: $age'),
failure: (error) => print('Validation error: $error'),
);
// Using Unit for operations without meaningful return values
final saveResult = Result.tryCatch<String, Unit>(
() {
// Simulate a save operation
print('Saving data...');
return const Unit();
},
(error, stackTrace) => 'Save failed: ${error.toString()}',
);
saveResult.when(
success: (_) => print('Save completed successfully'),
failure: (error) => print('Save failed: $error'),
);
}
Result<E, T>
(Abstract Class)Represents the result of an operation, which can be either a Success<E, T>
or a Failure<E, T>
.
bind<R>(Result<E, R> Function(T value) transform)
: Binds a function to the Result
.flatMap<R>(Result<E, R> Function(T value) transform)
: Similar to map but the transformation returns another Result.map<R>(R Function(T value) transform)
: Transforms the value inside a Success
, otherwise does nothing.mapFailure<R>(R Function(E error) transform)
: Transforms the error inside a Failure
, otherwise does nothing.when<R>({required R Function(T data) success, required R Function(E error) failure})
: Executes the success
callback if it’s a Success
or failure
if it’s a Failure
.getOrElse(T Function(E error) orElse)
: Returns the value if it’s a Success
, otherwise returns the result of the orElse callback.getOrThrow()
: Returns the value if it’s a Success
, otherwise throws the error.getOrNull()
: Returns the value if it’s a Success
, otherwise returns null
.getErrorOrNull()
: Returns the error if it’s a Failure
, otherwise returns null
.isSuccess
: Returns true
if this is a Success
.isFailure
: Returns true
if this is a Failure
.tap(void Function(T value) fn)
: Executes a function with the success value for side effects.tapError(void Function(E error) fn)
: Executes a function with the error value for side effects.orElse(Result<E, T> Function(E error) recoveryFn)
: Attempts to recover from a failure.filterOrElse(bool Function(T value) predicate, E Function(T value) errorIfFalse)
: Filters success values based on a predicate.zipWith<U, R>(Result<E, U> other, R Function(T a, U b) combiner)
: Combines this result with another result.Result.fromNullable<E, T>(T? value, E Function() errorIfNull)
: Creates a Result from a nullable value.Result.tryCatch<E, T>(T Function() fn, E Function(Object error, StackTrace stackTrace) onError)
: Safely executes a function and wraps the result.Result.tryCatchAsync<E, T>(Future<T> Function() fn, E Function(Object error, StackTrace stackTrace) onError)
: Safely executes an async function and wraps the result.Result.sequence<E, T>(Iterable<Result<E, T>> results)
: Transforms multiple results into a single result containing a list.Success<E, T>
(Class)Represents a successful result, holding a value of type T
.
data
: The successful value of type T
.Result
methods with success-specific behavior.toString()
representation.Failure<E, T>
(Class)Represents a failed result, holding an error of type E
.
error
: The error value of type E
.Result
methods with failure-specific behavior.toString()
representation.Unit
(Class)Represents a unit type for operations that complete successfully but don’t return meaningful values.
Result<E, Unit>
when you only care about success/failure, not the return value.Unit
instances are considered equal.This library is licensed under the MIT License. See the LICENSE file for more details.
See the CHANGELOG.md file for a detailed history of changes.
Contributions are welcome! Please feel free to submit issues or pull requests.