Infinite Paging ScrollView
Encapsulate animated_infinite_scroll_pagination package with common (loading, footer, error) widgets and functionality.Attributes:
Name | Type | Description | Default Value | Required | Nullable |
---|---|---|---|---|---|
controller |
| class extends [AnimatedInfinitePaginationController] | - | ||
itemBuilder |
| callback return [Widget] with item [T], to display item card in listView | - | ||
topWidgets |
| pass [topWidgets] when you want to place a widget at the top of the first [itemBuilder] widget. | - | ||
onRefresh |
| callback called when user swipe to refresh list. | - | ||
retry |
| callback called when pagination request failed and user press on [errorWidget]. | - | ||
refreshIndicator |
| wrap [ScrollView] in [RefreshIndicator]. |
| ||
scrollDirection |
| The [Axis] along which the scroll view's offset increases. |
| ||
gridDelegate |
| A delegate that controls the layout of the children within the [GridView]. | - | ||
spawnIsolate |
| Whether to spawn a new isolate on which to calculate the diff on. | - | ||
noItemsWidget |
| widget appears after fetch first page data and the result is empty. | - | ||
loadingWidget |
| widget appears when first page is in loading state. | - |
Implementing AnimatedInfinitePaginationController
Paging Controller works on fetch parts of data and hold this data, handle events like (refreshIndicator) event,We recommend implementing PagingController with Scoped SingletonHere is example to fetch ListResponse of Products with paging:import 'package:injectable/injectable.dart';
import 'package:animated_infinite_scroll_pagination/animated_infinite_scroll_pagination.dart';
@LazySingleton(scope: 'productsPaging')
class ProductsPagingController with AnimatedInfinitePaginationController<Product> {
final ProductsRepository _repository;
ProductsPagingController(this._repository);
/// decide whether two object represent the same Item
@override
bool areItemsTheSame(Product oldItem, Product newItem) {
return oldItem.id == newItem.id;
}
/// fetch data from repository and emit new state
@override
Future<void> fetchData(int page) async {
// emit loading
emitState(const PaginationLoadingState());
// fetch data from server
try {
await for (final response in _repository.findAll(page)) {
final result = response.data;
if (result?.data != null && response.isSuccess) {
// emit fetched data
//
// when emit remote data after cached data with same page,
// controller will replace the cached data with remote data
emitState(PaginationSuccessState(result!.data!.toList(), cached: result.cached));
// tell the controller the total of items,
// this will stop loading more data when last data-chunk is loaded.
setTotal(result.total!);
} else {
// emit error
emitState(const PaginationErrorState());
}
}
} catch (error) {
// emit error
emitState(const PaginationErrorState());
if (kDebugMode) print(error);
}
}
/// cancel all pending requests
void dispose() {
_repository.dispose();
}
}
Implementing AnimatedInfiniteScrollView
class ProductsListScreen extends StatefulWidget {
const ProductsListScreen({super.key});
@override
State<ProductsListScreen> createState() => _ProductsListScreenState();
}
class _ProductsListScreenState extends State<ProductsListScreen>
with LifecycleOwner<ProductsListScreen, ProductsListViewModel>, ObserverMixin {
@override
DiScope get diScope => DiScope(
name: 'productsList', // (viewModel) scope
factory: () => GetIt.instance.initProductsListScope(),
dependencies: [
// be sure to make the 'initProductsScope' before 'initProductsPagingScope'
DiScope(
name: 'products', // (repository and data-source) scope
factory: () => getIt.initProductsScope(),
),
DiScope(
name: 'productsPaging', // (paging controller) scope
factory: () => getIt.initProductsPagingScope(),
),
],
);
@override
Widget build(BuildContext context) {
return Scaffold(
body: AnimatedInfiniteScrollView<Product>(
controller: GetIt.instance<ProductsPagingController>(), // injected pagingController
options: AnimatedInfinitePaginationOptions(
loadingWidget: const CircularProgressIndicator.adaptive(), // Displayed when loading first page
errorWidget: const Center(child: Text('An error occurred')), // Displayed on error
itemBuilder: (BuildContext context, Product product, int index) {
// Build the UI for each product item
return ListTile(
title: Text(product.name!),
subtitle: Text(product.description!),
);
},
),
),
);
}
}