Comment on page
Runner

The Runner state flow
A simple use case that executes a specific task which returns a
Left Value
when an error occurs or a Right Value
when the task succeeds.This is ideal for create, read, update and delete (CRUD) operations.
A Runner use case can emit 4 possible class states, all of which inherit from the
RunnerState
sealed class:Runner States | Description |
---|---|
RunnerInitial | The initial state or the state emitted when the use case has been reset. |
Running | The state emitted when the task execution is in progress. |
RunSuccess | The state emitted when the use case fails. |
RunFailed | The state emitted when the use case succeeds. |
A Runner also has the following properties that you can access:
Property | Description |
---|---|
value | The latest value returned when calling run() . This may either be the leftValue if RunFailed was recently emitted. Otherwise, this will be equal to the rightValue if RunSuccess was more recent. |
params | The latest params passed when calling run() . This may either be the leftParams if RunFailed was recently emitted. Otherwise, this will be equal to the rightParams if RunSuccess was more recent. |
leftValue | The last left value returned when a failed run() was called. |
leftParams | The last left params passed when a failed run() was called. |
rightValue | The last right value returned when a successful run() was called. |
rightParams | The last right params passed when a successful run() was called. |
Let's create a Runner use case named
CountFruits
for categorizing and counting the number of fruits given in our fruit basket.Create the following classes:
1. Create the Parameter class
This is the parameter class passed to the
Runner
when being executed./// The parameter for the [CountFruit] use case containing all the available
/// fruits to count.
class CountFruitParams {
const CountFruitParams(this.fruits);
/// The fruits in our fruit basket.
///
/// Example:
/// [ apple, orange, mango, apple, apple, mango ]
final List<String> fruits;
@override
String toString() => {'fruits': fruits}.toString();
}
2. Create the Left value class
This is the object returned when the
Runner
fails and emits a RunFailed
state:class Failure {
const Failure(this.message);
final String message;
@override
String toString() => 'Failure: $message';
}
This is the object returned when the
Runner
succeeds and emits a RunSuccess
state:/// The right value for [CountFruit] containing the count for each fruit.
class CountFruitResult {
const CountFruitResult(this.fruitCount);
/// The fruits counted by type
///
/// Example:
/// { apple: 3, orange: 1, mango: 2 }
final Map<String, int> fruitCount;
@override
String toString() => '$fruitCount';
}
Pass the
Parameter
, Left
and Right
classes to the Runner
's generic arguments. Afterwards, implement the code logic in the onCall
method:/// A runner that counts the quantity of each given fruit.
class CountFruit extends Runner<CountFruitParams, Failure, CountFruitResult> {
/// A callback function triggered when the [run] method is called.
@override
FutureOr<Either<Failure, CountFruitResult>> onCall(
CountFruitParams params,
) async {
if (params.fruits.isEmpty) {
// When the given fruits is empty, then a left value is returned
return const Left(Failure('There are no fruits to count'));
}
final fruitCount = <String, int>{};
for (final fruit in params.fruits) {
fruitCount[fruit] = (fruitCount[fruit] ?? 0) + 1;
}
// Returns a right value containing the fruit count
final result = CountFruitResult(fruitCount);
return Right(result);
}
}
To start the runner, call the
run
method: final countFruit = CountFruit();
await countFruit.run(
params: const CountFruitParams(['Apple', 'Orange', 'Apple']),
);
The Runner will emit the
Running
state, followed either by the RunSuccess
with a Right Value
or RunFailed
state with a Left Value
depending on the result of the runner.The Runner gives you access to the following properties:
// The recent value returned when calling `run()`. This may either be the
// `leftValue` if the state current is `RunFailed` or the `rightValue` if the
// current state is `RunSuccess`
print('Current value: ${runner.value}');
// The recent params passed when calling `run()`. This may either be the
// `leftParams` if the state current is `RunFailed` or the `rightParams` if
// the current state is `RunSuccess`
print('Current params: ${runner.params}');
// The last left value returned when a failed `run()` was called
print('Last left value: ${runner.leftValue}');
// The last left params passed when a failed `run()` was called
print('Last left params: ${runner.leftParams}');
// The last right value returned when a successful `run()` was called
print('Last right value: ${runner.rightValue}');
// The last right params passed when a successful `run()` was called
print('Last right params: ${runner.rightParams}');
To clear the Runner and reset it back to its initial state, call the
reset
method:runner.reset();
Every use case is a descendent of BLoC cubit. Hence, we can manage its states via the flutter_bloc package.
BlocProvider(
lazy: false,
create: (context) =>
// Initialize and eagerly run the Runner
CountFruit()
..run(
params:
const CountFruitParams(['apple', 'orange', 'mango', 'apple']),
),
child: BlocConsumer<CountFruit, RunnerState>(
listener: (context, runnerState) {
if (runnerState is RunnerInitial) {
// Handle initial state. This is also triggered when the Runner has
// been reset
} else if (runnerState is Running) {
// Handle running state
} else if (runnerState is RunFailed<Failure>) {
// Handle failure
print('Left value: ${runnerState.leftValue}');
} else if (runnerState is RunSuccess<CountFruitResult>) {
// Handle success
print('Right value: ${runnerState.rightValue}');
}
},
builder: (context, runnerState) {
switch (runnerState) {
case RunnerInitial():
return Container();
case Running():
return const Center(child: CircularProgressIndicator());
case RunFailed():
return Center(
child: Text('Left value: ${runnerState.leftValue}'),
);
case RunSuccess():
return Center(
child: Text('Right value: ${runnerState.rightValue}'),
);
}
},
),
);
To run the Dart demo:
- 1.
- 2.Navigate to the
packages/codenic_bloc_use_case
directory. - 3.Run the example code:
dart run example/main.dart
Last modified 6mo ago