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

Flutter中ListView需热重载/刷新才显示且出现RangeError(index)问题求助

Hey there, let's tackle your two issues step by step. I've gone through your code and spotted several key problems causing both the ListView display delay and the RangeError.

1. Why You're Facing These Issues

RangeError Root Cause

You're using uniqueImages[index + 1] in the ListView's image builder, but uniqueImages only gets populated when the CarouselSlider's itemBuilder runs. Since you have two separate FutureBuilders making duplicate API calls, the ListView's data might load before the Carousel's, leaving uniqueImages empty. Plus, modifying state variables like urlImages directly in widget build methods leads to inconsistent list lengths and invalid index access.

ListView Hot Reload Requirement

  • You're calling apiCall() twice (once for Carousel, once for ListView), creating separate network requests that resolve at different times.
  • The Future in each FutureBuilder is recreated every time the widget rebuilds (e.g., when setState runs), causing snapshot state chaos and making the ListView only render reliably after a hot reload.
  • Modifying class-level lists like growableList inside the ListView's itemBuilder triggers unnecessary rebuilds and state inconsistencies.

Fixed Code Implementation

import 'package:cached_network_image/cached_network_image.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:video_games/model.dart';
import 'package:flutter/material.dart';
import 'package:video_games/send_models.dart';
import 'package:video_games/views/details_view.dart';
import 'package:get/get.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';

class HomePage extends StatefulWidget {
  HomePage({ Key? key, }) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // Store the API future once to avoid duplicate requests
  late Future<Autogenerated> _gamesFuture;
  int slideractiveindex = 0;

  @override
  void initState() {
    super.initState();
    // Initialize the API request once when the widget loads
    _gamesFuture = apiCall();
    // Clear cache only once on initialization
    clearCache();
  }

  Future<Autogenerated> apiCall() async {
    final response = await http.get(Uri.parse(
        'https://api.rawg.io/api/games?key=5ac29048d12d45d0949c77038115cb56&page=1'));
    return Autogenerated.fromJson(jsonDecode(response.body));
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: FutureBuilder<Autogenerated>(
        // Reuse the single stored future instead of creating new ones
        future: _gamesFuture,
        builder: (BuildContext context, AsyncSnapshot<Autogenerated> snapshot) {
          // Handle all possible snapshot states properly
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          } else if (snapshot.hasError) {
            return Center(child: Text('Failed to load data: ${snapshot.error}'));
          } else if (!snapshot.hasData || snapshot.data?.results == null) {
            return const Center(child: Text('No games found'));
          }

          final List<Results> games = snapshot.data!.results!;
          // Generate unique images once when data is ready
          final List<String?> uniqueImages = games
              .map((game) => game.backgroundImage)
              .where((image) => image != null)
              .toSet()
              .toList();

          return Column(
            children: [
              CarouselSlider.builder(
                // Don't render more slides than available images
                itemCount: uniqueImages.length.clamp(0, 20),
                itemBuilder: (context, index, realIndex) {
                  final urlImage = uniqueImages[index];
                  return buildImage(urlImage, index);
                },
                options: CarouselOptions(
                    autoPlay: true,
                    enlargeCenterPage: true,
                    onPageChanged: (index, reason) {}),
              ),
              const SizedBox(height: 32),
              ListView.builder(
                physics: const NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemBuilder: (BuildContext context, index) {
                  final game = games[index];
                  // Use the game's own background image instead of guessing indices
                  final urlImage = game.backgroundImage;
                  return Padding(
                    padding: const EdgeInsets.all(10.0),
                    child: ListTile(
                      tileColor: Colors.black12,
                      leading: InkWell(
                          onTap: () {
                            Navigator.push(
                              context,
                              MaterialPageRoute(
                                builder: (context) => DetailsPage(
                                  id: '${game.id}',
                                ),
                              ));
                          },
                          child: buildImage2(urlImage, index)),
                      title: Column(
                        children: [
                          Text('${game.name}'),
                          Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              Text('Rating : ${game.rating}'),
                              const SizedBox(width: 20),
                              Text('Release : ${game.released}'),
                            ],
                          )
                        ],
                      ),
                    ),
                  );
                },
                // Don't render more items than available games
                itemCount: games.length.clamp(0, 19),
              ),
            ],
          );
        },
      ),
    );
  }

  Widget buildImage(String? urlImage, int index) {
    return Container(
      margin: const EdgeInsets.symmetric(horizontal: 12),
      color: Colors.grey,
      child: CachedNetworkImage(
        imageUrl: urlImage!,
        fit: BoxFit.cover,
        key: UniqueKey(),
        errorWidget: (context, url, error) => const Icon(Icons.error),
        placeholder: (context, url) => const Center(child: CircularProgressIndicator()),
      ),
    );
  }

  Widget buildImage2(String? urlImage, int index) {
    return Container(
      height: 100,
      width: 100,
      child: CachedNetworkImage(
        imageUrl: urlImage!,
        key: UniqueKey(),
        fit: BoxFit.cover,
        errorWidget: (context, url, error) => const Icon(Icons.error),
        placeholder: (context, url) => const Center(child: CircularProgressIndicator()),
      ),
    );
  }

  void clearCache() {
    DefaultCacheManager().emptyCache();
    imageCache!.clear();
    imageCache!.clearLiveImages();
  }
}

Key Fixes Breakdown

  1. Single API Request: We store the API future in _gamesFuture and initialize it once in initState(), so both the Carousel and ListView use the exact same data set with no duplicate requests.
  2. Robust Snapshot Handling: We explicitly handle loading, error, and empty data states to prevent trying to render content before data is ready.
  3. Eliminate State Bugs: Removed messy class-level lists that were modified in build methods. Instead, we generate unique images once when data loads, and use each game's own background image directly in the ListView (no more invalid index guesswork).
  4. Prevent RangeErrors: Added .clamp(0, ...) to item counts to ensure we never try to render more items than available data, and removed the risky index + 1 offset.
  5. Optimize Cache Clearing: Moved cache clearing to initState() so it only runs once, avoiding unnecessary rebuilds and state inconsistencies.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 12:23:11