You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中ListView无限滚动异常及分页实现求助

Hey there! Let's tackle your Flutter/Dart questions and fix those issues you're facing, step by step.

1. Implement a ListView with Fixed Item Count (100 entries)

To display exactly 100 items in a ListView, the key is to set an explicit itemCount for ListView.builder—this tells Flutter to only render those 100 items, preventing infinite scrolling. Also, make sure your data source has at least 100 entries to avoid index errors.

Example Code:

// Generate a sample data list with 100 items
final List<String> data = List.generate(100, (index) => 'Item ${index + 1}');

ListView.builder(
  itemCount: data.length, // Use the data list length to fix item count
  itemBuilder: (context, index) {
    // Index ranges from 0 to 99, perfectly matching our 100-item list
    return ListTile(
      title: Text(data[index]),
    );
  },
)

If you don't have a pre-generated list, you can directly set itemCount: 100 and generate content on the fly in itemBuilder—just ensure you don't try to access an out-of-bounds index.

2. Implement Pagination (Load First Page, Then Scroll to Load More)

Pagination works by loading the first page initially, then triggering the next page load when the user scrolls near the bottom of the list. We'll use a ScrollController to track scroll position, plus state variables to manage loading status and remaining data.

Example Code:

class PaginatedListScreen extends StatefulWidget {
  @override
  _PaginatedListScreenState createState() => _PaginatedListScreenState();
}

class _PaginatedListScreenState extends State<PaginatedListScreen> {
  final ScrollController _scrollController = ScrollController();
  List<String> _items = [];
  int _currentPage = 1;
  final int _pageSize = 20; // 20 items per page (total 5 pages = 100 items)
  bool _isLoading = false;
  bool _hasMoreItems = true;

  @override
  void initState() {
    super.initState();
    _loadFirstPage();
    _scrollController.addListener(_checkScrollPosition);
  }

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

  // Load first page data (simulate API call)
  Future<void> _loadFirstPage() async {
    setState(() => _isLoading = true);
    await Future.delayed(const Duration(seconds: 1)); // Simulate network delay
    final newItems = List.generate(_pageSize, (i) => 'Page 1 - Item ${i + 1}');
    setState(() {
      _items = newItems;
      _isLoading = false;
    });
  }

  // Load next page data
  Future<void> _loadNextPage() async {
    if (_isLoading || !_hasMoreItems) return; // Prevent duplicate loads

    setState(() => _isLoading = true);
    await Future.delayed(const Duration(seconds: 1));
    final nextPage = _currentPage + 1;

    // Stop loading once we hit 100 total items
    if (_items.length + _pageSize > 100) {
      final remainingItems = 100 - _items.length;
      final newItems = List.generate(remainingItems, (i) => 'Page $nextPage - Item ${i + 1}');
      setState(() {
        _items.addAll(newItems);
        _hasMoreItems = false;
        _isLoading = false;
      });
      return;
    }

    final newItems = List.generate(_pageSize, (i) => 'Page $nextPage - Item ${i + 1}');
    setState(() {
      _items.addAll(newItems);
      _currentPage = nextPage;
      _isLoading = false;
    });
  }

  // Trigger next page load when near bottom
  void _checkScrollPosition() {
    final nearBottom = _scrollController.position.pixels >= 
        _scrollController.position.maxScrollExtent - 200; // 200px from bottom
    if (nearBottom) _loadNextPage();
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: _scrollController,
      // Add 1 extra item for loading/no-more-data indicator
      itemCount: _items.length + (_isLoading || _hasMoreItems ? 1 : 0),
      itemBuilder: (context, index) {
        if (index == _items.length) {
          // Render loading indicator or "no more data" message
          if (_isLoading) {
            return const Center(child: CircularProgressIndicator());
          } else if (!_hasMoreItems) {
            return const Center(child: Text('No more items to load!'));
          }
          return const SizedBox.shrink();
        }
        return ListTile(title: Text(_items[index]));
      },
    );
  }
}
Fixing Your Current Issues

Issue 1: Infinite Scrolling at the Bottom of ListView

This happens because you didn't set a proper itemCount for your ListView.builder. Without itemCount, Flutter assumes there are infinite items and keeps calling itemBuilder as you scroll.

Fix:

  • For fixed 100 items: Set itemCount: 100 (or your data list length)
  • For pagination: Use itemCount: _items.length + (_isLoading || _hasMoreItems ? 1 : 0) as shown in the pagination example

Issue 2: RangeError (index): Invalid value: Not in range 0..99, inclusive: 100

This error means you're trying to access index 100, but your data list only has 100 items (valid indices 0-99). Common causes:

  • itemCount is set to a value larger than your data list length
  • Pagination logic doesn't stop loading once you hit 100 total items

Fix:

  • Ensure itemCount never exceeds your data list's actual length (for fixed lists)
  • In pagination, add a check to stop loading when _items.length >= 100 (like in the pagination example above), and set _hasMoreItems = false so no extra items are added to itemCount

内容的提问来源于stack exchange,提问作者Paresh Mayani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:19:31