type_result
type_result
is a simple yet powerful Flutter package that helps manage operation results effectively. It introduces a Result
type, which can represent either a successful outcome (Success
) or an error (Failure
). This makes handling errors clean, safe, and predictable.
Installation
To use type_result
, add it to your pubspec.yaml
file:
dependencies:
type_result: ^3.0.0
Then run:
flutter packages get
Usage
Basics of the Result
Class
The Result<V, E>
class is designed to handle operations that might succeed or fail. Here is an example:
Result<int, String> divide(int dividend, int divisor) {
if (divisor == 0) {
return Result.failure("Cannot divide by zero");
} else {
return Result.success(dividend ~/ divisor);
}
}
In this example:
V
represents the type of the successful result (e.g.,int
).E
represents the type of the error (e.g.,String
).
Here's how you might use the divide
function:
var result = divide(10, 2);
if (result.isSuccess) {
print('The result is ${result.success}.'); // The result is 5.
} else {
print('An error occurred: ${result.failure}.');
}
Handling Results with switch
The Result
type is a sealed class, which means you can use a switch
statement to handle each possible outcome:
switch (result) {
case Success(:final value):
print('The result is $value.');
case Failure(:final value):
print('An error occurred: $value.');
}
Useful Methods
isSuccess
: Returnstrue
if the result is successful (Success
).isFailure
: Returnstrue
if the result is an error (Failure
).successOrNull
: Returns the value if successful, ornull
otherwise.failureOrNull
: Returns the error if failed, ornull
otherwise.
For example:
var result = divide(10, 0);
print(result.successOrNull); // null
print(result.failureOrNull); // "Cannot divide by zero"
If you are sure about the result type, you can directly access the value or error using success
or failure
. Note that these will throw an AssertionError
if the result is not of the expected type.
Transforming Results
You can transform values or errors using:
mapSuccess
: Transforms the value if it'sSuccess
.mapFailure
: Transforms the error if it'sFailure
.successThen
andfailureThen
: Chain transformations and operations.
Example:
Result<int, String> result = divide(10, 2); // Success(5)
Result<String, String> stringResult = result.mapSuccess((value) => value.toString()); // Success("5")
You can also use successOr
and failureOr
to provide default values:
Result<int, String> result = divide(10, 0); // Failure("Cannot divide by zero")
print(result.successOr(0)); // 0
print(result.failureOr("No error")); // "Cannot divide by zero"
Asynchronous Support
type_result
also works well with asynchronous operations by extending Future<Result<V, E>>
. This allows you to chain operations in a clean and readable way, and also simplifies handling with await
.
For example, instead of writing:
final userId = (await authedUserId()).successOrNull;
you can write:
final userId = await authedUserId().successOrNull;
## Benefits of Using `Result`
Using the `Result` type helps you clearly indicate whether an operation succeeded or failed. It encapsulates both the success and error scenarios, making it less likely that you'll forget to handle an error or mistakenly assume success. This leads to more predictable, maintainable, and readable code.
By using `type_result`, you can avoid common error-handling pitfalls and write safer, cleaner code.