fresult 1.1.2
fresult: ^1.1.2 copied to clipboard
A Dart package providing Result types for better error handling with freezed.
fresult #
A Dart package providing Result types for better error handling using Freezed. This package helps you write more robust and functional code by avoiding exceptions and providing type-safe error handling.
Features #
- 🎯 Type-safe error handling - No more exceptions for expected errors
- 🔒 Immutable Result types - Built with Freezed for immutability
- 🔄 Functional programming patterns - Map, flatMap, and chain operations
- ⚡ Async/await support - Work seamlessly with Futures
- 🛡️ Null safety - Full null safety support
- 📚 Comprehensive documentation - Well-documented API with examples
- ✅ 100% test coverage - Thoroughly tested
Installation #
Add this to your package's pubspec.yaml
file:
dependencies:
fresult: ^1.1.1
dev_dependencies:
build_runner: ^2.7.1
freezed: ^3.1.0
Then run:
dart pub get
dart run build_runner build
Quick Start #
import 'package:fresult/fresult.dart';
void main() {
// Create a successful result
final success = Result<String, String>.success('Hello World');
// Create a failed result
final failure = Result<String, String>.failure('Something went wrong');
// Pattern matching with when
success.when(
success: (value) => print('Success: $value'),
failure: (error) => print('Error: $error'),
);
// Check if result is success or failure
if (success.isSuccess) {
print('Value: ${success.valueOrNull}');
}
// Transform values
final doubled = Result<int, String>.success(5)
.map((value) => value * 2);
// Chain operations
final result = Result<int, String>.success(5)
.flatMap((value) => value > 0
? Result.success('Positive: $value')
: Result.failure('Not positive'));
}
API Reference #
Result<T, E> #
A sealed class representing either a success with a value of type T
or a failure with an error of type E
.
Constructors
Result.success(T value)
- Creates a successful resultResult.failure(E error)
- Creates a failed result
Properties
bool isSuccess
- Returns true if this result is a successbool isFailure
- Returns true if this result is a failureT? valueOrNull
- Returns the value if success, otherwise nullE? errorOrNull
- Returns the error if failure, otherwise null
Methods
Pattern Matching
result.when(
success: (value) => 'Success: $value',
failure: (error) => 'Error: $error',
);
Value Extraction
// Get value with default
final value = result.valueOr('Default');
// Get value with computed default
final value = result.valueOrElse(() => 'Computed');
// Get value or throw error
final value = result.valueOrThrow;
Transformations
// Map success value
final mapped = result.map((value) => value.toString());
// Map error
final mapped = result.mapError((error) => 'Mapped: $error');
// Map both
final mapped = result.mapBoth(
successMapper: (value) => 'Success: $value',
failureMapper: (error) => 'Error: $error',
);
// Chain operations
final chained = result.flatMap((value) =>
Result.success(value * 2));
Extensions #
ResultExtensions
// Execute side effects
result.onSuccess((value) => print('Success: $value'));
result.onFailure((error) => print('Error: $error'));
NullableToResult
// Convert nullable to Result
final result = nullableValue.toResult('Custom error message');
FutureResultExtensions
// Work with Future Results
final futureResult = Future.value(Result.success(5));
// Map async results
final mapped = await futureResult.map((value) => value * 2);
// Chain async operations
final chained = await futureResult.flatMap((value) =>
Future.value(Result.success(value.toString())));
Examples #
Basic Usage #
import 'package:fresult/fresult.dart';
void main() {
// Creating Results
final success = Result<String, String>.success('Hello World');
final failure = Result<String, String>.failure('Error occurred');
// Pattern matching
success.when(
success: (value) => print('Success: $value'),
failure: (error) => print('Error: $error'),
);
// Using extensions
success.onSuccess((value) => print('Got value: $value'));
failure.onFailure((error) => print('Got error: $error'));
}
API Error Handling #
sealed class ApiError {
const ApiError();
}
class NetworkError extends ApiError {
final String message;
const NetworkError(this.message);
}
class ValidationError extends ApiError {
final String field;
final String message;
const ValidationError(this.field, this.message);
}
class UserService {
Future<Result<User, ApiError>> getUser(String id) async {
if (id.isEmpty) {
return Result.failure(ValidationError('id', 'ID cannot be empty'));
}
try {
final user = await apiClient.getUser(id);
return Result.success(user);
} catch (e) {
return Result.failure(NetworkError(e.toString()));
}
}
}
Async Operations #
Future<Result<String, String>> fetchData() async {
await Future.delayed(Duration(seconds: 1));
return Result.success('Data fetched');
}
void main() async {
final result = await fetchData()
.map((data) => 'Processed: $data')
.flatMap((processed) =>
Future.value(Result.success('Final: $processed')));
result.when(
success: (value) => print('Final result: $value'),
failure: (error) => print('Error: $error'),
);
}
Running Examples #
The package includes comprehensive example applications:
# Basic usage and transformations
dart run example/basic_usage.dart
# Real-world API example with error handling
dart run example/api_example.dart
Example 1: Basic Usage (example/basic_usage.dart
) #
Demonstrates fundamental Result operations:
- Creating success and failure results
- Pattern matching with
when
- Using extensions for side effects
- Value extraction with defaults
- Transformations and chaining
- Working with nullable values
- Error handling patterns
Example 2: API Example (example/api_example.dart
) #
A comprehensive real-world example showing:
- Custom error types (NetworkError, ValidationError, etc.)
- User service with CRUD operations
- Complex error handling scenarios
- Chaining async operations
- Error transformation to user-friendly messages
- Side effects with extensions
Development Workflow #
Local Testing #
Use the provided test script to run all checks locally:
./scripts/test.sh
This script will:
- Install dependencies
- Generate Freezed code
- Check formatting
- Run static analysis
- Run all tests
GitHub Actions #
The repository includes automated workflows:
1. CI Workflow (.github/workflows/ci.yml
)
- Runs on every push and pull request
- Executes tests, formatting checks, and static analysis
- Generates test coverage reports
2. Test and Auto-PR Workflow (.github/workflows/test-and-pr.yml
)
- Triggers on pushes to
develop
branch - Runs
result_test.dart
andresult_extensions_test.dart
- Automatically creates a pull request to
main
when tests pass - Auto-merges the PR after approval
3. Publish Workflow (.github/workflows/publish.yml
)
- Triggers on GitHub releases
- Publishes the package to pub.dev automatically
Branch Strategy #
develop
- Development branch for new featuresmain
- Production-ready code- Feature branches - Created from
develop
Workflow Steps #
- Develop: Work on features in the
develop
branch - Test: Push to
develop
triggers automatic testing - Auto-PR: If tests pass, a PR to
main
is created automatically - Review: Review and approve the auto-created PR
- Merge: PR is automatically merged to
main
- Release: Create a GitHub release to publish to pub.dev
Testing #
Run the test suite:
dart test
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Changelog #
See CHANGELOG.md for a list of changes and version history.
Related Packages #
- freezed - Code generation for immutable classes
- dartz - Functional programming in Dart
- fpdart - Functional programming in Dart
Support #
If you find this package helpful, please consider giving it a ⭐ on github and 👍 on pub.dev!