doso 1.1.1
doso: ^1.1.1 copied to clipboard
DoSo is a lightweight Dart library for simple and elegant error and state handling, designed to reduce boilerplate and avoid code generation in Flutter apps.
DoSo: Simple and Elegant Error and State Handling #
DoSo is a lightweight Dart library designed to simplify and streamline state and error handling in Flutter applications. Built to reduce boilerplate and avoid code generation, DoSo provides a functional and expressive API for managing synchronous and asynchronous operations.
π Getting Started #
DoSo offers a declarative and functional approach to handling common states such as:
initialloadingsuccessfailure
This encapsulates logic and enhances error handling throughout your app. Check out the example below to see how DoSo can be used in your Flutter projects.
import 'package:doso/doso.dart';
void main() async {
// [Do] represents a action. (State with a value of type S and an optional failure of type F)
// [So] represents a return. (Is a type alias for Do<F, S> and SoException<Exception, S> for a fixed failure type)
// EMITTING STATES
// Use Do.tryCatch to handle sync or async operations
final result = await Do.tryCatch(
onTry: () => doSomething(),
onCatch: (exception, stackTrace) => Exception('Captured error: $exception and $stackTrace'), // optional
onFinally: () => print('finished'), // optional
);
// or
// Use Do states directly with a common try/catch
try {
final result = doSomething();
return Do.success(result);
} catch (e) {
return Do.failure(e);
} finally {
print('finished');
}
// STATE HANDLING
// Handle all Do states with when:
result.when(
onInitial: () => print('Initial State'), // optional
onLoading: () => print('Loading...'),
onSuccess: (value) => print('Success: $value'),
onFailure: (failure) => print('Failure: $failure'),
);
// or
// Handle only Do.success and Do.failure with fold:
result.fold(
onFailure: (failure) => print('Failure: $failure'),
onSuccess: (value) => print('Success: $value'),
);
// or
// Handle specific states with maybeWhen:
final output = result.maybeWhen(
onInitial: () => 'Initial', // optional
onLoading: () => 'Loading', // optional
onSuccess: (value) => 'Success: $value', // optional
onFailure: (failure) => 'Failure: $failure', // optional
orElse: () => 'Else' // optional
);
// or
// Just use a simple if statement:
if (result.isInitial) {}
if (result.isLoading) {}
if (result.isSuccess) {}
if (result.isFailure) {}
// RETURN STATEMENTS
// So with custom failure type
final So<MyCustomFailure, int> result = Do.success(42);
// So with failure fixed exception type
final SoException<String> result2 = Do.success('String');
}
π¦ Available States #
// Do
Do.initial(); // Represents the initial state
Do.loading(); // Represents a loading state
Do.success(value); // Represents a success with the associated value [S]
Do.failure([failure]); // Represents a failure with optional failure [F]
// So
So<F, S> // Represents a return statement with a failure of type F and a value of type S
SoException<S> // Represents a return statement with a fixed failure type of Exception and a value of type S
π Core Methods #
getOrElse(S defaultValue) #
Returns the value in Do.success, or the fallback value if it is not available.
final value = Do.success(42).getOrElse(0); // 42
map<T>(T Function(S value)) #
Transforms the success value into another value.
final result = Do.success(42).map((value) => value.toString()); // Do<String>.success('42')
flatMap<T>(Do<T, F> Function(S value)) #
Maps to another Do state.
final result = Do.success(42).flatMap((value) => Do.success(value.toString()));
fold<T> #
Handle success and failure.
final message = Do.success(42).fold(
onFailure: (failure) => 'Error: $failure',
onSuccess: (value) => 'Success: $value',
);
when<T> #
Handle all states.
final output = result.when(
onInitial: () => 'Initial',
onLoading: () => 'Loading...',
onSuccess: (value) => 'Success: $value',
onFailure: (failure) => 'Failure: $failure',
);
maybeWhen<T> #
Handle specific states with orElse.
final result = Do.success(42);
final output = result.maybeWhen(
onSuccess: (value) => 'Success: $value', // optional
orElse: () => 'Default', // optional
);
print(output); // Output: Success: 42
tryCatch #
Wraps synchronous/async calls in a Do, automatically catching exceptions.
final result = await Do.tryCatch(
onTry: () async => 42,
onCatch: (exception, stackTrace) => Exception('Handled: $exception and $stackTrace'),
onFinally: () => print('Done'),
);
π Use in Application Layers #
β Data Source #
SoException<int> getOk() => Do.tryCatch(onTry: () => http.get('/ok'));
β Repository #
SoException<String> getOk() async {
final result = await dataSource.getOk();
return result.map((code) => code.toString());
}
or
So<CustomFailure, String> getOk() async {
final result = await dataSource.getOk();
return result.flatMap((code) {
if (code == is2XX() || code == is3XX()) {
return Do.success(code.toString());
} else {
return Do.failure(CustomFailure('Error: $code'));
}
});
}
β Cubit #
class MyCubit extends Cubit<Do<Exception, String>> {
MyCubit(this.repo) : super(Do.initial());
final Repository repo;
Future<void> getData() async {
emit(Do.loading());
final result = await repo.getOk();
result.fold(
onFailure: (failure) => emit(Do.failure(failure)),
onSuccess: (value) => emit(Do.success('Success: $value')),
);
}
}
β UI #
BlocBuilder<MyCubit, Do<Exception, String>>(
builder: (context, state) => state.when(
onInitial: () => Text('Initial'),
onLoading: () => CircularProgressIndicator(),
onSuccess: (value) => Text(value),
onFailure: (failure) => Text(failure.toString()),
),
)
Explore a practical example: DoSo Example
β οΈ When to Use DoSo #
DoSo is ideal for simple screen states or flows with a clear success/failure logic. You can
combine it with other libraries like flutter_bloc or provider for state management.
If your state grows in complexity (e.g., multiple fields, nested properties), you might want to adopt a more traditional and robust approach like using manually crafted classes or more advanced tools.
DoSo is not: #
- A replacement for functional programming libraries like
dartzorfpdart. It is a simple and elegant solution for handling states and errors in a functional way, without the need follow entire functional approach. - A replacement for state management libraries like
flutter_blocorprovider. It is a lightweight library that can be used in conjunction with these libraries to simplify state and error handling.
π More Info & Contribute #
Check out the full implementation, open issues, and contribute on GitHub: DoSo on GitHub