async_phase_notifier 0.2.0 copy "async_phase_notifier: ^0.2.0" to clipboard
async_phase_notifier: ^0.2.0 copied to clipboard

A variant of ValueNotifier that helps you better manage phases of asynchronous operations.

Pub Version codecov

A variant of ValueNotifier that has AsyncPhase representing the initial / waiting / complete / error phases of an asynchronous operation.

AsyncPhaseNotifier + AsyncPhase is similar to AsyncNotifier + AsyncValue of Riverpod.

Unlike AsyncNotifier and AsyncValue, which are tied to package:riverpod, AsyncPhaseNotifier and AsyncPhase have no such binding. The notifier can be used as just a handy variant of ValueNotifier with AsyncPhase as its value and convenient methods for manipulating the phases.

Sample apps #

Usage #

runAsync() #

The runAsync() method of AsyncPhaseNotifier executes an asynchronous function, updates the value of AsyncPhaseNotifier automatically according to the phase of the asynchronous operation, and notifies the listeners of those changes.

  1. The value of the notifier is switched to AsyncWaiting when the operation starts.
  2. The change is notified to listeners.
  3. The value is switched to either AsyncComplete or AsyncError depending on the result.
  4. The change is notified to listeners.
final notifier = AsyncPhaseNotifier<int>();
notifier.runAsync((data) => someAsyncOperation());

AsyncPhase #

The value of AsyncPhaseNotifier is either AsyncInitial, AsyncWaiting, AsyncComplete or AsyncError. They are subtypes of AsyncPhase.

AsyncPhase provides the when() and whenOrNull() methods, which are useful for choosing an action based on the current phase, like returning an appropriate widget.

child: phase.when(
  initial: (data) => Text('phase: AsyncInitial($data)'), // Optional
  waiting: (data) => Text('phase: AsyncWaiting($data)'),
  complete: (data) => Text('phase: AsyncComplete($data)'),
  error: (data, error, stackTrace) => Text('phase: AsyncError($data, $error)'),
)

async_phase is a separate package, contained in this package. See its document for details not covered here.

Listening for phase changes #

listen()

With listen(), you can listen for phase changes to imperatively trigger some action, like showing an indicator or a dialog / snack bar, or to just log the events.

Note:

  • All callbacks are optional.
    • Listener is not added if no callback function is passed.
  • The onWaiting callback gets a boolean value that indicates the start / end of an asynchronous operation.
final notifier = AsyncPhaseNotifier<Auth>();
final cancel = notifier.listen(
  onWaiting: (isWaiting) { /* e.g. Toggling an indicator */ },
  onComplete: (data) { /* e.g. Logging the result of an operation */ }, 
  onError: (e, s) { /* e.g. Showing an error dialog */ },
);

...

// Remove the listener if it is no longer necessary.
cancel();

AsyncPhaseListener

It is also possible to use the AsyncPhaseListener widget to listen for phase changes.

child: AsyncPhaseListener(
  notifier: notifier,
  onWaiting: (isWaiting) { /* e.g. Toggling an indicator */ },
  onComplete: (data) { /* e.g. Logging the result of an operation */ },
  onError: (e, s) { /* e.g. Showing an error dialog */ },
  child: ...,
)

A listener is added per each AsyncPhaseListener. Please note that if you use this widget at multiple places for a single notifier, the callback functions of all those widgets are called on error.

Examples #

Here is WeatherNotifier extending AsyncPhaseNotifier. It fetches the weather info of a city and notifies its listeners.

class WeatherNotifier extends AsyncPhaseNotifier<Weather> {
  WeatherNotifier();

  final repository = WeatherRepository();

  void fetch() {
    runAsync((weather) => repository.fetchWeather(Cities.tokyo));
  }
}
final notifier = WeatherNotifier();
notifier.fetch();

The examples below use this notifier and show a particular UI component corresponding to each phase of the fetch.

With ValueListenableBuilder #

@override
Widget build(BuildContext context) {
  return ValueListenableBuilder<AsyncPhase<Weather>>(
    valueListenable: notifier,
    builder: (context, phase, _) {
      // Shows a progress indicator while fetching and
      // either the result or an error when finished.
      return phase.when(
        waiting: (weather) => const CircularProgressIndicator(),
        complete: (weather) => Text('$weather'),
        error: (weather, e, s) => Text('$e'),
      );
    },
  ); 
}

Or you can use AnimatedBuilder in a similar way.

With Provider #

ValueListenableProvider<AsyncPhase<Weather>>.value(
  value: notifier,
  child: MaterialApp(home: ...),
)
@override
Widget build(BuildContext context) {
  final phase = context.watch<AsyncPhase<Weather>>();

  return phase.when(
    waiting: (weather) => const CircularProgressIndicator(),
    complete: (weather) => Text('$weather'),
    error: (weather, e, s) => Text('$e'),
  );
}

With Grab #

@override
Widget build(BuildContext context) {
  final phase = context.grab<AsyncPhase<Weather>>(notifier);

  return phase.when(
    waiting: (weather) => const CircularProgressIndicator(),
    complete: (weather) => Text('$weather'),
    error: (weather, e, s) => Text('$e'),
  );
}

TODO #

  • ❌ Add API documents
  • Write tests
3
likes
0
pub points
25%
popularity

Publisher

verified publisherkaboc.cc

A variant of ValueNotifier that helps you better manage phases of asynchronous operations.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

async_phase, flutter, meta

More

Packages that depend on async_phase_notifier