app_version_provider.dart 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import 'dart:developer' as developer;
  2. import 'package:flutter_riverpod/flutter_riverpod.dart';
  3. import 'package:shared_preferences/shared_preferences.dart';
  4. import '../core/config/app_config.dart';
  5. import '../core/network/dio_client.dart';
  6. import '../data/models/version/app_version.dart';
  7. import '../data/services/version_service.dart';
  8. // SharedPreferences keys for cached version data
  9. const _kCachedLowestVersion = 'version_cached_lowest';
  10. const _kCachedNewVersion = 'version_cached_new';
  11. const _kCachedDownloadUrl = 'version_cached_download_url';
  12. const _kCachedUpdateDesc = 'version_cached_update_desc';
  13. /// 版本检查结果
  14. class VersionCheckResult {
  15. final AppVersion version;
  16. final String currentVersion;
  17. final bool hasUpdate;
  18. final bool isForce;
  19. const VersionCheckResult({
  20. required this.version,
  21. required this.currentVersion,
  22. this.hasUpdate = false,
  23. this.isForce = false,
  24. });
  25. }
  26. /// 版本检查 Provider
  27. /// 返回 [VersionCheckResult],null 表示无需更新或检查失败
  28. /// 网络失败时回退到本地缓存,防止关网绕过强制更新
  29. final appVersionProvider =
  30. FutureProvider.autoDispose<VersionCheckResult?>((ref) async {
  31. final currentVersion = AppConfig.appVersion;
  32. AppVersion? version;
  33. try {
  34. final dio = ref.read(dioClientProvider);
  35. version = await VersionService(dio).getLatestVersion();
  36. developer.log(
  37. 'Version API response: newVersion=${version?.newVersion}, '
  38. 'lowestVersion=${version?.lowestVersion}, '
  39. 'downloadUrl=${version?.downloadUrl}',
  40. name: 'VersionCheck',
  41. );
  42. if (version != null && version.newVersion.isNotEmpty) {
  43. // 成功后写入缓存,供断网时使用
  44. final prefs = await SharedPreferences.getInstance();
  45. await prefs.setString(_kCachedLowestVersion, version.lowestVersion);
  46. await prefs.setString(_kCachedNewVersion, version.newVersion);
  47. await prefs.setString(_kCachedDownloadUrl, version.downloadUrl);
  48. await prefs.setString(_kCachedUpdateDesc, version.updateDescription);
  49. }
  50. } catch (e, st) {
  51. developer.log(
  52. 'Version check failed, falling back to cache: $e',
  53. name: 'VersionCheck',
  54. error: e,
  55. stackTrace: st,
  56. );
  57. // 网络失败时读缓存
  58. try {
  59. final prefs = await SharedPreferences.getInstance();
  60. final cachedLowest = prefs.getString(_kCachedLowestVersion) ?? '';
  61. if (cachedLowest.isNotEmpty) {
  62. version = AppVersion(
  63. lowestVersion: cachedLowest,
  64. newVersion: prefs.getString(_kCachedNewVersion) ?? '',
  65. downloadUrl: prefs.getString(_kCachedDownloadUrl) ?? '',
  66. updateDescription: prefs.getString(_kCachedUpdateDesc) ?? '',
  67. );
  68. developer.log(
  69. 'Using cached version: lowestVersion=$cachedLowest',
  70. name: 'VersionCheck',
  71. );
  72. }
  73. } catch (_) {}
  74. }
  75. if (version == null || version.newVersion.isEmpty) {
  76. // 缓存也没有,只检查 lowestVersion(断网首次启动的兜底)
  77. if (version == null) return null;
  78. }
  79. final hasUpdate = version.hasNewVersion(currentVersion);
  80. final isForce = version.isForceUpdate(currentVersion);
  81. developer.log(
  82. 'Current=$currentVersion, latest=${version.newVersion}, '
  83. 'hasUpdate=$hasUpdate, isForce=$isForce',
  84. name: 'VersionCheck',
  85. );
  86. // 即使 newVersion 为空(纯缓存 lowestVersion 场景),只要需要强制更新也弹窗
  87. if (!hasUpdate && !isForce) return null;
  88. return VersionCheckResult(
  89. version: version,
  90. currentVersion: currentVersion,
  91. hasUpdate: hasUpdate || isForce,
  92. isForce: isForce,
  93. );
  94. });