fabrik_result 1.1.0
fabrik_result: ^1.1.0 copied to clipboard
A lightweight toolkit for functional-style types like Either, built for clarity and real-world use.
fabrik_result #
Lightweight, explicit result types for Dart and Flutter — model success, failure, and absence of values without exceptions or nulls.
What's included #
| Type | Purpose |
|---|---|
Either<L, R> |
Success (Right) or failure (Left) with exhaustive handling |
Option<T> |
Value present (Some) or absent (None) |
Unit |
Typed replacement for void in generic APIs |
Installation #
dependencies:
fabrik_result: ^1.0.1
flutter pub get
Quick Start #
Either — handle failures explicitly #
Either<Failure, User> result = await getUser(id);
result.fold(
(failure) => showError(failure.message),
(user) => navigateToDashboard(user),
);
Because Either is a sealed class, you can also pattern match on it and the
compiler will tell you if you miss a case — no default branch needed:
final message = switch (result) {
Left(value: final failure) => 'Failed: ${failure.message}',
Right(value: final user) => 'Welcome, ${user.name}',
};
Return values using the helper functions:
Future<Either<Failure, User>> getUser(String id) async {
try {
final user = await api.fetchUser(id);
return right(user);
} catch (e) {
return left(Failure(e.toString()));
}
}
Capture exceptions at the boundary #
tryCatch and tryCatchAsync replace that hand-written try/catch:
Future<Either<Failure, User>> getUser(String id) {
return Either.tryCatchAsync(
() => api.fetchUser(id),
(error, stackTrace) => Failure('$error'),
);
}
Transform and chain #
Use map to change the success value and flatMap to run another fallible
step. A Left short-circuits the whole chain, so failures propagate untouched:
final result = await getUser(id)
.then((r) => r.map((user) => user.profile))
.then((r) => r.flatMap(validateProfile));
// Read it back with a fallback:
final name = result.getOrElse((failure) => 'Anonymous');
Available on Either: map, mapLeft, flatMap, flatMapAsync,
getOrElse, swap.
Option — model absence without null #
Option<User> cached = findCachedUser();
cached.fold(
() => showLoginScreen(),
(user) => showDashboard(user),
);
Option interoperates with Dart's nullable types in both directions, which
makes it useful for loosely typed data such as JSON:
final name = Option.fromNullable(json['name'] as String?)
.where((n) => n.isNotEmpty)
.map((n) => n.trim())
.getOrElse(() => 'Anonymous');
// And back to a nullable when an API expects one:
final String? raw = cached.map((u) => u.email).toNullable();
Turn a missing value into a typed failure with toEither:
Either<Failure, User> result =
cached.toEither(() => Failure('no cached user'));
Available on Option: map, flatMap, getOrElse, where, toNullable,
toEither, plus the static Option.fromNullable.
Unit — type-safe void #
Use Unit when an operation succeeds but has nothing meaningful to return:
Future<Either<Failure, Unit>> saveSettings(Settings s) async {
try {
await storage.write(s);
return right(unit);
} catch (e) {
return left(Failure(e.toString()));
}
}
Extras #
Either also has side-effect helpers for imperative code:
result.onRight((user) => analytics.track('login'));
result.onLeft((failure) => logger.error(failure));
final user = result.rightOrNull;
Documentation #
Full API reference and guides at fabriktool.com
Contributing #
Found a bug or have a suggestion? Open an issue or pull request on GitHub.