enhanced_pagination_view 1.0.0
enhanced_pagination_view: ^1.0.0 copied to clipboard
Powerful pagination package with O(1) item updates, dual mode support (infinite scroll + pagination buttons), and comprehensive state management.
Enhanced Pagination View π #
A powerful and flexible pagination package for Flutter that solves common pagination challenges with elegance.
β¨ Features #
- π Dual Mode Support: Choose between infinite scroll or traditional pagination buttons
- β‘ O(1) Item Updates: Update, remove, or insert items without full page refresh
- π― Smart State Management: Comprehensive states (loading, error, empty, completed)
- π Pull-to-Refresh: Built-in refresh functionality
- π¨ Fully Customizable: Custom widgets for loading, error, empty, and pagination controls
- π¦ Error Handling: Automatic retry mechanism with error states
- π± Responsive: Works with any scroll direction and physics
- π§© Type Safe: Full TypeScript-like type safety with generics
π Why Enhanced Pagination View? #
Traditional pagination packages (like pagination_view) have limitations:
- β No direct access to loaded items
- β Can't update individual items without full refresh
- β Limited state management
- β No built-in item tracking
Enhanced Pagination View solves these:
- β Direct item list access and manipulation
- β O(1) item updates using key-based lookup
- β Comprehensive state management
- β Built-in Map-based item tracking
π¦ Installation #
Add to your pubspec.yaml:
dependencies:
enhanced_pagination_view: ^1.0.0
π― Quick Start #
1. Basic Infinite Scroll #
import 'package:enhanced_pagination_view/enhanced_pagination_view.dart';
// Create controller
final controller = PagingController<ProfileModel>(
config: PagingConfig(
pageSize: 20,
infiniteScroll: true,
),
pageFetcher: (page) async {
final response = await api.fetchProfiles(page);
return response.data;
},
itemKeyGetter: (item) => item.id, // For O(1) updates
);
// Use in widget
EnhancedPaginationView<ProfileModel>(
controller: controller,
itemBuilder: (context, item, index) {
return ListTile(
title: Text(item.name),
subtitle: Text(item.email),
);
},
)
2. Pagination with Buttons #
final controller = PagingController<ProfileModel>(
config: PagingConfig(
pageSize: 20,
infiniteScroll: false, // Traditional pagination
),
pageFetcher: (page) async {
return await api.fetchProfiles(page);
},
);
EnhancedPaginationView<ProfileModel>(
controller: controller,
itemBuilder: (context, item, index) => ProfileCard(item),
showPaginationButtons: true,
)
3. Update Items Without Refresh β‘ #
The killer feature! Update individual items in the list:
// Update a single item (O(1) with key getter)
controller.updateItem(
updatedProfile,
where: (profile) => profile.id == targetId,
);
// Remove an item
controller.removeItem(
where: (profile) => profile.id == targetId,
);
// Insert at specific position
controller.insertItem(0, newProfile);
// Append to end
controller.appendItem(newProfile);
π¨ Customization #
Custom Loading States #
EnhancedPaginationView<ProfileModel>(
controller: controller,
itemBuilder: (context, item, index) => ProfileCard(item),
// Custom initial loader
initialLoader: Center(
child: Column(
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Loading profiles...'),
],
),
),
// Custom bottom loader (infinite scroll)
bottomLoader: Padding(
padding: EdgeInsets.all(16),
child: CircularProgressIndicator(),
),
// Custom empty state
onEmpty: Center(
child: Column(
children: [
Icon(Icons.inbox, size: 64, color: Colors.grey),
Text('No profiles found'),
],
),
),
// Custom error state
onError: (error) => ErrorWidget(error: error),
)
Custom Pagination Controls #
EnhancedPaginationView<ProfileModel>(
controller: controller,
itemBuilder: (context, item, index) => ProfileCard(item),
paginationBuilder: (controller) {
return Container(
padding: EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: controller.currentPage > 0
? controller.refresh
: null,
child: Text('First'),
),
SizedBox(width: 8),
Text('Page ${controller.currentPage + 1}'),
SizedBox(width: 8),
ElevatedButton(
onPressed: controller.hasMoreData
? controller.loadNextPage
: null,
child: Text('Next'),
),
],
),
);
},
)
π§ Configuration #
PagingConfig Options #
PagingConfig(
pageSize: 20, // Items per page
infiniteScroll: true, // true for infinite, false for buttons
initialPage: 0, // Starting page number
autoLoadFirstPage: true, // Auto-load on init
invisibleItemsThreshold: 3, // Trigger next page when 3 items from end
// Cache behavior (infiniteScroll only):
// Default is a bounded window to avoid unbounded memory growth.
// Set cacheMode: CacheMode.all if you intentionally want to keep everything.
cacheMode: CacheMode.limited,
maxCachedItems: 500,
)
Notes:
- If you use a large
pageSize, setmaxCachedItems >= pageSize. - For βvery large total datasetsβ (e.g. 1M+ over time), prefer
CacheMode.limitedorCacheMode.none.
Controller Methods #
// Loading
controller.loadFirstPage() // Load first page
controller.loadNextPage() // Load next page
controller.refresh() // Refresh from start
controller.retry() // Retry after error
// Item Management
controller.updateItem(item) // Update single item
controller.removeItem(where: ...) // Remove item
controller.insertItem(index, item) // Insert at position
controller.appendItem(item) // Add to end
// State Access
controller.items // List of all items
controller.state // Current PagingState
controller.currentPage // Current page number
controller.hasMoreData // More pages available
controller.error // Last error
controller.isLoading // Currently loading
controller.itemCount // Total items loaded
PagingState Enum #
enum PagingState {
initial, // Before any data loaded
loading, // Loading first page
loaded, // Data loaded successfully
loadingMore, // Loading additional pages
error, // Error occurred
empty, // No data available
completed, // All data loaded
}
π‘ Real-World Example #
Here's how to use it in a voter management app:
class VotersController extends GetxController {
late PagingController<ProfileModel> pagingController;
@override
void onInit() {
super.onInit();
pagingController = PagingController<ProfileModel>(
config: PagingConfig(
pageSize: 20,
infiniteScroll: true,
),
pageFetcher: (page) => fetchVoters(page),
itemKeyGetter: (voter) => voter.id!,
);
}
Future<List<ProfileModel>> fetchVoters(int page) async {
final response = await api.get('/voters',
params: {'page': page, 'limit': 20}
);
return (response.data as List)
.map((json) => ProfileModel.fromJson(json))
.toList();
}
// Update voter religion and reflect in UI immediately
Future<void> updateVoterReligion(String voterId, String religionId) async {
await api.patch('/voters/$voterId', {'religion_id': religionId});
// Update local data - UI updates automatically!
pagingController.updateItem(
pagingController.items
.firstWhere((v) => v.id == voterId)
.copyWith(religionId: religionId),
where: (v) => v.id == voterId,
);
}
@override
void onClose() {
pagingController.dispose();
super.onClose();
}
}
class VotersScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: EnhancedPaginationView<ProfileModel>(
controller: controller.pagingController,
itemBuilder: (context, voter, index) {
return VoterCard(voter: voter);
},
enablePullToRefresh: true,
),
);
}
}
π Comparison with pagination_view #
| Feature | enhanced_pagination_view | pagination_view |
|---|---|---|
| Infinite Scroll | β | β |
| Pagination Buttons | β | β |
| Direct Item Access | β | β |
| Update Single Item | β (O(1)) | β |
| Remove Item | β | β |
| Insert Item | β | β |
| State Management | β Comprehensive | β οΈ Limited |
| Error Retry | β Built-in | β οΈ Manual |
| Pull-to-Refresh | β | β |
| Custom Pagination UI | β | β |
π€ Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
π License #
MIT License - see LICENSE file for details
π¬ Support #
If you find this package helpful, please β star the repo!
For issues and feature requests, please use GitHub Issues.
6. Header & Footer Support #
Add sticky headers and footers to your pagination view:
EnhancedPaginationView<User>(
controller: controller,
// Sticky header widget
header: Container(
padding: EdgeInsets.all(16),
color: Colors.blue.shade50,
child: Column(
children: [
Text('Search Users', style: TextStyle(fontWeight: FontWeight.bold)),
TextField(
decoration: InputDecoration(hintText: 'Search...'),
),
],
),
),
// Footer widget (before pagination controls)
footer: Container(
padding: EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStat('Total', totalCount),
_buildStat('Active', activeCount),
_buildStat('Inactive', inactiveCount),
],
),
),
itemBuilder: (context, user, index) {
return ListTile(title: Text(user.name));
},
)
Header/Footer Features:
- π Sticky header that stays at top
- π Footer for stats/info display
- π¨ Fully customizable widgets
- π Works with both infinite scroll and pagination modes
- β‘ Uses CustomScrollView for optimal performance