pagify 0.1.0 copy "pagify: ^0.1.0" to clipboard
pagify: ^0.1.0 copied to clipboard

A flexible and customizable Flutter package for handling paginated data with minimal setup.

Pagination Helper #

pub package GitHub stars GitHub forks GitHub issues License: MIT

A powerful and flexible Flutter package for implementing pagination with both ListView and GridView support. This package handles infinite scrolling, pull-to-refresh, loading states, error handling, and network connectivity checks automatically.

  • normal pagination:

Image

  • reverse pagination:

Image

🚀 Features #

  • Dual View Support: Both ListView and GridView implementations
  • Infinite Scrolling: Automatic loading of next pages when scrolling reaches the end
  • Pull-to-Refresh: Built-in refresh indicator support
  • Network Awareness: Automatic network connectivity checking
  • Error Handling: Comprehensive error handling for HTTP and Dio exceptions
  • Loading States: Customizable loading indicators and states
  • Reverse Pagination: Support for reverse scrolling (useful for chat interfaces)
  • Controller Support: Programmatic control over pagination data and scroll behavior
  • Customizable UI: Custom error builders, loading widgets, and empty state messages
  • Memory Efficient: Optimized scroll position retention during data loading

📦 Installation #

Add this to your package's pubspec.yaml file:

dependencies:
  easy_pagination: ^0.1.0

Then run:

flutter pub get

Dependencies #

This package uses the following dependencies:

  • connectivity_plus - for network connectivity checking
  • dio (optional) - for enhanced HTTP error handling
  • http (optional) - for basic HTTP error handling
  • lottie (optional) - for default loading animations

🎯 Quick Start #

Basic ListView Example #

import 'package:flutter/material.dart';
import 'package:easy_pagination/easy_pagination.dart';

class MyPaginatedList extends StatefulWidget {
  @override
  _MyPaginatedListState createState() => _MyPaginatedListState();
}

class _MyPaginatedListState extends State<MyPaginatedList> {
  final EasyPaginationController<MyModel> controller = 
      EasyPaginationController<MyModel>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Paginated List')),
      body: EasyPagination.listView(
        controller: controller,
        asyncCall: (currentPage) => fetchData(currentPage),
        mapper: (response) => DataListAndPaginationData(
          data: response.items,
          paginationData: PaginationData(
            perPage: response.perPage,
            totalPages: response.totalPages,
          ),
        ),
        errorMapper: ErrorMapper(
          errorWhenDio: (e) => "Network error: ${e.message}",
          errorWhenHttp: (e) => "HTTP error: ${e.message}",
        ),
        itemBuilder: (data, index, item) => ListTile(
          title: Text(item.title),
          subtitle: Text(item.description),
        ),
      ),
    );
  }

  Future<ApiResponse> fetchData(int page) async {
    // Your API call implementation
    final response = await apiService.getData(page: page);
    return ApiResponse.fromJson(response.data);
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }
}

GridView Example #

EasyPagination.gridView(
  controller: controller,
  crossAxisCount: 2,
  childAspectRatio: 0.8,
  mainAxisSpacing: 10.0,
  crossAxisSpacing: 10.0,
  asyncCall: (currentPage) => fetchData(currentPage),
  mapper: (response) => DataListAndPaginationData(
    data: response.items,
    paginationData: PaginationData(
      perPage: response.perPage,
      totalPages: response.totalPages,
    ),
  ),
  errorMapper: ErrorMapper(
    errorWhenDio: (e) => "Error: ${e.message}",
  ),
  itemBuilder: (data, index, item) => Card(
    child: Column(
      children: [
        Image.network(item.imageUrl),
        Text(item.title),
      ],
    ),
  ),
)

🔧 Advanced Usage #

Custom Error Handling #

EasyPagination.listView(
  // ... other parameters
  errorMapper: ErrorMapper(
    errorWhenDio: (DioException e) {
      switch (e.type) {
        case DioExceptionType.connectionTimeout:
          return "Connection timeout. Please try again.";
        case DioExceptionType.receiveTimeout:
          return "Server response timeout.";
        case DioExceptionType.badResponse:
          return "Server error: ${e.response?.statusCode}";
        default:
          return "Network error occurred.";
      }
    },
    errorWhenHttp: (HttpException e) => "HTTP Error: ${e.message}",
  ),
            onError: (context, page, e) {
            log('page : $page');
            if(e is PagifyNetworkException){
              log('check your internet connection');

            }else if(e is ApiRequestException){
              log('check your server ${e.msg}');

            }else{
              log('other error ...!');
            }
          },

  errorBuilder: (errorMsg) => Container(
    padding: EdgeInsets.all(20),
    child: Column(
      children: [
        Icon(Icons.error, color: Colors.red, size: 64),
        SizedBox(height: 16),
        Text(errorMsg, textAlign: TextAlign.center),
        ElevatedButton(
          onPressed: () => controller.refresh(),
          child: Text('Retry'),
        ),
      ],
    ),
  ),
)

Custom Loading Widget #

EasyPagination.listView(
  // ... other parameters
  loadingBuilder: Container(
    height: 100,
    child: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          CircularProgressIndicator(color: Colors.blue),
          SizedBox(height: 8),
          Text('Loading more items...'),
        ],
      ),
    ),
  ),
)

Reverse Pagination (Chat-like) #

EasyPagination.listView(
  controller: controller,
  isReverse: true, // Enable reverse pagination
  asyncCall: (currentPage) => fetchOlderMessages(currentPage),
  mapper: (response) => DataListAndPaginationData(
    data: response.messages,
    paginationData: PaginationData(
      perPage: response.perPage,
      totalPages: response.totalPages,
    ),
  ),
  // ... rest of configuration
)

🎮 Controller Methods #

