CW-1186-Show-the-fiat-amount-for-UTXOs-in-coin-control (#2595)
* Add fiat amount display to unspent coins list * refactor fiat amount handling for unspent coins * Update lib/view_model/unspent_coins/unspent_coins_list_view_model.dart [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
Serhii committed
Nov 22, 2025 at 00:05 UTC
ccfe424b28a39abb4dd5f8e84789da5a3b5d98b8
4 files changed
+82
-23
lib/di.dart
+2
@@ -1428,6 +1428,8 @@ Future<void> setup({
1428
return UnspentCoinsListViewModel(
1429
wallet: wallet!,
1430
unspentCoinsInfo: _unspentCoinsInfoSource,
1431
+ fiatConversationStore: getIt.get<FiatConversionStore>(),
1432
+ settingsStore: getIt.get<SettingsStore>(),
1433
coinTypeToSpendFrom: coinTypeToSpendFrom ?? UnspentCoinType.any,
1434
);
1435
});
lib/src/screens/unspent_coins/unspent_coins_list_page.dart
+26
-22
@@ -178,28 +178,32 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
178
itemBuilder: (_, int index) {
179
final item = unspentCoinsListViewModel.items[index];
180
return Observer(
181
- builder: (_) => GestureDetector(
182
- onTap: () => Navigator.of(context).pushNamed(
183
- Routes.unspentCoinsDetails,
184
- arguments: [item, unspentCoinsListViewModel],
185
- ),
186
- child: UnspentCoinsListItem(
187
- note: item.note,
188
- amount: item.amount,
189
- address: item.address,
190
- isSending: item.isSending,
191
- isFrozen: item.isFrozen,
192
- isChange: item.isChange,
193
- isSilentPayment: item.isSilentPayment,
194
- onCheckBoxTap: item.isFrozen
195
- ? null
196
- : () async {
197
- item.isSending = !item.isSending;
198
- await unspentCoinsListViewModel
199
- .saveUnspentCoinInfo(item);
200
- },
201
- ),
202
- ),
181
+ builder: (_) {
182
+ final fiatAmount = unspentCoinsListViewModel.fiatAmounts[item.hash] ?? '';
183
+ return GestureDetector(
184
+ onTap: () => Navigator.of(context).pushNamed(
185
+ Routes.unspentCoinsDetails,
186
+ arguments: [item, unspentCoinsListViewModel],
187
+ ),
188
+ child: UnspentCoinsListItem(
189
+ note: item.note,
190
+ amount: item.amount,
191
+ fiatAmount: fiatAmount,
192
+ address: item.address,
193
+ isSending: item.isSending,
194
+ isFrozen: item.isFrozen,
195
+ isChange: item.isChange,
196
+ isSilentPayment: item.isSilentPayment,
197
+ onCheckBoxTap: item.isFrozen
198
+ ? null
199
+ : () async {
200
+ item.isSending = !item.isSending;
201
+ await unspentCoinsListViewModel
202
+ .saveUnspentCoinInfo(item);
203
+ },
204
+ ),
205
+ );
206
+ }
207
);
208
},
209
),
lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart
+18
@@ -7,6 +7,7 @@ class UnspentCoinsListItem extends StatelessWidget {
7
UnspentCoinsListItem({
8
required this.note,
9
required this.amount,
10
+ required this.fiatAmount,
11
required this.address,
12
required this.isSending,
13
required this.isFrozen,
@@ -17,6 +18,7 @@ class UnspentCoinsListItem extends StatelessWidget {
18
19
final String note;
20
final String amount;
21
+ final String fiatAmount;
22
final String address;
23
final bool isSending;
24
final bool isFrozen;
@@ -108,6 +110,22 @@ class UnspentCoinsListItem extends StatelessWidget {
110
),
111
],
112
),
113
+ if (fiatAmount.isNotEmpty)
114
+ Row(
115
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
116
+ crossAxisAlignment: CrossAxisAlignment.start,
117
+ children: [
118
+ AutoSizeText(
119
+ fiatAmount,
120
+ style: Theme.of(context).textTheme.bodyMedium!.copyWith(
121
+ color: amountColor,
122
+ fontSize: 1,
123
+ fontWeight: FontWeight.w600,
124
+ ),
125
+ maxLines: 1,
126
+ ),
127
+ ],
128
+ ),
129
Expanded(
130
child: Row(
131
crossAxisAlignment: CrossAxisAlignment.center,
lib/view_model/unspent_coins/unspent_coins_list_view_model.dart
+36
-1
@@ -1,5 +1,10 @@
1
import 'package:cake_wallet/bitcoin/bitcoin.dart';
2
+import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
3
+import 'package:cake_wallet/entities/fiat_api_mode.dart';
4
+import 'package:cake_wallet/entities/fiat_currency.dart';
5
import 'package:cake_wallet/monero/monero.dart';
6
+import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
7
+import 'package:cake_wallet/store/settings_store.dart';
8
import 'package:cake_wallet/utils/exception_handler.dart';
9
import 'package:cake_wallet/decred/decred.dart';
10
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
@@ -27,13 +32,19 @@ abstract class UnspentCoinsListViewModelBase with Store {
32
required this.wallet,
33
required Box<UnspentCoinsInfo> unspentCoinsInfo,
34
this.coinTypeToSpendFrom = UnspentCoinType.any,
35
+ required FiatConversionStore fiatConversationStore,
36
+ required SettingsStore settingsStore,
37
}) : _unspentCoinsInfo = unspentCoinsInfo,
38
+ _fiatConversationStore = fiatConversationStore,
39
+ _settingsStore = settingsStore,
40
items = ObservableList<UnspentCoinsItem>(),
41
_originalState = {};
42
43
@observable
44
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
45
final Box<UnspentCoinsInfo> _unspentCoinsInfo;
46
+ final FiatConversionStore _fiatConversationStore;
47
+ final SettingsStore _settingsStore;
48
final UnspentCoinType coinTypeToSpendFrom;
49
50
@observable
@@ -47,6 +58,30 @@ abstract class UnspentCoinsListViewModelBase with Store {
58
@computed
59
bool get isAllSelected => items.every((element) => element.isFrozen || element.isSending);
60
61
+ @computed
62
+ FiatCurrency get fiatCurrency => _settingsStore.fiatCurrency;
63
+
64
+ @computed
65
+ bool get isFiatDisabled => _settingsStore.fiatApiMode == FiatApiMode.disabled;
66
+
67
+ @computed
68
+ Map<String, String> get fiatAmounts {
69
+
70
+ final currency = wallet.currency;
71
+ final price = _fiatConversationStore.prices[currency];
72
+ if (price == null || price == 0.0 || isFiatDisabled) return {};
73
+
74
+ final result = <String, String>{};
75
+ for (final item in items) {
76
+ final formatted = formatAmountToString(item.value);
77
+ final cryptoAmount = double.tryParse(formatted.replaceAll(',', '')) ?? 0.0;
78
+ final fiatValue = price * cryptoAmount;
79
+ result[item.hash] = fiatCurrency.title + ' ' + fiatValue.toStringAsFixed(2);
80
+ }
81
+
82
+ return result;
83
+ }
84
+
85
Future<void> initialSetup() async {
86
await _updateUnspents();
87
_storeOriginalState();
@@ -176,7 +211,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
211
.map((elem) {
212
try {
213
final existingItem = _unspentCoinsInfo.values
179
- .firstWhereOrNull((item) => item.walletId == wallet.id && item == elem);
214
+ .firstWhereOrNull((item) => item.walletId == wallet.id && item == elem);;
215
216
if (existingItem == null) return null;
217