resultex 2.3.1
resultex: ^2.3.1 copied to clipboard
A robust core architecture ecosystem for Flutter and Dart, providing unified dependency injection and error management utilities.
Resultex #
A robust, type-safe error handling ecosystem for Dart and Flutter that implements the Result pattern for clean, predictable error handling without exceptions.
๐ฆ Overview #
resultex brings a functional programming approach to error handling by wrapping your operational
outcomes in a type-safe container. Instead of throwing heavy exceptions and messing up your call
stack, every function returns a Result that is either a SuccessResult wrapping your value or a
FailureResult containing structured error details.
Why Resultex? #
- ๐ก๏ธ Zero Try-Catch Boilerplate - Handle operational errors exactly where they matter.
- ๐ Type-Safe Destructuring - The Dart compiler ensures you handle both success and failure states.
- ๐งฉ Pattern Matching - Fully optimized for Dart 3+ exhaustive switch expressions.
- ๐ Composable Pipelines - Chain complex operations effortlessly with
mapandflatMap. - ๐งช Highly Testable - Predictable control flow, making Unit Testing a breeze without unexpected runtime crashes.
- ๐ Clean Architecture Approved - Follows SOLID principles, separating business logic outcomes from presentation layers.
๐ Installation #
Prerequisites #
- Dart SDK:
>=3.0.0 <4.0.0 - Flutter SDK:
>=3.10.0
Add resultex to your pubspec.yaml:
dependencies:
resultex: ^2.2.0
๐ฏ Core Concepts #
- Successful Result
final result = Result.success(User(id: 1, name: "Hassan"));
// Contains a SuccessResult wrapping a Success<User> container
- Failure Result
final result = Result.failure(Failure(message: "Network request timeout"));
// Contains a FailureResult with messages, codes, and optional stackTraces
- Exhaustive Pattern Matching (Dart 3+)
switch (result) {
case SuccessResult<User>(success: Success(:final value)):
print('Welcome back, ${value.name}!');
case FailureResult<User>(failure: final fail):
print('Error occurred: ${fail.message}');
}
๐จ Reactive UI Layer (New in v2.3.0) #
๐ฆ State Management via ResultNotifier
A lightweight, lifecycle-aware alternative to heavy state management boilerplate. Track any
asynchronous operation safely inside your controllers or state classes.
final userNotifier = ResultNotifier<User>();
// Automatically handles loading state, catches unexpected errors, and updates the UI
userNotifier.track
(
repository
.
fetchUserProfile
(
)
);
๐งฑ Declarative Layouts via ResultBuilder
Eliminate bloated conditions inside your widget tree. Separate your UI cleanly into three
predictable layout paths.
@override
Widget build(BuildContext context) {
return ResultBuilder<User>(
notifier: _userNotifier,
onLoading: (context) => const CircularProgressIndicator(),
onFailure: (context, failure) => Text('Error: ${failure.message}'),
onSuccess: (context, user) => Text('Hello, ${user.name}'),
);
}
๐ Advanced Functional Pipelines #
๐ Asynchronous Chaining (asyncMap & asyncFlatMap) Chain multiple asynchronous dependencies sequentially without running into await callback hell.
// Streamline complex database/network pipelines elegantly
Future<Result<Orders>> ordersResult = repository.getUser(1) // Future<Result<User>>
.asyncMap((user) => user.id) // Future<Result<int>>
.asyncFlatMap((id) => orderRepository.getOrders(id)); // Future<Result<Orders>>
๐ญ Adapting Errors downstream (mapFailure)
Transform internal exceptions into localized or presentation-friendly error messages before hitting
the UI.
final uiResult = apiResult.mapFailure(
(fail) => Failure(message: 'Localized Error: ${fail.message}'),
);
๐ Resilient Operation Retries (withRetry)
Attach configurable recovery retries to any transient network dispatch with support for exponential
backoff.
final networkResult = await
Result.guardAsync
(
() => (() => http.get(uri)).withRetry(
const RetryOptions(maxAttempts: 3, delay: Duration(seconds: 2), backoffFactor: 2.0),
),
);
๐จ Advanced Features & Extensions #
- UI Layer Clean Mapping (.when()) The package provides a tailored Flutter extension to directly map your Result states into widgets inside the build method without bloated conditions.
import 'package:resultex/core/utils/result_flutterx_extension.dart';
@override
Widget build(BuildContext context) {
return userResult.when(
onSuccess: (user) => Text('Hello, ${user.name}'),
onFailure: (failure) => Text('Error: ${failure.message}'),
);
}
- Concurrent Parallel Execution (ResultUtils.combineAll) When executing multiple operations simultaneously, ResultUtils.combineAll fires them in parallel. If any operation fails, it aggregates all intercepted errors into a unified MultiFailure contract instead of short-circuiting on the first error.
import 'package:resultex/core/utils/result_utils.dart';
final Result<List<dynamic>> dashboardResult = await
ResultUtils.combineAll
([repository.fetchUserProfile(), // Future<Result<User>>
repository.fetchNotifications(), // Future<Result<List<Notif>>>
repository.fetchCryptoWallet(), // Future<Result<Wallet>>
]);
dashboardResult.when(
onSuccess: (data) {
final user = data[0] as User;
final wallet = data[2] as CryptoWallet;
// Render your screen using safely casted data
},
onFailure: (failure) {
if (failure is MultiFailure) {
print('${failure.failures.length} operations failed concurrently.');
}
},
);
- Operational Recovery Path (.recover()) Intercept an operational Failure and route it through an alternative backup execution pipeline smoothly.
Result<User> userResult = await
authRepository.fetchRemoteUser
();
Result<User> finalizedResult = userResult.recover((failure) {
print('Remote fetch failed: ${failure.message}. Falling back to local cache...');
return Result.success(localDatabase.getCachedUser());
});
๐ Fluid Usage Guide
Safe Execution Closures (guard / guardAsync)
Automatically intercept synchronous or asynchronous unexpected exceptions and encapsulate them into
safe Result variants.
// Synchronous parsing guard
final result = Result.guard(() => jsonDecode(rawJsonString));
// Asynchronous API call guard
final networkResult = await
Result.guardAsync
(
() => http.get(Uri.parse(url
)
)
);
Functional Chaining (map & flatMap)
Transform successful values or sequentially chain multiple business operations without nesting
blocks.
// Map values on success
final nameResult = result.map((user) => user.name); // Result<String>
// Chain dependencies flatly
final ordersResult = await
repository.getUser
(1)
.then((res) => res.flatMap((user) => orderRepository.getOrders(user.id)
)
);
๐ง Advanced ResultExecutor Architecture
Wrap execution scopes into monitored contexts complete with automated structured logging and error
tracking capabilities.
final executor = ResultExecutor(logger: AppLogger());
final result = await
executor.executeAsync
(
() async {
`final data = await api.fetchData();
`return processData(data);
},
context
:
fetchAndProcessDashboardData
);
๐๏ธ Best Practices #
โ DO
- Use ResultUtils.combineAll for maximizing parallel performance across independent network dispatches.
- Use the .when() extension inside Flutter layout building pipelines for pristine scannability.
- Provide meaningful context tags within ResultExecutor blocks to maintain bulletproof debug logs.
โ DON'T
- Don't force-extract values without evaluating state via pattern matching or explicit folds.
- Don't catch generic raw exceptions manually inside execution blocks managed by guard hooks.
๐ License #
This project is licensed under the MIT License - see the LICENSE file for details. Open Source development is respected; feel free to modify, distribute, and implement this package in both public repositories and enterprise closed-source commercial systems.