Skip to content

dombroks/pagist

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

24 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Pagist πŸ“„

pub package CI codecov style: very good analysis License: MIT Flutter

A modern, efficient, and state-management agnostic pagination library for Flutter applications. Pagist provides a clean abstraction for handling paginated data with automatic loading, error handling, and refresh capabilities.

✨ Features

  • πŸ”„ Automatic pagination: Seamlessly loads more data as users scroll
  • πŸ”„ Pull-to-refresh: Built-in refresh functionality
  • 🎯 State management agnostic: Works with any state management solution
  • πŸ›‘οΈ Type-safe: Full TypeScript-like generics support
  • 🎨 Customizable UI: Flexible widget customization for all states
  • ⚑ Performance optimized: Efficient memory usage and smooth scrolling
  • πŸ§ͺ Well tested: Comprehensive test coverage
  • πŸ“± Production ready: Battle-tested in real applications

πŸš€ Getting Started

Installation

Add pagist to your pubspec.yaml:

dependencies:
  pagist: ^0.0.1

Then run:

flutter pub get

Import

import 'package:pagist/pagist.dart';

πŸ“– Usage

Basic Example

Here's a simple example showing how to implement pagination for a list of users:

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

// 1. Define your data model
class User {
  final int id;
  final String name;
  final String email;

  User({required this.id, required this.name, required this.email});
}

// 2. Implement PagingSource
class UserPagingSource extends PagingSource<int, User> {
  @override
  Future<LoadResult<int, User>> load(LoadParams<int> params) async {
    final page = params.key ?? 1;
    final users = await fetchUsersFromApi(page, params.loadSize);
    
    return LoadResult<int, User>(
      data: users,
      nextKey: users.isNotEmpty ? page + 1 : null,
    );
  }
  
  Future<List<User>> fetchUsersFromApi(int page, int pageSize) async {
    // Your API call implementation
    // ...
  }
}

// 3. Use in your widget
class UserListPage extends StatefulWidget {
  @override
  _UserListPageState createState() => _UserListPageState();
}

class _UserListPageState extends State<UserListPage> {
  late PaginationController<int, User> _controller;

  @override
  void initState() {
    super.initState();
    _controller = PaginationController<int, User>(
      pagingSource: UserPagingSource(),
      pageSize: 20,
      initialKey: 1,
    );
    _controller.loadMore(); // Load initial data
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Users')),
      body: PaginatedListView<User>(
        controller: _controller,
        itemBuilder: (context, user, index) {
          return ListTile(
            title: Text(user.name),
            subtitle: Text(user.email),
          );
        },
      ),
    );
  }
  
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

Advanced Customization

PaginatedListView<User>(
  controller: _controller,
  enableRefresh: true,
  loadMoreThreshold: 200.0, // Load more when 200px from bottom
  itemBuilder: (context, user, index) {
    return UserCard(user: user);
  },
  loadingWidget: Center(
    child: CircularProgressIndicator(),
  ),
  errorWidget: ErrorRetryWidget(
    onRetry: () => _controller.refresh(),
  ),
  emptyWidget: EmptyStateWidget(),
  loadingMoreWidget: Padding(
    padding: EdgeInsets.all(16),
    child: Center(child: CircularProgressIndicator()),
  ),
)

Using with State Management

Pagist works seamlessly with any state management solution. Here's an example with Provider:

class UserListNotifier extends ChangeNotifier {
  late PaginationController<int, User> _controller;
  
  UserListNotifier() {
    _controller = PaginationController<int, User>(
      pagingSource: UserPagingSource(),
      pageSize: 20,
    );
    
    // Listen to pagination state changes
    _controller.stateStream.listen((_) => notifyListeners());
  }
  
  PaginationState<User> get state => _controller.currentState;
  
  void loadMore() => _controller.loadMore();
  void refresh() => _controller.refresh();
  
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

πŸ—οΈ Architecture

Pagist follows a clean architecture pattern with clear separation of concerns:

Core Components

  • PagingSource<Key, Value>: Abstract class defining how to load data
  • PaginationController<Key, Value>: Manages pagination state and loading logic
  • PaginatedListView<T>: Flutter widget that displays paginated data
  • LoadParams<Key>: Parameters passed to the paging source
  • LoadResult<Key, Value>: Result returned by the paging source
  • PaginationState<Value>: Current state of pagination

Flow Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  PagingSource   │◄───│PaginationController│◄───│PaginatedListViewβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                       β”‚                       β”‚
         β”‚                       β”‚                       β”‚
         β–Ό                       β–Ό                       β–Ό
    Load Data              Manage State            Display UI

πŸ”§ API Reference

PagingSource

abstract class PagingSource<Key, Value> {
  Future<LoadResult<Key, Value>> load(LoadParams<Key> params);
}

PaginationController

class PaginationController<Key, Value> {
  PaginationController({
    required PagingSource<Key, Value> pagingSource,
    int pageSize = 20,
    Key? initialKey,
  });
  
  Stream<PaginationState<Value>> get stateStream;
  PaginationState<Value> get currentState;
  
  Future<void> loadMore();
  Future<void> refresh();
  void dispose();
}

PaginatedListView

class PaginatedListView<T> extends StatefulWidget {
  const PaginatedListView({
    required this.controller,
    required this.itemBuilder,
    this.loadingWidget,
    this.errorWidget,
    this.emptyWidget,
    this.loadingMoreWidget,
    this.loadMoreThreshold = 200.0,
    this.enableRefresh = true,
  });
}

πŸ§ͺ Testing

Pagist is designed to be easily testable. Here's how to test your pagination logic:

void main() {
  group('UserPagingSource', () {
    late UserPagingSource pagingSource;
    
    setUp(() {
      pagingSource = UserPagingSource();
    });
    
    test('should load first page correctly', () async {
      final result = await pagingSource.load(
        LoadParams<int>(key: 1, loadSize: 10),
      );
      
      expect(result.data.length, 10);
      expect(result.nextKey, 2);
    });
  });
}

🀝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup

  1. Fork the repository
  2. Clone your fork: git clone https://github.com/yourusername/pagist.git
  3. Install dependencies: flutter pub get
  4. Run tests: flutter test
  5. Create a branch: git checkout -b feature/your-feature
  6. Make your changes and add tests
  7. Run tests: flutter test
  8. Submit a pull request

πŸ“ License

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

πŸ™‹β€β™‚οΈ Support

🌟 Show your support

Give a ⭐️ if this project helped you!

About

Flutter pagination made easy and effective

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published