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

Flutter本地存储需求:如何持久化API数据避免重复请求

Hey there! As a fellow Flutter dev, I totally get this frustration—route arguments work for one-off data passes, but they don’t cut it for persistent, globally accessible data. Let’s fix this with a straightforward, beginner-friendly solution that combines local storage and a central data manager, exactly matching your needs.

The Root of the Problem

Right now, you’re passing data via route arguments, which only lives as long as the receiving page is in memory. When you navigate away and come back, the page gets reinitialized, and that data is gone. We need two things:

  1. Persistent local storage to save data even when the app restarts or pages are destroyed
  2. A global data access point so any page can fetch the data without re-requesting the API

Solution 1: Singleton Data Manager + Hive (Simple & Perfect for Beginners)

We’ll use a singleton class to handle API calls, local storage, and in-memory caching. For local storage, Hive is ideal here—it’s lightweight, easy to use with custom objects (like your CurrInfo), and doesn’t require complex setup.

Step 1: Add Dependencies

First, add these to your pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  http: ^1.1.0
  hive: ^2.2.3
  hive_flutter: ^1.1.0

dev_dependencies:
  hive_generator: ^1.1.5
  build_runner: ^2.4.6

Step 2: Prepare Your CurrInfo Class for Hive

Hive needs an adapter to store custom objects. Annotate your class and generate the adapter:

import 'package:hive/hive.dart';

part 'curr_info.g.dart';

@HiveType(typeId: 0) // Unique ID for your object type
class CurrInfo {
  @HiveField(0)
  final double price; // Example field—replace with your actual data

  CurrInfo({required this.price});

  // Your existing API parsing logic
  factory CurrInfo.fromJson(Map<String, dynamic> json) {
    return CurrInfo(price: json['price'] as double);
  }

  // Existing API call method
  Future<CurrInfo> getInfo() async {
    // Your existing HTTP request logic here
  }
}

Run this command in your terminal to generate the adapter:

flutter packages pub run build_runner build

Step 3: Create the Singleton Data Manager

This class will handle all data operations—fetching from API, reading/writing to local storage, and caching in memory:

import 'package:hive/hive.dart';
import 'curr_info.dart';

class DataManager {
  // Singleton instance—only one exists across the app
  static final DataManager _instance = DataManager._internal();
  factory DataManager() => _instance;
  DataManager._internal();

  final Box<CurrInfo> _infoBox = Hive.box<CurrInfo>('curr_info_box');
  CurrInfo? _cachedData; // In-memory cache for fast access

  // Get data: prioritize cache → local storage → API
  Future<CurrInfo> getCurrInfo({bool forceRefresh = false}) async {
    // Return cached data if available and not forcing refresh
    if (!forceRefresh && _cachedData != null) {
      return _cachedData!;
    }

    // Check local storage next
    final localData = _infoBox.get('saved_info');
    if (!forceRefresh && localData != null) {
      _cachedData = localData;
      return localData;
    }

    // Fetch from API if no cache/local data, or forced refresh
    final apiData = await CurrInfo().getInfo();
    // Save to local storage and cache
    _infoBox.put('saved_info', apiData);
    _cachedData = apiData;
    return apiData;
  }

  // Force refresh data from API
  Future<CurrInfo> refreshCurrInfo() async {
    return getCurrInfo(forceRefresh: true);
  }
}

Step 4: Initialize Hive & Update Your Pages

Modify your main.dart to initialize Hive before running the app:

import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'curr_info.dart';
import 'data_manager.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Hive.initFlutter();
  Hive.registerAdapter(CurrInfoAdapter());
  await Hive.openBox<CurrInfo>('curr_info_box');
  runApp(const MyApp());
}

Update your loading page to use the data manager (no more route arguments!):

class LoadingPage extends StatefulWidget {
  const LoadingPage({super.key});

  @override
  State<LoadingPage> createState() => _LoadingPageState();
}

class _LoadingPageState extends State<LoadingPage> {
  @override
  void initState() {
    super.initState();
    _loadData();
  }

  Future<void> _loadData() async {
    // Preload data into cache/local storage
    await DataManager().getCurrInfo();
    // Navigate to home page
    Navigator.pushReplacement(
      context,
      MaterialPageRoute(builder: (_) => const HomePage()),
    );
  }

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(child: CircularProgressIndicator()),
    );
  }
}

In your home page (or any other page), fetch data directly from the manager:

class HomePage extends StatefulWidget {
  const HomePage({super.key});

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

class _HomePageState extends State<HomePage> {
  late Future<CurrInfo> _dataFuture;

  @override
  void initState() {
    super.initState();
    _dataFuture = DataManager().getCurrInfo();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('计算APP')),
      body: FutureBuilder<CurrInfo>(
        future: _dataFuture,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            final currInfo = snapshot.data!;
            return Center(child: Text('当前价格: ${currInfo.price}'));
          } else if (snapshot.hasError) {
            return Center(child: Text('加载失败: ${snapshot.error}'));
          }
          return const Center(child: CircularProgressIndicator());
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          // Refresh data and update the UI
          await DataManager().refreshCurrInfo();
          setState(() => _dataFuture = DataManager().getCurrInfo());
        },
        child: const Icon(Icons.refresh),
      ),
    );
  }
}

Bonus: Handling Offline Mode

Add a check in your getInfo() method to return local data if there’s no network:

Future<CurrInfo> getInfo() async {
  try {
    final response = await http.get(Uri.parse('YOUR_API_URL'));
    if (response.statusCode == 200) {
      return CurrInfo.fromJson(jsonDecode(response.body));
    } else {
      throw Exception('请求失败');
    }
  } on SocketException {
    // No internet—return local data if available
    final localData = Hive.box<CurrInfo>('curr_info_box').get('saved_info');
    if (localData != null) {
      return localData;
    } else {
      throw Exception('无网络且无本地缓存数据');
    }
  }
}

Why This Works

  • Global Access: The singleton DataManager lets any page fetch data without route arguments.
  • No Repeated API Calls: In-memory cache and local storage mean you only hit the API when needed.
  • Offline Support: Local storage keeps data available even without internet.
  • Easy Refresh: The refreshCurrInfo() method lets you force an API update anytime.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 07:27:20