← Code knowledge base
Reviewed Flutter implementation · 2026-10-27

exchange_transfer_decision.dart

mobile_flutter/lib/features/wallets/presentation/exchange_transfer_decision.dart

Immutable source snapshot based on revision de78e63ed65c. Original line numbers are retained. This is implementation evidence; check the public API catalogue before adapting it.

The mobile /api/v1/mobile routes belong to the example backend. Company credentials stay on that backend. Some sections outside the documented product scope may be omitted.

Lines 1–124

import 'package:flutter/material.dart';import 'package:flutter_riverpod/flutter_riverpod.dart';import '../../platform/application/platform_providers.dart';import '../domain/exchange_models.dart';import 'exchange_support.dart'; Future<void> showExchangeTransferDecision(BuildContext context, WidgetRef ref,    BoomFiTransfer transfer, String decision) async {  var busy = false, accepted = false;  String? error;  await showDialog<void>(      context: context,      barrierDismissible: false,      builder: (dialog) => StatefulBuilder(builder: (context, setState) {            final expiry = DateTime.tryParse(transfer.detail('quoteExpiresAt'));            final expired = decision == 'approve' &&                (expiry == null ||                    !expiry.isAfter(                        DateTime.now().add(const Duration(seconds: 2))));            return PopScope(                canPop: !busy,                child: AlertDialog(                    title: Text(decision == 'approve'                        ? 'Review updated quote'                        : decision == 'return'                            ? 'Return funds'                            : decision == 'keep'                                ? 'Keep crypto in Exchange'                                : 'Decline quote'),                    content: SingleChildScrollView(                        child: Column(                            mainAxisSize: MainAxisSize.min,                            crossAxisAlignment: CrossAxisAlignment.start,                            children: [                          if (decision == 'approve') ...[                            Text(                                'Original rate: ${transfer.detail('initialExchangeRate')}'),                            Text(                                'Updated rate: ${transfer.detail('effectiveRate')}'),                            Text(                                'You receive ${transfer.detail('toAmount')} ${transfer.direction == 'equals_to_interlace' ? transfer.cryptoCurrency : transfer.fiatCurrency}'),                            Text(                                'Fees: ${transfer.detail('currentFeeAmount')} ${transfer.detail('currentFeeCurrency')}'),                            Text(                                'Quote expires: ${transfer.detail('quoteExpiresAt')}'),                            if (expired)                              const Text(                                  'This quote expired. Refresh it and review the new quote before approving.'),                          ],                          if (decision == 'return')                            const Text(                                'Provider and network return fees are only available after submission. The returned amount may be lower than the original amount.'),                          if (decision == 'decline')                            Text(transfer.direction == 'equals_to_interlace'                                ? 'Fiat must be returned before the 24-hour holding deadline.'                                : 'You can keep the crypto in Exchange or return it after declining.'),                          if (['approve', 'return'].contains(decision))                            ExchangeTerms(                                accepted: accepted,                                enabled: !busy,                                onChanged: (v) => setState(() => accepted = v),                                withdrawal: decision == 'return'),                          if (error != null) Text(error!),                        ])),                    actions: [                      TextButton(                          onPressed: busy ? null : () => Navigator.pop(dialog),                          child: const Text('Cancel')),                      FilledButton(                          onPressed: busy ||                                  (!expired &&                                      ['approve', 'return']                                          .contains(decision) &&                                      !accepted)                              ? null                              : () async {                                  if (decision == 'keep' &&                                      !transfer.canKeepFunds) {                                    return;                                  }                                  final expiredNow = decision == 'approve' &&                                      (expiry == null ||                                          !expiry.isAfter(DateTime.now().add(                                              const Duration(seconds: 2))));                                  if (expiredNow && !expired) {                                    setState(() => accepted = false);                                    return;                                  }                                  setState(() {                                    busy = true;                                    error = null;                                  });                                  try {                                    // Refresh is a separate operation and cannot execute a newer provider quote.                                    final result = await ref                                        .read(mobilePlatformApiProvider)                                        .decideExchangeTransfer(transfer.id,                                            expired ? 'refresh' : decision);                                    ref.invalidate(exchangeTransfersProvider);                                    ref.invalidate(exchangeOverviewProvider);                                    if (dialog.mounted) {                                      Navigator.pop(dialog);                                      ScaffoldMessenger.of(dialog).showSnackBar(                                          SnackBar(                                              content: Text(result.message)));                                    }                                  } catch (e) {                                    if (dialog.mounted) {                                      setState(() => error = e.toString());                                    }                                  } finally {                                    if (dialog.mounted) {                                      setState(() => busy = false);                                    }                                  }                                },                          child: Text(busy                              ? 'Submitting…'                              : expired                                  ? 'Refresh quote'                                  : 'Confirm')),                    ]));          }));}
HOPPA DEVELOPER ASSISTANT

What are you building?

From a question to an implementation.

Get a step-by-step flow, example code and the docs behind it. Choose a starting point or describe your feature.

Answers use the public API guide and example app code. Check the linked reference before shipping. Explore code sources ↗

Your question and recent conversation are sent to our AI provider. Don’t include API keys, personal data or card details.