A generic, flexible, and API-agnostic pagination container for Flutter and Dart.
pageable allows you to easily map any paginated API response into a strongly-typed Page<T> object. It captures standard pagination fields, handles "meta overflow" (so you never lose extra API data), and supports infinite scroll through simple data appending.
- Generic Container: Works with any data model that extends
BaseData. - Flexible Mapping: Use
PageConfigto map your API's field names (e.g.,resultsvsdata,current_pagevspageIndex) to internal properties. - Meta Overflow: All JSON fields not explicitly mapped to
dataare stored in ametamap for easy access. - Infinite Scroll Support: Easily merge pages using the
appendmethod. - Full Serialization: Built-in
fromJsonandtoJsonsupport. - Equality: Extends
Equatablefor efficient rebuilding in Flutter (e.g., with Bloc or Riverpod).
Add pageable to your pubspec.yaml:
dependencies:
pageable: ^0.0.1Extend BaseData and implement a factory to parse your JSON.
import 'package:pageable/pageable.dart';
class Product extends BaseData {
final int id;
final String name;
const Product({required this.id, required this.name});
factory Product.fromJson(Map<String, dynamic> json) {
return Product(id: json['id'], name: json['name']);
}
@override
List<Object?> get props => [id, name];
}If your API uses default names (pageIndex, pageSize, totalResults, numberOfPages, data):
final json = {
'pageIndex': 1,
'totalResults': 100,
'data': [{'id': 1, 'name': 'Laptop'}],
};
final page = Page<Product>.fromJson(json, Product.fromJson);
print(page.totalResults); // 100If your API uses different keys, use PageConfig:
final json = {
'current_page': 1,
'results': [...],
'total_count': 50,
};
final config = PageConfig(
pageIndexKey: 'current_page',
dataKey: 'results',
totalResultsKey: 'total_count',
);
final page = Page<Product>.fromJson(json, Product.fromJson, config: config);Merge a newly fetched page into your existing data list:
final nextPage = await api.fetchPage(2);
final combinedPage = currentPage.append(nextPage);Access any unmapped fields directly from the meta map:
final cursor = page.meta['next_cursor'];Tip: Use Dart extensions for typed access!
extension ProductPageX on Page<Product> {
String? get nextCursor => meta['next_cursor'] as String?;
}| Property | Default | Description |
|---|---|---|
pageIndexKey |
pageIndex |
Key for the current page index |
pageSizeKey |
pageSize |
Key for items per page |
totalResultsKey |
totalResults |
Key for total number of items |
numberOfPagesKey |
numberOfPages |
Key for total number of pages |
dataKey |
data |
Key for the list of items |
| Property | Type | Description |
|---|---|---|
data |
List<T>? |
The list of typed items |
meta |
Map<String, dynamic> |
All other API fields |
pageIndex |
int |
Convenience getter for meta['pageIndex'] |
pageSize |
int |
Convenience getter for meta['pageSize'] |
totalResults |
int |
Convenience getter for meta['totalResults'] |
numberOfPages |
int |
Convenience getter for meta['numberOfPages'] |
George Ikwegbu
Contact: 07377349751
This project is licensed under the MIT License - see the LICENSE file for details.