0
0
Fluttermobile~20 mins

Infinite scrolling in Flutter - Mini App: Build & Ship

Choose your learning style9 modes available
Build: Infinite Scroll List
A screen that shows a list of items which loads more items automatically when the user scrolls near the bottom.
Target UI
-------------------------
| Infinite Scroll List   |
-------------------------
| Item 1                |
| Item 2                |
| Item 3                |
| ...                   |
| Loading more...       |
-------------------------
Display a vertical scrollable list of items labeled 'Item 1', 'Item 2', etc.
Initially show 20 items.
When the user scrolls near the bottom, automatically load 20 more items.
Show a loading indicator at the bottom while loading more items.
Use a StatefulWidget to manage the list and loading state.
Starter Code
Flutter
import 'package:flutter/material.dart';

class InfiniteScrollList extends StatefulWidget {
  @override
  State<InfiniteScrollList> createState() => _InfiniteScrollListState();
}

class _InfiniteScrollListState extends State<InfiniteScrollList> {
  final ScrollController _scrollController = ScrollController();
  List<String> _items = [];
  bool _isLoading = false;

  @override
  void initState() {
    super.initState();
    _loadInitialItems();
    _scrollController.addListener(_onScroll);
  }

  void _loadInitialItems() {
    // TODO: Add code to load initial 20 items
  }

  void _onScroll() {
    // TODO: Add code to detect scroll near bottom and load more items
  }

  void _loadMoreItems() async {
    // TODO: Add code to load 20 more items with delay
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Infinite Scroll List')),
      body: ListView.builder(
        controller: _scrollController,
        itemCount: _items.length + (_isLoading ? 1 : 0),
        itemBuilder: (context, index) {
          if (index < _items.length) {
            return ListTile(title: Text(_items[index]));
          } else {
            return const Padding(
              padding: EdgeInsets.all(16.0),
              child: Center(child: CircularProgressIndicator()),
            );
          }
        },
      ),
    );
  }
}
Task 1
Task 2
Task 3
Solution
Flutter
import 'package:flutter/material.dart';

class InfiniteScrollList extends StatefulWidget {
  @override
  State<InfiniteScrollList> createState() => _InfiniteScrollListState();
}

class _InfiniteScrollListState extends State<InfiniteScrollList> {
  final ScrollController _scrollController = ScrollController();
  List<String> _items = [];
  bool _isLoading = false;

  @override
  void initState() {
    super.initState();
    _loadInitialItems();
    _scrollController.addListener(_onScroll);
  }

  void _loadInitialItems() {
    _items = List.generate(20, (index) => 'Item ${index + 1}');
    setState(() {});
  }

  void _onScroll() {
    if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 100 && !_isLoading) {
      _loadMoreItems();
    }
  }

  Future<void> _loadMoreItems() async {
    setState(() {
      _isLoading = true;
    });
    await Future.delayed(const Duration(seconds: 2));
    final nextItems = List.generate(20, (index) => 'Item ${_items.length + index + 1}');
    _items.addAll(nextItems);
    setState(() {
      _isLoading = false;
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Infinite Scroll List')),
      body: ListView.builder(
        controller: _scrollController,
        itemCount: _items.length + (_isLoading ? 1 : 0),
        itemBuilder: (context, index) {
          if (index < _items.length) {
            return ListTile(title: Text(_items[index]));
          } else {
            return const Padding(
              padding: EdgeInsets.all(16.0),
              child: Center(child: CircularProgressIndicator()),
            );
          }
        },
      ),
    );
  }
}

This solution uses a StatefulWidget to keep track of the list of items and whether the app is currently loading more items.

We start by loading 20 items labeled 'Item 1' to 'Item 20'. The ScrollController listens for scroll events. When the user scrolls near the bottom (within 100 pixels), and if not already loading, it triggers _loadMoreItems().

The _loadMoreItems() method simulates a network delay of 2 seconds, then adds 20 more items to the list. While loading, a circular progress indicator is shown at the bottom of the list.

This creates a smooth infinite scrolling experience where more items appear as the user scrolls down.

Final Result
Completed Screen
-------------------------
| Infinite Scroll List   |
-------------------------
| Item 1                |
| Item 2                |
| Item 3                |
| ...                   |
| Item 40               |
| Loading more...       |
-------------------------
User scrolls down the list.
When near the bottom, a loading spinner appears.
After 2 seconds, 20 more items appear below.
User can keep scrolling to load more items endlessly.
Stretch Goal
Add a pull-to-refresh feature that reloads the list back to the first 20 items.
💡 Hint
Wrap the ListView.builder with a RefreshIndicator widget and implement the onRefresh callback to reset the list.