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>
This commit is contained in:
Serhii
2025-11-21 23:05:15 +01:00
committed by GitHub
co-authored by Omar Hatem
parent c552ab86d2
commit ccfe424b28
4 changed files with 82 additions and 23 deletions
+2
View File
@@ -1428,6 +1428,8 @@ Future<void> setup({
return UnspentCoinsListViewModel(
wallet: wallet!,
unspentCoinsInfo: _unspentCoinsInfoSource,
fiatConversationStore: getIt.get<FiatConversionStore>(),
settingsStore: getIt.get<SettingsStore>(),
coinTypeToSpendFrom: coinTypeToSpendFrom ?? UnspentCoinType.any,
);
});
@@ -178,28 +178,32 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
itemBuilder: (_, int index) {
final item = unspentCoinsListViewModel.items[index];
return Observer(
builder: (_) => GestureDetector(
onTap: () => Navigator.of(context).pushNamed(
Routes.unspentCoinsDetails,
arguments: [item, unspentCoinsListViewModel],
),
child: UnspentCoinsListItem(
note: item.note,
amount: item.amount,
address: item.address,
isSending: item.isSending,
isFrozen: item.isFrozen,
isChange: item.isChange,
isSilentPayment: item.isSilentPayment,
onCheckBoxTap: item.isFrozen
? null
: () async {
item.isSending = !item.isSending;
await unspentCoinsListViewModel
.saveUnspentCoinInfo(item);
},
),
),
builder: (_) {
final fiatAmount = unspentCoinsListViewModel.fiatAmounts[item.hash] ?? '';
return GestureDetector(
onTap: () => Navigator.of(context).pushNamed(
Routes.unspentCoinsDetails,
arguments: [item, unspentCoinsListViewModel],
),
child: UnspentCoinsListItem(
note: item.note,
amount: item.amount,
fiatAmount: fiatAmount,
address: item.address,
isSending: item.isSending,
isFrozen: item.isFrozen,
isChange: item.isChange,
isSilentPayment: item.isSilentPayment,
onCheckBoxTap: item.isFrozen
? null
: () async {
item.isSending = !item.isSending;
await unspentCoinsListViewModel
.saveUnspentCoinInfo(item);
},
),
);
}
);
},
),
@@ -7,6 +7,7 @@ class UnspentCoinsListItem extends StatelessWidget {
UnspentCoinsListItem({
required this.note,
required this.amount,
required this.fiatAmount,
required this.address,
required this.isSending,
required this.isFrozen,
@@ -17,6 +18,7 @@ class UnspentCoinsListItem extends StatelessWidget {
final String note;
final String amount;
final String fiatAmount;
final String address;
final bool isSending;
final bool isFrozen;
@@ -108,6 +110,22 @@ class UnspentCoinsListItem extends StatelessWidget {
),
],
),
if (fiatAmount.isNotEmpty)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AutoSizeText(
fiatAmount,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: amountColor,
fontSize: 1,
fontWeight: FontWeight.w600,
),
maxLines: 1,
),
],
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
@@ -1,5 +1,10 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/utils/exception_handler.dart';
import 'package:cake_wallet/decred/decred.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
@@ -27,13 +32,19 @@ abstract class UnspentCoinsListViewModelBase with Store {
required this.wallet,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
this.coinTypeToSpendFrom = UnspentCoinType.any,
required FiatConversionStore fiatConversationStore,
required SettingsStore settingsStore,
}) : _unspentCoinsInfo = unspentCoinsInfo,
_fiatConversationStore = fiatConversationStore,
_settingsStore = settingsStore,
items = ObservableList<UnspentCoinsItem>(),
_originalState = {};
@observable
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
final Box<UnspentCoinsInfo> _unspentCoinsInfo;
final FiatConversionStore _fiatConversationStore;
final SettingsStore _settingsStore;
final UnspentCoinType coinTypeToSpendFrom;
@observable
@@ -47,6 +58,30 @@ abstract class UnspentCoinsListViewModelBase with Store {
@computed
bool get isAllSelected => items.every((element) => element.isFrozen || element.isSending);
@computed
FiatCurrency get fiatCurrency => _settingsStore.fiatCurrency;
@computed
bool get isFiatDisabled => _settingsStore.fiatApiMode == FiatApiMode.disabled;
@computed
Map<String, String> get fiatAmounts {
final currency = wallet.currency;
final price = _fiatConversationStore.prices[currency];
if (price == null || price == 0.0 || isFiatDisabled) return {};
final result = <String, String>{};
for (final item in items) {
final formatted = formatAmountToString(item.value);
final cryptoAmount = double.tryParse(formatted.replaceAll(',', '')) ?? 0.0;
final fiatValue = price * cryptoAmount;
result[item.hash] = fiatCurrency.title + ' ' + fiatValue.toStringAsFixed(2);
}
return result;
}
Future<void> initialSetup() async {
await _updateUnspents();
_storeOriginalState();
@@ -176,7 +211,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
.map((elem) {
try {
final existingItem = _unspentCoinsInfo.values
.firstWhereOrNull((item) => item.walletId == wallet.id && item == elem);
.firstWhereOrNull((item) => item.walletId == wallet.id && item == elem);;
if (existingItem == null) return null;