async_notifier 0.3.2 copy "async_notifier: ^0.3.2" to clipboard
async_notifier: ^0.3.2 copied to clipboard

A ValueNotifier for all async states. Listen, notify, and manage loading, error and data in one place.

AsyncNotifier #

A ValueNotifier for all async states. Listen, notify, and manage loading, error, and data in one place.

Usage #

AsyncNotifier is essentially a ValueNotifier with two new setters: future and stream. All it's states are resolved into an AsyncSnapshot. Due to it's nature, it's easily integrated with Flutter own widgets and classes, making it simple and straightforward. Two objects we all know, working together as one.

  • Here's a simple overview:
import 'package:async_notifier/async_notifier.dart';

void main() {
  // Easy side effects.
  final counter = AsyncNotifier(0, onData: .., onError: ..);

  // Listenable.
  counter.addListener(() {
    print("New state: ${counter.snapshot}");
  });

  // The same ValueNotifier we all know ...
  counter.value = 1;

  // ... with two new setters:
  counter.future = Future.value(42);
  counter.stream = Stream.fromIterable([1, 2, 3]);

  // Get all async states in 1 place:
  counter.isLoading
  counter.isReloading
  counter.future
  counter.stream
  counter.requireValue
  counter.hasData
  counter.hasError
  counter.error
  counter.stackTrace

  // Control its states:
  counter.cancel(); // works for future!
  counter.dispose();

  // And resolve them with ease:
  final result = counter.when(
    data: (data) => 'Data $data',
    error: (error, stackTrace) => 'Error $error',
    loading () => 'Loading',
  );
}

Benefits #

  • Simplified State Management: No need to manually manage separate variables for loading, error, data states and more.
  • Easy to Use: Just set the future or stream and let AsyncNotifier handle the rest.
  • Reactive: Automatically notifies listeners when the each state changes

Advanced #

Internally, AsyncNotifier<T> is an implementation of AsyncListenableBase<T,Data>.

Where:

  • T is the ValueNotifier<T>
  • Data is the AsyncSnapshot<Data>
  • Data extends T

So when you are typing AsyncNotifier<T> you are also typing Data as T.

Which is good, and works well with non-nullables like AsyncNotifier(0). But there are cases where you don't have an initial value, and you'd have to do AsyncNotifier<User?>(null).

Doing this will also type the internal Data type as Data?, resulting in all your getters to be nullable. Ex: Future<User?> instead of Future<User>, which is bad for type safety.

For those cases use AsyncNotifier.late<T>.

The late constructor is an implementation of AsyncListenableBase<T?, Data>, which allows you to work with an optional initial value and later with a non-nullable Data in all your async operations!

  • TLDR:
// use AsyncNotifier<T> for value T and data T.
final todos = AsyncNotifier(<Todo>[]);

// use AsyncNotifier.late<T> for value T? and data T.
final user = AsyncNotifier.late<User>();

State Management #

You can listen to all AsyncNotifier states directly and bind it to other objects.

class MyNotifier extends ChangeNotifier {
  MyNotifier() {
    _todos.addListener(notifyListeners); // notifies ChangeNotifier

    getTodos(); // init
  }

  final _todos = AsyncNotifier(<Todo>[]);

  AsyncListenable<List<Todo>> get todosListenable => _todos;

  List<Todo> get todos => _todos.value.toList(); // copy

  void getTodos() {
    _todos.future = _repository.getAllTodos();
  }

  void addTodo(Todo todo) {
    _todos.value = todos..add(todo);
  }

}

Consuming the State #

You can use Flutter native solutions like ListenableBuilder.

class TodoList extends StatelessWidget {
  const TodoList({super.key, required this.todosListenable});

  final AsyncListenable<List<Todo>> todosListenable;

  @override
  Widget build(BuildContext context) {

    // This Flutter builder rebuilds whenever our AsyncNotifier changes.
    return ListenableBuilder(
      listenable: todosListenable,
      builder: (context, _) {

        // Use `when` for resolving AsyncNotifier states.
        return todosListenable.when(
          loading: () => const CircularProgressIndicator(),
          error: (error, stackTrace) => Text('Error: $error'),
          data: (data) => ListView.builder(
            itemCount: todosListenable.value.length,
            itemBuilder: (context, index) => Text(data[index].title),
          ),
        );
      },
    );
  }
}

With provider:

class TodoList extends StatelessWidget {
  const TodoList({super.key});

  @override
  Widget build(BuildContext context) {
    final todosListenable = context.watch<MyNotifier>().todosListenable;

    // Use `when` for resolving AsyncSnapshot states.
    return todosListenable.when(
      loading: () => const CircularProgressIndicator(),
      error: (error, stackTrace) => Text('Error: $error'),
      data: (data) => ListView.builder(
        itemCount: todosListenable.value.length,
        itemBuilder: (context, index) => Text(data[index].title),
      ),
    );
  }
}

Contributing #

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

1
likes
140
pub points
40%
popularity

Publisher

verified publisherbranvier.com

A ValueNotifier for all async states. Listen, notify, and manage loading, error and data in one place.

Homepage
Repository (GitHub)
View/report issues

Topics

#async #value #notifier #state

Documentation

API reference

License

MIT (LICENSE)

Dependencies

flutter

More

Packages that depend on async_notifier