Skip to content

Instantly share code, notes, and snippets.

@rutvik110
Last active December 30, 2021 04:46
Show Gist options
  • Save rutvik110/888ca4058e6cb28dad19af761e2f2ece to your computer and use it in GitHub Desktop.
Save rutvik110/888ca4058e6cb28dad19af761e2f2ece to your computer and use it in GitHub Desktop.
Pagination With StateNotifier
import 'package:coves_management/notifiers/pagination/pagination_notifier_state.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logging/logging.dart';
class PaginationNotifier<T> extends StateNotifier<PaginationNotifierState<T>> {
PaginationNotifier({required this.fetchNextItems, required this.hitsPerPage})
: super(const PaginationNotifierState.data([], false)) {
init();
}
final Logger logger = Logger('PaginationNotifier');
final Future<List<T>> Function(T? item, int offset,
{String? query, required bool forceNetwork}) fetchNextItems;
final int hitsPerPage;
String _currentQuery = "";
final List<T> _items = [];
void init() {
if (_items.isEmpty) {
fetchNextPage();
}
}
bool get _canLoadNextPage => state.maybeWhen(
dataLoading: (_) => false,
data: (_, hasReachedMax) => !hasReachedMax,
orElse: () => true,
);
Future<void> loadQuery(String query) {
_currentQuery = query;
return fetchNextPage(skipLoadNextPageCheck: true, clearCurrentList: true);
}
Future<void> fetchNextPage(
{bool skipLoadNextPageCheck = false,
bool clearCurrentList = false,
bool forceNetwork = false}) async {
if (!mounted) {
logger.warning("fetchNextPage called on unmounted PaginationNotifier");
return;
}
if (!_canLoadNextPage && !skipLoadNextPageCheck) {
return;
}
try {
state = PaginationNotifierState.dataLoading(_items);
final List<T> result = _items.isEmpty || clearCurrentList
? await fetchNextItems(null, 0,
query: _currentQuery, forceNetwork: forceNetwork)
: await fetchNextItems(_items.last, _items.length,
query: _currentQuery, forceNetwork: forceNetwork);
if (clearCurrentList) {
_items.clear();
}
if (result.isEmpty) {
state = PaginationNotifierState.data(_items, true);
} else if (result.length < hitsPerPage) {
state = PaginationNotifierState.data(_items..addAll(result), true);
} else {
state = PaginationNotifierState.data(_items..addAll(result), false);
}
} catch (e, stk) {
print(mounted);
state = PaginationNotifierState.error(e, stk);
}
}
}
/// Pagination State
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'pagination_notifier_state.freezed.dart';
@freezed
abstract class PaginationNotifierState<T> with _$PaginationNotifierState<T> {
const factory PaginationNotifierState.data(
List<T> items, bool hasReachedMax) = _Data;
const factory PaginationNotifierState.dataLoading(List<T> movies) =
_DataLoading;
const factory PaginationNotifierState.error(Object? e, [StackTrace? stk]) =
_Error;
}
/// Controller listening to scrolling updates and fetching items if user reaches end of list
@override
void initState() {
super.initState();
_invoiceScrollController = ScrollController();
_invoiceScrollController.addListener(() {
double maxScroll = _invoiceScrollController.position.maxScrollExtent;
double currentScroll = _invoiceScrollController.position.pixels;
double delta = MediaQuery.of(context).size.width * 0.20;
if (maxScroll - currentScroll <= delta) {
widget.onEnd(lastItem);
}
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment