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

A Flutter infinite scroll list/grid with load-more pagination. Supports empty, loading, error, header, and footer slots.

example/lib/main.dart

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

void main() {
  runApp(const InfinitePagingWidgetDemo());
}

class InfinitePagingWidgetDemo extends StatelessWidget {
  const InfinitePagingWidgetDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'infinite_paging_widget',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF1B4DFF),
          brightness: Brightness.light,
        ),
        useMaterial3: true,
        scaffoldBackgroundColor: const Color(0xFFF2F4F8),
      ),
      home: const DemoHomePage(),
    );
  }
}

class DemoHomePage extends StatefulWidget {
  const DemoHomePage({super.key});

  @override
  State<DemoHomePage> createState() => _DemoHomePageState();
}

class _DemoHomePageState extends State<DemoHomePage> {
  static const int _pageSize = 20;
  static const int _totalItems = 80;

  final List<String> _items = [];
  var _displayType = PagingDisplayType.list;
  var _isLoading = false;
  var _hasMore = true;
  var _page = 0;

  @override
  void initState() {
    super.initState();
    _loadMore();
  }

  Future<void> _loadMore() async {
    if (_isLoading || !_hasMore) return;

    setState(() => _isLoading = true);
    await Future<void>.delayed(const Duration(milliseconds: 650));

    final start = _page * _pageSize;
    final end = (start + _pageSize).clamp(0, _totalItems);
    final next = [for (var i = start; i < end; i++) 'Item ${i + 1}'];

    if (!mounted) return;
    setState(() {
      _items.addAll(next);
      _page++;
      _hasMore = _items.length < _totalItems;
      _isLoading = false;
    });
  }

  void _reset() {
    setState(() {
      _items.clear();
      _page = 0;
      _hasMore = true;
      _isLoading = false;
    });
    _loadMore();
  }

  @override
  Widget build(BuildContext context) {
    final scheme = Theme.of(context).colorScheme;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Infinite Paging'),
        centerTitle: false,
        backgroundColor: scheme.surface,
        surfaceTintColor: Colors.transparent,
        actions: [
          IconButton(
            tooltip: 'Reset',
            onPressed: _reset,
            icon: const Icon(Icons.refresh),
          ),
        ],
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Padding(
            padding: const EdgeInsets.fromLTRB(16, 12, 16, 8),
            child: Text(
              'Scroll near the bottom to load the next fake page '
              '(${_items.length}/$_totalItems).',
              style: Theme.of(
                context,
              ).textTheme.bodyLarge?.copyWith(color: scheme.onSurfaceVariant),
            ),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            child: SegmentedButton<PagingDisplayType>(
              segments: const [
                ButtonSegment(
                  value: PagingDisplayType.list,
                  label: Text('List'),
                  icon: Icon(Icons.view_list_outlined),
                ),
                ButtonSegment(
                  value: PagingDisplayType.grid,
                  label: Text('Grid'),
                  icon: Icon(Icons.grid_view_outlined),
                ),
              ],
              selected: {_displayType},
              onSelectionChanged: (value) {
                setState(() => _displayType = value.first);
              },
            ),
          ),
          const SizedBox(height: 8),
          Expanded(
            child: InfinitePagingWidget<String>(
              items: _items,
              displayType: _displayType,
              hasMore: _hasMore,
              isLoading: _isLoading,
              onLoadMore: _loadMore,
              padding: const EdgeInsets.fromLTRB(16, 8, 16, 24),
              gridCrossAxisCount: 2,
              gridMainAxisSpacing: 12,
              gridCrossAxisSpacing: 12,
              gridChildAspectRatio: 1.15,
              separatorBuilder: _displayType == PagingDisplayType.list
                  ? (context, index) => const SizedBox(height: 8)
                  : null,
              itemBuilder: (context, item, index) {
                if (_displayType == PagingDisplayType.grid) {
                  return _GridCard(label: item, index: index);
                }
                return _ListCard(label: item, index: index);
              },
              footer: !_hasMore && _items.isNotEmpty
                  ? Padding(
                      padding: const EdgeInsets.only(bottom: 24),
                      child: Text(
                        'End of results',
                        textAlign: TextAlign.center,
                        style: TextStyle(color: scheme.onSurfaceVariant),
                      ),
                    )
                  : null,
            ),
          ),
        ],
      ),
    );
  }
}

class _ListCard extends StatelessWidget {
  const _ListCard({required this.label, required this.index});

  final String label;
  final int index;

  @override
  Widget build(BuildContext context) {
    final scheme = Theme.of(context).colorScheme;

    return Card(
      elevation: 0,
      color: scheme.surfaceContainerHighest.withValues(alpha: 0.55),
      child: ListTile(
        leading: CircleAvatar(
          backgroundColor: scheme.primaryContainer,
          child: Text('${index + 1}'),
        ),
        title: Text(label),
        subtitle: const Text('Fake paginated row'),
      ),
    );
  }
}

class _GridCard extends StatelessWidget {
  const _GridCard({required this.label, required this.index});

  final String label;
  final int index;

  @override
  Widget build(BuildContext context) {
    final scheme = Theme.of(context).colorScheme;

    return DecoratedBox(
      decoration: BoxDecoration(
        color: scheme.surface,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: scheme.outlineVariant),
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          CircleAvatar(
            backgroundColor: scheme.primaryContainer,
            child: Text('${index + 1}'),
          ),
          const SizedBox(height: 10),
          Text(label, style: const TextStyle(fontWeight: FontWeight.w600)),
        ],
      ),
    );
  }
}
0
likes
160
points
88
downloads
screenshot

Documentation

API reference

Publisher

verified publisherayushd70.dev

Weekly Downloads

A Flutter infinite scroll list/grid with load-more pagination. Supports empty, loading, error, header, and footer slots.

Repository (GitHub)
View/report issues

Topics

#ui #list #pagination #infinite-scroll #widget

License

MIT (license)

Dependencies

flutter

More

Packages that depend on infinite_paging_widget