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

Flutter处理Movie API空数据:基于Repo、Provider与MovieWidget实现随机有效电影推荐方案问询

Hey there! Let's work through this problem together. The main goal here is to make sure users never see a blank screen when they tap that recommend button—instead, we’ll keep fetching until we get a valid movie ID, all while working with your Repo, Provider, and MovieWidget setup. Here’s how to do it:

1. Update the Repository Layer to Handle Retries

The repo is where we’ll implement the core retry logic. We’ll generate random IDs, check if the API returns valid data, and loop until we get a hit (or hit a max retry limit to avoid infinite loops).

import 'dart:math';
import 'package:flutter/foundation.dart';

class MovieRepo {
  final ApiService apiService; // Assume this is your existing API client

  MovieRepo(this.apiService);

  // Add max retries to prevent infinite loops if most IDs are invalid
  Future<Movie> getRandomMovie({int maxRetries = 10}) async {
    int attemptCount = 0;
    
    while (attemptCount < maxRetries) {
      try {
        // Generate your random MovieID exactly as you did before
        final randomId = Random().nextInt(100000) + 1;
        final movie = await apiService.fetchMovieById(randomId);
        
        // Validate the movie data (adjust this check based on your API's response structure)
        if (movie.id != null && movie.title.isNotEmpty) {
          return movie; // Success! Return the valid movie
        }
      } catch (e) {
        // Catch API errors like 404 (invalid ID) or network issues
        if (kDebugMode) {
          print("Failed attempt ${attemptCount + 1}: $e");
        }
      }
      
      attemptCount++;
      // Add a tiny delay between retries to avoid spamming the API
      await Future.delayed(const Duration(milliseconds: 200));
    }
    
    // If we exhaust all retries, throw an error to handle in the provider
    throw Exception("Couldn't find a valid movie after $maxRetries attempts");
  }
}
2. Manage State with Provider

Next, we’ll update the provider to handle loading, success, and error states. This ensures the UI always shows feedback instead of a blank screen.

import 'package:flutter/material.dart';

class MovieProvider extends ChangeNotifier {
  final MovieRepo movieRepo;
  
  Movie? _currentMovie;
  bool _isLoading = false;
  String? _errorMessage;

  Movie? get currentMovie => _currentMovie;
  bool get isLoading => _isLoading;
  String? get errorMessage => _errorMessage;

  MovieProvider(this.movieRepo);

  Future<void> fetchRandomMovie() async {
    // Set loading state first so the UI shows a spinner
    _isLoading = true;
    _errorMessage = null;
    notifyListeners();

    try {
      // Call our repo's retry-enabled method
      _currentMovie = await movieRepo.getRandomMovie();
    } catch (e) {
      // Capture errors if all retries fail
      _errorMessage = e.toString();
    } finally {
      // Turn off loading state regardless of success/error
      _isLoading = false;
      notifyListeners();
    }
  }
}
3. Update the UI (MovieWidget + Recommend Button)

Finally, we’ll adjust the MovieWidget to react to the provider’s state. Users will see a loading spinner while fetching, valid movie data when it works, or an error message with a retry option if all attempts fail.

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

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

  @override
  Widget build(BuildContext context) {
    final movieProvider = Provider.of<MovieProvider>(context);

    return Scaffold(
      appBar: AppBar(title: const Text("Random Movie Recommender")),
      body: Center(
        child: movieProvider.isLoading
            ? const CircularProgressIndicator() // Show spinner while fetching
            : movieProvider.errorMessage != null
                ? Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Text(
                        movieProvider.errorMessage!,
                        textAlign: TextAlign.center,
                        style: const TextStyle(color: Colors.red),
                      ),
                      const SizedBox(height: 16),
                      ElevatedButton(
                        onPressed: () => movieProvider.fetchRandomMovie(),
                        child: const Text("Try Again"),
                      ),
                    ],
                  )
                : movieProvider.currentMovie != null
                    ? Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          Text(
                            movieProvider.currentMovie!.title,
                            style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
                          ),
                          const SizedBox(height: 8),
                          Text("Release Date: ${movieProvider.currentMovie!.releaseDate}"),
                          // Add other movie details (poster, overview, etc.) here
                        ],
                      )
                    : const Text("Tap the shuffle button to get started!"),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => movieProvider.fetchRandomMovie(),
        child: const Icon(Icons.shuffle),
      ),
    );
  }
}
Key Things to Keep in Mind
  • Max Retries: Don’t skip setting a max retry limit—this prevents your app from getting stuck in an infinite loop if the API is down or most IDs are invalid.
  • API Rate Limits: The small delay between retries helps avoid hitting the API’s rate limits too quickly. Adjust the duration if needed based on the API’s rules.
  • Validation Logic: Tweak the movie data check (if (movie.id != null && movie.title.isNotEmpty)) to match your API’s actual response—some APIs might return a 404 status for invalid IDs, which will be caught in the catch block automatically.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 21:23:08