The EasyPaginationController provides various methods to programmatically control the pagination:

final controller = EasyPaginationController<MyModel>();

// Add items
controller.addItem(newItem);
controller.addItemAt(0, newItem);
controller.addAtBeginning(newItem);

// Remove items
controller.removeItem(item);
controller.removeAt(index);
controller.removeWhere((item) => item.id == targetId);

// Update items
controller.replaceWith(index, updatedItem);

// Filter and sort
controller.filterAndUpdate((item) => item.isActive);
controller.sort((a, b) => a.name.compareTo(b.name));

// Access items
MyModel? item = controller.accessElement(index);
MyModel? randomItem = controller.getRandomItem();
List<MyModel> filteredItems = controller.filter((item) => item.category == 'active');

// Scroll control
await controller.moveToMaxBottom();
await controller.moveToMaxTop();

// Refresh
controller.refresh();

// Clear all items
controller.clear();

📊 Data Models #

Your API response should be mappable to DataListAndPaginationData:

class ApiResponse {
  final List<MyModel> items;
  final int perPage;
  final int totalPages;
  final int currentPage;

  ApiResponse({
    required this.items,
    required this.perPage,
    required this.totalPages,
    required this.currentPage,
  });

  factory ApiResponse.fromJson(Map<String, dynamic> json) {
    return ApiResponse(
      items: (json['data'] as List).map((item) => MyModel.fromJson(item)).toList(),
      perPage: json['per_page'],
      totalPages: json['last_page'],
      currentPage: json['current_page'],
    );
  }
}

class MyModel {
  final int id;
  final String title;
  final String description;
  final String imageUrl;

  MyModel({
    required this.id,
    required this.title,
    required this.description,
    required this.imageUrl,
  });

  factory MyModel.fromJson(Map<String, dynamic> json) {
    return MyModel(
      id: json['id'],
      title: json['title'],
      description: json['description'],
      imageUrl: json['image_url'],
    );
  }
}

📋 API Reference #

ListView Parameters #

Parameter Type Description Required
controller EasyPaginationController<Model> Controller for managing pagination state
asyncCall Future<Response> Function(int currentPage) Function to fetch data for given page
mapper DataListAndPaginationData<Model> Function(Response) Maps API response to required format
errorMapper ErrorMapper Handles different types of errors
itemBuilder Widget Function(List<Model>, int, Model) Builds individual list items
onUpdateStatus FutureOr<void> Function(AsyncCallStatus)? Callback for status changes
isReverse bool Enable reverse pagination
onSuccess Function(int currentPage, List<Model> data)? Success callback
onError Function(int currentPage, String errorMessage)? Error callback
loadingBuilder Widget? Custom loading widget
errorBuilder Widget Function(String errorMsg)? Custom error widget
shrinkWrap bool? ListView shrinkWrap property
scrollDirection Axis? Scroll direction
emptyListText String? Text shown when list is empty
noConnectionText String? Text shown when no internet connection

GridView Additional Parameters #

Parameter Type Description
crossAxisCount int? Number of columns in grid
childAspectRatio double? Aspect ratio of grid items
mainAxisSpacing double? Spacing between rows
crossAxisSpacing double? Spacing between columns

📱 Status Management #

The package provides different status states through AsyncCallStatus:

enum PagifyAsyncCallStatus {
  initial,     // Initial state
  loading,     // Data is being loaded
  success,     // Data loaded successfully
  error,       // General error occurred
  networkError, // Network connectivity error
}

You can listen to status changes:

EasyPagination.listView(
  // ... other parameters
  onUpdateStatus: (status) {
    switch (status) {
      case PagifyAsyncCallStatus.loading:
        // Show loading indicator in app bar
        break;
      case PagifyAsyncCallStatus.error:
        // Show error snackbar
        break;
      case PagifyAsyncCallStatus.networkError:
        // Show network error dialog
        break;
      case PagifyAsyncCallStatus.success:
        // Hide any error states
        break;
    }
  },
)

⚠️ Error Handling Best Practices #

ErrorMapper(
  errorWhenDio: (DioException e) {
    if (e.response?.statusCode == 401) {
      return "Authentication failed. Please login again.";
    } else if (e.response?.statusCode == 404) {
      return "Data not found.";
    } else if (e.response?.statusCode == 500) {
      return "Server error. Please try again later.";
    }
    return "Something went wrong.";
  },
  errorWhenHttp: (HttpException e) => "Connection error: ${e.message}",
)

💡 Best Practices #

  • Always dispose controllers: Call controller.dispose() in your widget's dispose method
  • Handle empty states: Provide meaningful empty state messages
  • Implement proper error handling: Use custom error builders for better user experience
  • Optimize item builders: Keep item builders lightweight for smooth scrolling
  • Use appropriate page sizes: Balance between performance and user experience
  • Handle network states: Provide offline indicators when appropriate

📖 Example #

For a complete example, check the /example folder in the package repository.

🐛 Troubleshooting #

Common Issues #

  • Memory leaks
    • Always call controller.dispose() in your widget's dispose method
    • Avoid holding references to controllers in static variables

🤝 Contributing #

Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.

📄 License #

This project is licensed under the MIT License - see the LICENSE file for details.

🙋‍♂️ Support #

If you have any questions or need help with implementation, please open an issue on GitHub.

⭐ Show Your Support #

If this package helped you, please give it a ⭐ on GitHub and like it on pub.flutter-io.cn!


Made with ❤️ by Ahmed Emara linkedIn

15
likes
0
points
321
downloads

Publisher

unverified uploader

Weekly Downloads

A flexible and customizable Flutter package for handling paginated data with minimal setup.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

connectivity_plus, dio, flutter, fluttertoast, http, lottie

More

Packages that depend on pagify