mo_infinite_scroll 1.0.2 copy "mo_infinite_scroll: ^1.0.2" to clipboard
mo_infinite_scroll: ^1.0.2 copied to clipboard

Infinite scroll for Flutter with pull-to-refresh, pre-fetch, a Sliver variant, and customisable loading, empty, and error placeholders.

example/example.dart

// ignore_for_file: public_member_api_docs

/// This file is the pub.dev example tab for mo_infinite_scroll.
///
/// It shows the two most common use cases side by side:
///   1. [MoInfiniteScrollFlow]        – standalone list with built-in pull-to-refresh.
///   2. [MoSliverInfiniteScrollFlow]  – sliver variant inside a [CustomScrollView],
///      with pull-to-refresh delegated to the parent [RefreshIndicator].
library;

import 'package:flutter/material.dart';
import 'package:mo_infinite_scroll/mo_infinite_scroll.dart';
import 'package:mo_infinite_scroll/mo_infinite_scroll_controller.dart';
import 'package:mo_infinite_scroll/mo_infinite_scroll_sliver.dart';

void main() => runApp(const App());

// ── Fake model & API ──────────────────────────────────────────────────────────

class Post {
  const Post({required this.id, required this.title});

  final int id;
  final String title;
}

/// Simulates a remote API: 47 posts total, 800 ms latency.
Future<List<Post>> _fetchPosts(int page, int limit) async {
  await Future.delayed(const Duration(milliseconds: 800));

  const total = 47;
  final start = (page - 1) * limit;
  if (start >= total) return [];

  return List.generate(
    (start + limit).clamp(0, total) - start,
    (i) => Post(id: start + i + 1, title: 'Post #${start + i + 1}'),
  );
}

// ── App shell ─────────────────────────────────────────────────────────────────

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'mo_infinite_scroll example',
      theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
      home: const _HomePage(),
    );
  }
}

class _HomePage extends StatelessWidget {
  const _HomePage();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('mo_infinite_scroll')),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            // ── Option 1: standalone list ──────────────────────────────────
            FilledButton(
              onPressed: () => Navigator.push(
                context,
                MaterialPageRoute(builder: (_) => const _StandalonePage()),
              ),
              child: const Text('InfiniteScrollFlow (standalone)'),
            ),
            const SizedBox(height: 12),
            // ── Option 2: sliver inside CustomScrollView ───────────────────
            FilledButton.tonal(
              onPressed: () => Navigator.push(
                context,
                MaterialPageRoute(builder: (_) => const _SliverPage()),
              ),
              child: const Text('MoSliverInfiniteScrollFlow'),
            ),
          ],
        ),
      ),
    );
  }
}

// ── Example 1 — InfiniteScrollFlow ───────────────────────────────────────────
//
// The simplest usage: only [fetcher] and [itemBuilder] are required.
// Pull-to-refresh is built in. An external [MoInfiniteScrollController] is
// passed so the AppBar action can also trigger a refresh.

class _StandalonePage extends StatefulWidget {
  const _StandalonePage();

  @override
  State<_StandalonePage> createState() => _StandalonePageState();
}

class _StandalonePageState extends State<_StandalonePage> {
  final _controller = MoInfiniteScrollController<Post>();

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Standalone'),
        actions: [
          // Refresh from outside the widget via the external controller.
          IconButton(
            icon: const Icon(Icons.refresh),
            tooltip: 'Refresh',
            onPressed: () => _controller.refresh(_fetchPosts, 10),
          ),
        ],
      ),
      body: MoInfiniteScroll<Post>(
        controller: _controller,
        fetcher: _fetchPosts,
        limit: 10,
        prefetchOffset: 3,
        itemBuilder: (context, post) => _PostTile(post: post),
        separatorBuilder: (_, __) => const Divider(height: 1),
      ),
    );
  }
}

// ── Example 2 — MoSliverInfiniteScrollFlow ─────────────────────────────────────
//
// Drop the sliver into any CustomScrollView. Pull-to-refresh is handled by
// the wrapping RefreshIndicator; the controller is passed in so it can be
// called from onRefresh.

class _SliverPage extends StatefulWidget {
  const _SliverPage();

  @override
  State<_SliverPage> createState() => _SliverPageState();
}

class _SliverPageState extends State<_SliverPage> {
  final _controller = MoInfiniteScrollController<Post>();

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: RefreshIndicator(
        onRefresh: () => _controller.refresh(_fetchPosts, 10),
        child: CustomScrollView(
          slivers: [
            const SliverAppBar(
              title: Text('Sliver'),
              floating: true,
              snap: true,
            ),
            MoInfiniteScrollSliver<Post>(
              controller: _controller,
              fetcher: _fetchPosts,
              limit: 10,
              prefetchOffset: 3,
              itemBuilder: (context, post) => _PostTile(post: post),
              separatorBuilder: (_, __) =>
                  const Divider(height: 1, indent: 16, endIndent: 16),
            ),
          ],
        ),
      ),
    );
  }
}

// ── Shared item tile ──────────────────────────────────────────────────────────

class _PostTile extends StatelessWidget {
  const _PostTile({required this.post});

  final Post post;

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: CircleAvatar(child: Text('${post.id}')),
      title: Text(post.title),
      subtitle: Text('Page ${((post.id - 1) ~/ 10) + 1}'),
    );
  }
}
2
likes
160
points
5
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Infinite scroll for Flutter with pull-to-refresh, pre-fetch, a Sliver variant, and customisable loading, empty, and error placeholders.

Homepage
Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on mo_infinite_scroll