inline_result 1.1.0
inline_result: ^1.1.0 copied to clipboard
This package brings a Kotlin-like Result<T> to Dart, using extension types for zero-cost wrapping.
Inline Result π #
Welcome to Inline Result β your new best friend for functional error handling in Flutter/Dart. If you're coming from Android and miss Kotlin's slick Result
type, this package is here to save you from messy try/catch blocks and help you write safer, cleaner code.
This package brings a Kotlin-like Result<T>
to Dart, using extension types for zero-cost wrapping.
β¨ Features #
- β Functional Error Handling β Chain transformations without losing control.
- β Zero-Cost Wrapping β Uses Dart extension types, meaning no extra objects at runtime.
- β
Familiar API β Inspired by Kotlin's
Result<T>
, but Dart-friendly. - β
Safe & Readable β No more
null
checks or exceptions hiding in logs.
Why Inline Result? #
Flutter/Dart lacks a built-in, functional way to handle errors like Kotlin. With Dart Result, you get a familiar and robust API to:
- Avoid nested try/catch blocks
- Chain operations declaratively
- Embrace immutability and safer coding practices
β‘ Quick Comparison: Kotlin vs Dart #
Kotlin #
fun divide(a: Int, b: Int): Result<Int> {
return runCatching { a / b }
}
val result = divide(10, 2)
.map { it * 2 }
.getOrElse { -1 }
println(result) // 10
Dart #
Result<int> divide(int a, int b) {
return runCatching(() => a ~/ b);
}
final result = divide(10, 2)
.map((value) => value * 2)
.getOrElse((e, st) => -1);
print(result); // 10
ποΈ Whatβs Inlining & Why Should You Care? #
Kotlin's Result<T>
is an inline class, meaning it avoids extra allocations while wrapping values.
Dart doesnβt have inline classes, but extension types do something similar:
extension type Result<T>._(dynamic _value) { ... }
With this, your Result<T>
doesnβt create an extra objectβitβs just a wrapper at compile-time.
This means no native performance and runtime overhead. π
π οΈ Usage Examples #
β Basic Success & Failure #
final success = Result.success("Yay!");
final failure = Result.failure(Exception("Oops!"));
print(success.getOrNull); // "Yay!"
print(failure.getOrNull); // null
π Chaining with map and recover #
final result = runCatching(() => int.parse("42"))
.map((value) => value * 2)
.recover((_) => 0);
print(result.getOrThrow); // 84
π₯ Handling Failures Gracefully #
final result = runCatching(() => int.parse("NaN"))
.getOrElse((e, st) => -1);
print(result); // -1
π€ Future Extensions #
You can easily handle asynchronous operations with our Future
extensions. Convert a Future<T>
into a Future<Result<T>>
to chain transformations and error handling without verbose try/catch blocks.
Or use asyncRunCatching
.
Example:
Future<Result<Post>> fetchPost() {
return http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'))
.asResult()
.map(Post.fromJson);
}
Future<String> getCurrentTitle() {
return fetchPost()
.onFailure((exception, stacktrace) => print('$exception; $stacktrace'))
.recover((_, __) => 'Something went wrong')
.map((post) => post.title)
.getOrThrow;
}
As you can see, we have repeated each extension method for Future<Result<T>>
to make it easier for you to work with the results of asynchronous operations.
βοΈ Why does Result only catch Exceptions? #
The problem is that dart error system not perfect, Error
and Exception
does not have single parent.
And since Error
is a class that should not be caught, we decided to keep only Exception
.
Feel free to implement runErrorCatching
or runObjectCatching
in your project and use it. π₯
β€οΈ Contributing #
If you have ideas, improvements, or just want to say Result.success("hello!")
, feel free to open an issue or PR!