import 'dart:async'; import 'package:hooks_riverpod/hooks_riverpod.dart'; abstract class PaginationController { int? get totalCount; int get fetchedCount; bool get fetchedAll; bool get isLoading; FutureOr> fetch(); Future refresh(); Future fetchFurther(); } abstract class PaginationFiltered { late F currentFilter; Future applyFilter(F filter); } mixin AsyncPaginationController on AsyncNotifier> implements PaginationController { @override int? totalCount; @override int get fetchedCount => state.value?.length ?? 0; @override bool get fetchedAll => totalCount != null && fetchedCount >= totalCount!; @override bool isLoading = false; @override FutureOr> build() async => fetch(); @override Future refresh() async { isLoading = true; totalCount = null; state = AsyncData>([]); final newState = await AsyncValue.guard>(() async { return await fetch(); }); state = newState; isLoading = false; } @override Future fetchFurther() async { if (fetchedAll) return; isLoading = true; state = AsyncLoading>(); final newState = await AsyncValue.guard>(() async { final elements = await fetch(); return [...?state.value, ...elements]; }); state = newState; isLoading = false; } } mixin AsyncPaginationFilter on AsyncPaginationController implements PaginationFiltered { @override Future applyFilter(F filter) async { if (currentFilter == filter) return; // Reset the data isLoading = true; totalCount = null; state = AsyncData>([]); currentFilter = filter; final newState = await AsyncValue.guard>(() async { return await fetch(); }); state = newState; isLoading = false; } }