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; bool get hasMore; set hasMore(bool value); String? get cursor; set cursor(String? value); 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 => !hasMore || (totalCount != null && fetchedCount >= totalCount!); @override bool isLoading = false; @override bool hasMore = true; @override String? cursor; @override FutureOr> build() async { cursor = null; return fetch(); } @override Future refresh() async { isLoading = true; totalCount = null; hasMore = true; cursor = null; state = AsyncLoading>(); final newState = await AsyncValue.guard>(() async { return await fetch(); }); state = newState; isLoading = false; } @override Future fetchFurther() async { if (fetchedAll) return; if (isLoading) 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; hasMore = true; cursor = null; state = AsyncLoading>(); currentFilter = filter; final newState = await AsyncValue.guard>(() async { return await fetch(); }); state = newState; isLoading = false; } }