import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; // 引入 Flutter 核心包 import 'package:http/http.dart' as http; import 'package:puzzleweave/models/api_helper.dart'; import 'package:puzzleweave/utils/utils.dart'; import 'package:logging/logging.dart'; final Logger _log = Logger('cached_request.dart'); typedef TransformFunction = Future Function(dynamic json); // 混入 WidgetsBindingObserver 以监听应用生命周期 class CachedRequest with WidgetsBindingObserver { // !!! 新增属性 1: 标记最近一次请求是否通过网络成功完成 bool _hasRecentSuccessfulFetch = false; bool get hasRecentSuccessfulFetch => _hasRecentSuccessfulFetch; static final Map _cache = {}; final String url; TransformFunction? transformFunction; // 仅使用 .broadcast(),但 onListen 只会触发一次 final StreamController _streamController = StreamController.broadcast(); dynamic _transformed; // --- 【新增】Getter:允许外部同步访问最新的缓存数据 --- dynamic get cachedData => _transformed; CachedRequest._internal(this.url, this.transformFunction) { _log.info('New cached request: $url'); // 注册生命周期监听器 WidgetsBinding.instance.addObserver(this); _init(); } factory CachedRequest.fromUrl(String url, {TransformFunction? transformFunction}) { // 确保单例模式下,只注册一次监听器 if (!_cache.containsKey(url)) { _cache[url] = CachedRequest._internal(url, transformFunction); } return _cache[url]!; } // --- 关键修改:生命周期监听 --- @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { _log.info('App Resumed from background. Forcing refresh for $url'); // 应用程序从后台恢复到前台时,强制刷新数据 refresh(); } } // 由于 CachedRequest 是一个单例,它不会被销毁,除非应用完全关闭。 // 但是,为了严谨性,如果添加了 Dispose 逻辑,应记得移除 Observer。 // 注意:在 Flutter Provider 或 InheritedWidget 依赖的单例中,通常不需要手动调用 dispose。 // 如果需要清理: /* void dispose() { WidgetsBinding.instance.removeObserver(this); _streamController.close(); // ... clean up } */ // --------------------------------- _init() { // FIX: 首次初始化时,立即尝试加载缓存(如果有) _cacheLoad(); _remoteLoad(); // _streamController.onListen = () { // if (_transformed != null) { // // 如果有缓存数据,立即发送给新的监听者 // _streamController.add(_transformed); // } // }; _streamController.onListen = () { _log.info('Stream listener added for $url. Current data available? ${_transformed != null}'); if (_transformed != null) { // 确保新订阅者立即获得缓存数据 (用于热重载或首次进入) // 使用 Future.microtask 确保在 onListen 结束后再触发 add,避免同步递归 Future.microtask(() => _streamController.add(_transformed)); } }; } Future refresh() async { await _remoteLoad(); } Future reload() async { await _cacheLoad(); } _remoteLoad() async { try { final response = await http.get(Uri.parse(url)); if (response.statusCode != 200) { // 如果状态码失败,则标记为失败,并抛出异常 _hasRecentSuccessfulFetch = false; // !!! 关键:网络失败 throw Exception('Invalid status code: ${response.statusCode} when fetching: $url'); } // !!! 关键:网络请求成功,标记为成功 _hasRecentSuccessfulFetch = true; _log.info('${response.statusCode}, $url, Network Success: true'); final data = jsonDecode(response.body); _emit(data); await saveString(cachePath, response.body); } catch (error) { _streamController.addError(error); _log.severe('Remote load failed for $url: $error'); // 即使在 catch 块中,也再次确认标记为失败(以防万一) _hasRecentSuccessfulFetch = false; } } _emit(dynamic data) async { _log.info('Emiting data..... '); try { if (transformFunction != null) { data = await transformFunction?.call(data); } // 仅当有监听者时才尝试添加数据 if (_streamController.hasListener) { _streamController.add(data); } _transformed = data; } catch (error) { _streamController.addError(error); _log.severe('Data transformation or emission failed: $error'); } } _cacheLoad() async { try { final file = await localFile(cachePath); if (await file.exists()) { _log.info('File $file exists, try loading from cache..'); final data = await loadJson(cachePath); _emit(data); } else { if (url == ApiHelper.latestUri) { _log.info('Loading builtin latest asset data...'); final data = await loadJSONFromAsset('assets/builtin/latest.json'); _emit(data); } if (url == ApiHelper.collectionUri) { _log.info('Loading builtin collection asset data...'); final data = await loadJSONFromAsset('assets/builtin/collection.json'); _emit(data); } } } catch (error) { // 缓存加载失败不应该阻止远程加载 _streamController.addError(error); } } String get hash => md5Hash(url); String get cachePath => 'api_cache/$hash'; Stream get stream => _streamController.stream; @override String toString() { return 'CachedRequest(url=$url)'; } }