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:
initial
loading
success
failure
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
dartz
orfpdart
. 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_bloc
orprovider
. 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