currency_provider.dart 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import 'package:flutter_riverpod/flutter_riverpod.dart';
  2. import '../core/network/dio_client.dart';
  3. import '../core/utils/number_format.dart';
  4. import '../data/models/currency_rate.dart';
  5. import 'app_provider.dart';
  6. const _kCurrencyCode = 'currency_code';
  7. const _kCurrencyRate = 'currency_rate';
  8. const _kCurrencySymbol = 'currency_symbol';
  9. // ── State ─────────────────────────────────────────────────
  10. class CurrencyState {
  11. final List<CurrencyRate> rates;
  12. final String selectedCode;
  13. final bool isLoading;
  14. const CurrencyState({
  15. required this.rates,
  16. required this.selectedCode,
  17. this.isLoading = false,
  18. });
  19. CurrencyRate? get selected {
  20. if (rates.isEmpty) return null;
  21. try {
  22. return rates.firstWhere((r) => r.currency == selectedCode);
  23. } catch (_) {
  24. return rates.first;
  25. }
  26. }
  27. CurrencyState copyWith({
  28. List<CurrencyRate>? rates,
  29. String? selectedCode,
  30. bool? isLoading,
  31. }) =>
  32. CurrencyState(
  33. rates: rates ?? this.rates,
  34. selectedCode: selectedCode ?? this.selectedCode,
  35. isLoading: isLoading ?? this.isLoading,
  36. );
  37. }
  38. // ── Notifier ──────────────────────────────────────────────
  39. class CurrencyNotifier extends Notifier<CurrencyState> {
  40. @override
  41. CurrencyState build() {
  42. final prefs = ref.read(sharedPreferencesProvider);
  43. final savedCode = prefs.getString(_kCurrencyCode) ?? 'USD';
  44. final savedRate = prefs.getDouble(_kCurrencyRate) ?? 1.0;
  45. final savedSymbol = prefs.getString(_kCurrencySymbol) ?? '\$';
  46. // 立即应用上次保存的汇率,避免启动时显示错误
  47. setFiatCurrency(rate: savedRate, symbol: savedSymbol);
  48. Future.microtask(_loadRates);
  49. return CurrencyState(rates: const [], selectedCode: savedCode, isLoading: true);
  50. }
  51. Future<void> _loadRates() async {
  52. try {
  53. final dio = ref.read(dioClientProvider);
  54. final response = await dio.get<Map<String, dynamic>>('contract/currency/rate');
  55. final list = (response.data?['data'] as List<dynamic>? ?? [])
  56. .map((e) => CurrencyRate.fromJson(e as Map<String, dynamic>))
  57. .toList();
  58. state = state.copyWith(rates: list, isLoading: false);
  59. // 用最新汇率更新全局法币设置
  60. final current = state.selected;
  61. if (current != null) {
  62. _applyAndPersist(current);
  63. }
  64. } catch (_) {
  65. // 接口失败时继续使用上次保存的汇率
  66. state = state.copyWith(isLoading: false);
  67. }
  68. }
  69. void selectCurrency(CurrencyRate rate) {
  70. state = state.copyWith(selectedCode: rate.currency);
  71. _applyAndPersist(rate);
  72. }
  73. void _applyAndPersist(CurrencyRate rate) {
  74. setFiatCurrency(rate: rate.rate, symbol: rate.symbol);
  75. final prefs = ref.read(sharedPreferencesProvider);
  76. prefs.setString(_kCurrencyCode, rate.currency);
  77. prefs.setDouble(_kCurrencyRate, rate.rate);
  78. prefs.setString(_kCurrencySymbol, rate.symbol);
  79. }
  80. }
  81. // ── Provider ──────────────────────────────────────────────
  82. final currencyProvider = NotifierProvider<CurrencyNotifier, CurrencyState>(
  83. CurrencyNotifier.new,
  84. );