What changed, and why it matters
This commit is a routine code cleanup and bug-fix pass for the buy/sell feature in Cake Wallet. It removes an unused money-parsing helper, fixes memory leaks by disposing controllers and focus nodes, improves localization, and tweaks buy/sell amount calculations. There is no clear security vulnerability introduced or fixed in the diff itself.
No immediate security action required. Treat as a normal refactor/bug-fix commit. If reviewing further, verify that the removal of fiat tag matching in provider selection does not allow incorrect currency pairs, and that setCryptoAmountFromFiat handles division-by-zero when fiatConversionStore.prices[cryptoCurrency] is 0 or null.
Security signals we found
Removed unused Money.safeParse factory (dead code cleanup, not security-relevant)
Added dispose() calls for TextEditingController and FocusNode (resource leak fix)
Added try/catch around _getAvailablePaymentTypes to prevent unhandled exceptions
Replaced hardcoded English strings with localized strings
Changed provider pair matching logic to drop fiat tag equality check
Changed default BTC sell amount fallback from 0.001 to 0.01
Evidence from the diff
The commit ‘address review comments’ refactors the new buy/sell UI flow. Key changes: removal of the unused Money.safeParse factory; addition of dispose() methods for TextEditingController/FocusNode in buy_sell_amount_page.dart and floating_amount_input.dart; hardcoded ‘LOW KYC’ string replaced with localized S.current.low_kyc; sell-mode amount handling now uses setCryptoAmountFromFiat; default crypto fallback for BTC changed from 0.001 to 0.01; provider matching no longer requires fiat tag equality; error messages localized; try/catch added around payment-method loading; and various formatting/style changes. No explicit security fix or vulnerability is described.
Changed components
cw_core/lib/amount/money.dartlib/buy/buy_quote.dartlib/new-ui/pages/buy_sell/buy_sell_amount_page.dartlib/new-ui/pages/buy_sell/buy_sell_confirmation_page.dartlib/new-ui/pages/buy_sell/buy_sell_payment_method_page.dartlib/new-ui/pages/buy_sell/buy_sell_provider_page.dartlib/new-ui/pages/buy_sell/buy_sell_redirecting_page.dartlib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dartlib/new-ui/widgets/floating_amount_input.dartlib/router.dartlib/view_model/buy/buy_sell_view_model.dartres/values/strings_en.arbInspect captured patch +978 / −862
diff --git a/cw_core/lib/amount/money.dart b/cw_core/lib/amount/money.dart
index 8ec0793f..0e4bc0de 100644
--- a/cw_core/lib/amount/money.dart
+++ b/cw_core/lib/amount/money.dart
@@ -25,14 +25,6 @@ class Money implements Comparable<Money> {
return Money(amount, currency);
}
- factory Money.safeParse(source, Currency currency, {bool isBaseUnit = false}) {
- final amount = isBaseUnit
- ? BigInt.parse(source.toString())
- : parseFixed(source.toString().withDecimals(currency.decimals), currency.decimals);
-
- return Money(amount, currency);
- }
-
/// Parse the [source] and turn it into [Money] if possible
///
/// As [parse] except that this method returns `null` if the input is not
diff --git a/lib/buy/buy_quote.dart b/lib/buy/buy_quote.dart
index ee301e82..370e57a8 100644
--- a/lib/buy/buy_quote.dart
+++ b/lib/buy/buy_quote.dart
@@ -16,7 +16,7 @@ extension RecommendationTitle on ProviderRecommendation {
case ProviderRecommendation.bestRate:
return S.current.best_rate;
case ProviderRecommendation.lowKyc:
- return 'LOW KYC';
+ return S.current.low_kyc;
case ProviderRecommendation.successRate:
return S.current.highest_success_rate;
}
diff --git a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart
index 8012f721..c7031c8d 100644
--- a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart
+++ b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart
@@ -5,7 +5,6 @@ import "package:cake_wallet/buy/sell_buy_states.dart";
import "package:cake_wallet/entities/fiat_currency.dart";
import "package:cake_wallet/generated/i18n.dart";
import "package:cake_wallet/new-ui/pages/buy_sell/buy_sell_provider_page.dart";
-import "package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart";
import "package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart";
import "package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_sheet.dart";
import "package:cake_wallet/new-ui/widgets/currency_picker/fiat_currency_picker_sheet.dart";
@@ -43,6 +42,13 @@ class _NewBuySellAmountPageState extends State<NewBuySellAmountPage> {
final customInputController = TextEditingController();
final customInputFocusNode = FocusNode();
+ @override
+ void dispose() {
+ customInputController.dispose();
+ customInputFocusNode.dispose();
+ super.dispose();
+ }
+
@override
void initState() {
super.initState();
@@ -50,107 +56,120 @@ class _NewBuySellAmountPageState extends State<NewBuySellAmountPage> {
// this is a hack for the "up to" display to show the actual upper limit.
// limits are loaded alongside quotes, and quotes for some providers only load properly with an amount and payment method selected.
// it'll be refactored soon anyway, i guess.
- widget.buySellViewModel.changeFiatAmount(amount: "1000");
+ widget.buySellViewModel.changeFiatAmount(amount: widget.buySellViewModel.defaultAmounts[3]);
widget.buySellViewModel.calculateBestRate();
when(
- (_) => widget.buySellViewModel.paymentMethodState is PaymentMethodLoaded,
- () => widget.buySellViewModel.selectedPaymentMethod = widget.buySellViewModel.paymentMethods
- .firstWhere((item) => item.paymentMethodType == PaymentType.all),);
+ (_) => widget.buySellViewModel.paymentMethodState is PaymentMethodLoaded,
+ () => widget.buySellViewModel.selectedPaymentMethod = widget.buySellViewModel.paymentMethods
+ .firstWhere((item) => item.paymentMethodType == PaymentType.all),
+ );
}
@override
Widget build(BuildContext context) => Container(
- decoration: BoxDecoration(
- borderRadius: const BorderRadius.vertical(top: Radius.circular(18)),
- gradient: LinearGradient(
- colors: [
- Theme.of(context).colorScheme.surface,
- Theme.of(context).colorScheme.surfaceDim,
- ],
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- ),
- ),
- child: SafeArea(
- child: Column(
- children: [
- Observer(
- builder: (_) => ModalTopBar(
- title: _pageTitle,
- bottomText: !_customAmountMode || widget.buySellViewModel.maxFiatAmount == null
- ? null
- : "${S.of(context).up_to} ~${widget.buySellViewModel.maxFiatAmount} ${widget.buySellViewModel.fiatCurrency.title}",
- leadingIcon: const Icon(Icons.close),
- leadingSemanticLabel: S.of(context).close,
- onLeadingPressed: Navigator.of(context, rootNavigator: true).pop,
- ),
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.vertical(top: Radius.circular(18)),
+ gradient: LinearGradient(
+ colors: [
+ Theme.of(context).colorScheme.surface,
+ Theme.of(context).colorScheme.surfaceDim,
+ ],
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
),
- Expanded(
- child: Observer(
- builder: (_) => AnimatedSwitcher(
- duration: const Duration(milliseconds: 300),
- child: _customAmountMode
- ? BuySellCustomAmountInput(
- fiatCurrency: widget.buySellViewModel.fiatCurrency,
- cryptoCurrency: widget.buySellViewModel.cryptoCurrency,
- cryptoAmount: widget.buySellViewModel.cryptoAmount,
- isLoading: _isLoadingPaymentMethods,
- hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies,
- onCurrencySelectorPressed: () => selectCryptoCurrency(context),
- controller: customInputController,
- focusNode: customInputFocusNode,
- onContinuePressed: () {
- navigateToProviders(context);
- },
- onChanged: (amount) =>
- widget.buySellViewModel.changeFiatAmount(amount: amount),
- )
- : BuySellDefaultAmountSelector(
- key: const ValueKey(0),
- defaultAmounts: widget.buySellViewModel.defaultAmounts,
- fiatCurrency: widget.buySellViewModel.fiatCurrency,
- currentAmount: widget.buySellViewModel.fiatAmount,
- hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies,
- onCurrencySelectorPressed: () => selectCryptoCurrency(context),
- onFiatSelectorPressed: () => selectFiatCurrency(context),
+ ),
+ child: SafeArea(
+ child: Column(
+ children: [
+ Observer(
+ builder: (_) => ModalTopBar(
+ title: _pageTitle,
+ bottomText: !_customAmountMode || widget.buySellViewModel.maxFiatAmount == null
+ ? null
+ : "${S.of(context).up_to} ~${widget.buySellViewModel.maxFiatAmount} ${widget.buySellViewModel.fiatCurrency.title}",
+ leadingIcon: const Icon(Icons.close),
+ leadingSemanticLabel: S.of(context).close,
+ onLeadingPressed: Navigator.of(context, rootNavigator: true).pop,
+ ),
+ ),
+ Expanded(
+ child: Observer(
+ builder: (_) => AnimatedSwitcher(
+ duration: const Duration(milliseconds: 300),
+ child: _customAmountMode
+ ? BuySellCustomAmountInput(
+ fiatCurrency: widget.buySellViewModel.fiatCurrency,
cryptoCurrency: widget.buySellViewModel.cryptoCurrency,
- isLoading: _isLoadingPaymentMethods,
- mode: widget.buySellViewModel.mode,
- onSelected: (amount) async {
- if (amount == null) {
- // this resets the rate and prevents showing 0 usd = 0.something btc
- await widget.buySellViewModel.changeFiatAmount(amount: "");
- setState(() {
- _customAmountMode = true;
- });
- customInputFocusNode.requestFocus();
- } else {
- await widget.buySellViewModel.changeFiatAmount(amount: amount);
- if(context.mounted) {
- await navigateToProviders(context);
- }
- }
- },
- ),
- ),
- ),),
- ],
- ),
- ),
- );
+ cryptoAmount: widget.buySellViewModel.cryptoAmount,
+ isLoading: _isLoadingPaymentMethods,
+ hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies,
+ onCurrencySelectorPressed: () => selectCryptoCurrency(context),
+ controller: customInputController,
+ focusNode: customInputFocusNode,
+ onContinuePressed: () {
+ navigateToProviders(context);
+ },
+ onChanged: (amount) {
+ if (widget.buySellViewModel.mode == BuySellPageMode.sell) {
+ widget.buySellViewModel.setCryptoAmountFromFiat(amount);
+ } else {
+ widget.buySellViewModel.changeFiatAmount(amount: amount);
+ }
+ },
+ )
+ : BuySellDefaultAmountSelector(
+ key: const ValueKey(0),
+ defaultAmounts: widget.buySellViewModel.defaultAmounts,
+ fiatCurrency: widget.buySellViewModel.fiatCurrency,
+ currentAmount: widget.buySellViewModel.fiatAmount,
+ hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies,
+ onCurrencySelectorPressed: () => selectCryptoCurrency(context),
+ onFiatSelectorPressed: () => selectFiatCurrency(context),
+ cryptoCurrency: widget.buySellViewModel.cryptoCurrency,
+ isLoading: _isLoadingPaymentMethods,
+ mode: widget.buySellViewModel.mode,
+ onSelected: (amount) async {
+ if (amount == null) {
+ // this resets the rate and prevents showing 0 usd = 0.something btc
+ await widget.buySellViewModel.changeFiatAmount(amount: "");
+ setState(() {
+ _customAmountMode = true;
+ });
+ customInputFocusNode.requestFocus();
+ } else {
+ if (widget.buySellViewModel.mode == BuySellPageMode.sell) {
+ await widget.buySellViewModel.setCryptoAmountFromFiat(amount);
+ } else {
+ await widget.buySellViewModel.changeFiatAmount(amount: amount);
+ }
+ if (context.mounted) {
+ await navigateToProviders(context);
+ }
+ }
+ },
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
void selectCryptoCurrency(BuildContext context) => CurrencyPickerSheet.show(
- context: context,
- args: CurrencyPickerArgs(
- items: widget.buySellViewModel.activeWalletCurrencies.toList(),
- onSelected: (item) => widget.buySellViewModel.changeCryptoCurrency(currency: item),
- symbolResolver: widget.buySellViewModel.amountParsingProxy.getCryptoSymbol,
- ),);
+ context: context,
+ args: CurrencyPickerArgs(
+ items: widget.buySellViewModel.activeWalletCurrencies.toList(),
+ onSelected: (item) => widget.buySellViewModel.changeCryptoCurrency(currency: item),
+ symbolResolver: widget.buySellViewModel.amountParsingProxy.getCryptoSymbol,
+ ),
+ );
void selectFiatCurrency(BuildContext context) => FiatCurrencyPickerSheet.show(
- context: context,
- selected: widget.buySellViewModel.fiatCurrency,
- onSelected: (item) => widget.buySellViewModel.changeFiatCurrency(currency: item));
+ context: context,
+ selected: widget.buySellViewModel.fiatCurrency,
+ onSelected: (item) => widget.buySellViewModel.changeFiatCurrency(currency: item),
+ );
String get _pageTitle => widget.buySellViewModel.mode == BuySellPageMode.buy
? S.current.buy
@@ -169,18 +188,22 @@ class _NewBuySellAmountPageState extends State<NewBuySellAmountPage> {
_isLoadingPaymentMethods = true;
});
- await asyncWhen((_) => [PaymentMethodLoaded, PaymentMethodFailed]
- .contains(widget.buySellViewModel.paymentMethodState.runtimeType),);
+ await asyncWhen(
+ (_) => [PaymentMethodLoaded, PaymentMethodFailed]
+ .contains(widget.buySellViewModel.paymentMethodState.runtimeType),
+ );
if (widget.buySellViewModel.paymentMethodState is PaymentMethodFailed) {
- if(context.mounted) {
+ if (context.mounted) {
await showPopUp(
context: context,
builder: (context) => AlertWithOneAction(
alertTitle: S.of(context).failed_to_load_payment_methods,
alertContent: S.of(context).please_try_again_later,
buttonText: "OK",
- buttonAction: Navigator.of(context).pop,),);
+ buttonAction: Navigator.of(context).pop,
+ ),
+ );
}
return;
}
@@ -192,11 +215,15 @@ class _NewBuySellAmountPageState extends State<NewBuySellAmountPage> {
unawaited(widget.buySellViewModel.calculateBestRate());
final page = BuySellProviderPage(buySellViewModel: widget.buySellViewModel);
- if(context.mounted) {
- unawaited(Navigator.of(context).push(CupertinoPageRoute(
- builder: (context) => Material(color: Colors.transparent, child: page),),));
+ if (context.mounted) {
+ unawaited(
+ Navigator.of(context).push(
+ CupertinoPageRoute(
+ builder: (context) => Material(color: Colors.transparent, child: page),
+ ),
+ ),
+ );
}
-
} finally {
setState(() {
_isLoadingPaymentMethods = false;
@@ -233,50 +260,62 @@ class BuySellCustomAmountInput extends StatelessWidget {
@override
Widget build(BuildContext context) => Padding(
- padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- const SizedBox.shrink(),
- Column(
- spacing: 8,
- children: [
- FloatingAmountInput(
- currency: fiatCurrency,
- focusNode: focusNode,
- controller: controller,
- onChanged: onChanged,
- ),
- Opacity(
- opacity: cryptoAmount.isEmpty ? 0 : 1,
- child: Text(
- "≈ ${cryptoAmount} ${cryptoCurrency.symbol}",
- style: TextStyle(
+ padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ const SizedBox.shrink(),
+ Column(
+ spacing: 8,
+ children: [
+ FloatingAmountInput(
+ currency: fiatCurrency,
+ focusNode: focusNode,
+ controller: controller,
+ onChanged: onChanged,
+ ),
+ Opacity(
+ opacity: cryptoAmount.isEmpty ? 0 : 1,
+ child: Text(
+ "≈ ${cryptoAmount} ${cryptoCurrency.symbol}",
+ style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
- color: Theme.of(context).colorScheme.onSurfaceVariant,),
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ ),
),
- ),
- ],
- ),
- Padding(
- padding: const EdgeInsets.all(18),
- child: NewPrimaryButton(
+ ],
+ ),
+ Padding(
+ padding: const EdgeInsets.all(18),
+ child: NewPrimaryButton(
onPressed: onContinuePressed,
isLoading: isLoading,
text: S.of(context).continue_text,
color: Theme.of(context).colorScheme.primary,
- textColor: Theme.of(context).colorScheme.onPrimary,),
- ),
- ],
- ),
- );
+ textColor: Theme.of(context).colorScheme.onPrimary,
+ ),
+ ),
+ ],
+ ),
+ );
}
class BuySellDefaultAmountSelector extends StatelessWidget {
- const BuySellDefaultAmountSelector(
- {required this.onFiatSelectorPressed, required this.defaultAmounts, required this.fiatCurrency, required this.mode, required this.onSelected, required this.isLoading, required this.hasCurrencySelector, required this.onCurrencySelectorPressed, required this.cryptoCurrency, super.key,
- this.currentAmount,});
+ const BuySellDefaultAmountSelector({
+ required this.onFiatSelectorPressed,
+ required this.defaultAmounts,
+ required this.fiatCurrency,
+ required this.mode,
+ required this.onSelected,
+ required this.isLoading,
+ required this.hasCurrencySelector,
+ required this.onCurrencySelectorPressed,
+ required this.cryptoCurrency,
+ super.key,
+ this.currentAmount,
+ });
final List<String> defaultAmounts;
final String? currentAmount;
@@ -291,37 +330,42 @@ class BuySellDefaultAmountSelector extends StatelessWidget {
@override
Widget build(BuildContext context) => Padding(
- padding: const EdgeInsets.symmetric(horizontal: 18),
- child: Column(
- spacing: 24,
- mainAxisAlignment: MainAxisAlignment.start,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- if (hasCurrencySelector)
- BuySellCurrencyPickerPill(title: mode == BuySellPageMode.sell ? S
- .of(context)
- .sell : S
- .of(context)
- .buy, curr: cryptoCurrency, onTap: onCurrencySelectorPressed),
- BuySellCurrencyPickerPill(title: mode == BuySellPageMode.sell ? S
- .of(context)
- .receive : S
- .of(context)
- .purchase_with, curr: fiatCurrency, onTap: onFiatSelectorPressed),
-
- Text(
- mode == BuySellPageMode.sell
- ? S.of(context).choose_amount_to_sell
- : S.of(context).choose_amount_to_buy,
- style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
- ),
- GridView.builder(
+ padding: const EdgeInsets.symmetric(horizontal: 18),
+ child: Column(
+ spacing: 24,
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ if (hasCurrencySelector)
+ BuySellCurrencyPickerPill(
+ title: mode == BuySellPageMode.sell ? S.of(context).sell : S.of(context).buy,
+ curr: cryptoCurrency,
+ onTap: onCurrencySelectorPressed,
+ ),
+ BuySellCurrencyPickerPill(
+ title: mode == BuySellPageMode.sell
+ ? S.of(context).receive
+ : S.of(context).purchase_with,
+ curr: fiatCurrency,
+ onTap: onFiatSelectorPressed,
+ ),
+ Text(
+ mode == BuySellPageMode.sell
+ ? S.of(context).choose_amount_to_sell
+ : S.of(context).choose_amount_to_buy,
+ style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
+ ),
+ GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
// +1 for "custom" option
itemCount: defaultAmounts.length + 1,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
- crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 16, mainAxisExtent: 105,),
+ crossAxisCount: 2,
+ crossAxisSpacing: 8,
+ mainAxisSpacing: 16,
+ mainAxisExtent: 105,
+ ),
itemBuilder: (context, index) {
final String? item = index == defaultAmounts.length ? null : defaultAmounts[index];
@@ -330,10 +374,11 @@ class BuySellDefaultAmountSelector extends StatelessWidget {
amount: item == null ? null : Money.parse(item, fiatCurrency),
onTap: () => onSelected(item),
);
- },),
- ],
- ),
- );
+ },
+ ),
+ ],
+ ),
+ );
}
class BuySellAmountPill extends StatelessWidget {
@@ -345,68 +390,72 @@ class BuySellAmountPill extends StatelessWidget {
@override
Widget build(BuildContext context) => Container(
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(9999999999),
- border: Border.all(
- width: 1,
- color: Theme.of(context).colorScheme.surfaceContainerHigh,
- ),
- gradient: LinearGradient(
- colors: [
- context.customColors.cardGradientColorPrimary,
- context.customColors.cardGradientColorSecondary,
- ],
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(9999999999),
+ border: Border.all(
+ width: 1,
+ color: Theme.of(context).colorScheme.surfaceContainerHigh,
+ ),
+ gradient: LinearGradient(
+ colors: [
+ context.customColors.cardGradientColorPrimary,
+ context.customColors.cardGradientColorSecondary,
+ ],
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ ),
),
- ),
- child: Material(
- color: Colors.transparent,
- borderRadius: BorderRadius.circular(9999999999),
- child: InkWell(
+ child: Material(
+ color: Colors.transparent,
borderRadius: BorderRadius.circular(9999999999),
- onTap: onTap,
- child: isLoading
- ? const CupertinoActivityIndicator()
- : Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Row(
- spacing: 4,
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- if (amount != null)
- MoneyText(
- amount!,
- style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
- showSymbol: false,
- ),
- Text(
- amount?.currency.symbol ?? S.of(context).custom,
- style: TextStyle(
+ child: InkWell(
+ borderRadius: BorderRadius.circular(9999999999),
+ onTap: onTap,
+ child: isLoading
+ ? const CupertinoActivityIndicator()
+ : Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Row(
+ spacing: 4,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ if (amount != null)
+ MoneyText(
+ amount!,
+ style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
+ showSymbol: false,
+ ),
+ Text(
+ amount?.currency.symbol ?? S.of(context).custom,
+ style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: amount == null
? Theme.of(context).colorScheme.primary
- : Theme.of(context).colorScheme.onSurfaceVariant,),
- ),
- ],
- ),
- if (amount == null)
- Text(
- S.of(context).enter_amount,
- style: TextStyle(
- fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant,),
+ : Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ ),
+ ],
),
- ],
- ),
+ if (amount == null)
+ Text(
+ S.of(context).enter_amount,
+ style: TextStyle(
+ fontSize: 12,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ ),
+ ],
+ ),
+ ),
),
- ),
- );
+ );
}
class BuySellCurrencyPickerPill extends StatelessWidget {
- const BuySellCurrencyPickerPill({required this.title, required this.curr, required this.onTap, super.key});
+ const BuySellCurrencyPickerPill(
+ {required this.title, required this.curr, required this.onTap, super.key});
final String title;
final Currency curr;
@@ -414,65 +463,74 @@ class BuySellCurrencyPickerPill extends StatelessWidget {
@override
Widget build(BuildContext context) => Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- mainAxisSize: MainAxisSize.min,
- spacing: 12,
- children: [
- Text(title, style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),),
- GestureDetector(
- onTap: onTap,
- child: Container(
- width: MediaQuery.sizeOf(context).width,
- height: 62,
- decoration: BoxDecoration(
- color: Theme.of(context).colorScheme.surfaceContainer,
- borderRadius: BorderRadius.circular(18),
- ),
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 12),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
-
- children: [
- Row(spacing:12, children: [
- if(curr is CryptoCurrency)
- TokenChainDisplay(size: 30, asset: curr as CryptoCurrency)
- else if(curr is FiatCurrency)
- SizedBox(width: 30, child: Text((curr as FiatCurrency).emoji, style: const TextStyle(fontSize: 24),)),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- mainAxisAlignment: MainAxisAlignment.center,
- spacing: 2,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ spacing: 12,
+ children: [
+ Text(
+ title,
+ style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
+ ),
+ GestureDetector(
+ onTap: onTap,
+ child: Container(
+ width: MediaQuery.sizeOf(context).width,
+ height: 62,
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.surfaceContainer,
+ borderRadius: BorderRadius.circular(18),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 12),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ spacing: 12,
children: [
- Text(
- curr.fullName ?? curr.symbol,
+ if (curr is CryptoCurrency)
+ TokenChainDisplay(size: 30, asset: curr as CryptoCurrency)
+ else if (curr is FiatCurrency)
+ SizedBox(
+ width: 30,
+ child: Text(
+ (curr as FiatCurrency).emoji,
+ style: const TextStyle(fontSize: 24),
+ )),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.center,
+ spacing: 2,
+ children: [
+ Text(
+ curr.fullName ?? curr.symbol,
+ ),
+ CurrencySymbolText(
+ curr,
+ style: TextStyle(
+ fontSize: 12,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ ),
+ ],
),
- CurrencySymbolText(
- curr,
- style: TextStyle(
- fontSize: 12,
- color: Theme.of(context).colorScheme.onSurfaceVariant,
- ),
- )
],
),
- ],),
-
- RotatedBox(
- quarterTurns: 2,
- child: CakeImageWidget(
- imageUrl: "assets/new-ui/dropdown_arrow.svg",
- width: 8,
- height: 8,
- colorFilter:
- ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn),
+ RotatedBox(
+ quarterTurns: 2,
+ child: CakeImageWidget(
+ imageUrl: "assets/new-ui/dropdown_arrow.svg",
+ width: 8,
+ height: 8,
+ colorFilter: ColorFilter.mode(
+ Theme.of(context).colorScheme.primary, BlendMode.srcIn),
+ ),
),
- ),
- ],
+ ],
+ ),
),
),
),
- ),
- ],
- );
+ ],
+ );
}
diff --git a/lib/new-ui/pages/buy_sell/buy_sell_confirmation_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_confirmation_page.dart
index 7d6c3c7d..3b95faea 100644
--- a/lib/new-ui/pages/buy_sell/buy_sell_confirmation_page.dart
+++ b/lib/new-ui/pages/buy_sell/buy_sell_confirmation_page.dart
@@ -1,134 +1,144 @@
-import 'package:cake_wallet/buy/payment_method.dart';
-import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart';
-import 'package:cake_wallet/generated/i18n.dart';
-import 'package:cake_wallet/new-ui/pages/buy_sell/buy_sell_redirecting_page.dart';
-import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart';
-import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
-import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
-import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
-import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart';
-import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart';
-import 'package:flutter/cupertino.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_mobx/flutter_mobx.dart';
+import "package:cake_wallet/buy/payment_method.dart";
+import "package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart";
+import "package:cake_wallet/generated/i18n.dart";
+import "package:cake_wallet/new-ui/pages/buy_sell/buy_sell_redirecting_page.dart";
+import "package:cake_wallet/new-ui/widgets/new_primary_button.dart";
+import "package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart";
+import "package:cake_wallet/src/widgets/cake_image_widget.dart";
+import "package:cake_wallet/src/widgets/new_list_row/new_list_section.dart";
+import "package:cake_wallet/view_model/buy/buy_sell_view_model.dart";
+import "package:flutter/cupertino.dart";
+import "package:flutter/material.dart";
+import "package:flutter_mobx/flutter_mobx.dart";
class BuySellConfirmationPage extends StatelessWidget {
- const BuySellConfirmationPage({super.key, required this.buySellViewModel});
+ const BuySellConfirmationPage({required this.buySellViewModel, super.key});
final BuySellViewModel buySellViewModel;
@override
- Widget build(BuildContext context) {
- return Container(
- decoration: BoxDecoration(
- borderRadius: BorderRadius.vertical(top: Radius.circular(18)),
- gradient: LinearGradient(
- colors: [
- Theme.of(context).colorScheme.surface,
- Theme.of(context).colorScheme.surfaceDim,
- ],
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
+ Widget build(BuildContext context) => Container(
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.vertical(top: Radius.circular(18)),
+ gradient: LinearGradient(
+ colors: [
+ Theme.of(context).colorScheme.surface,
+ Theme.of(context).colorScheme.surfaceDim,
+ ],
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ ),
),
- ),
- child: SafeArea(
- child: Column(
- children: [
- ModalTopBar(
- title: _pageTitle,
- leadingIcon: Icon(Icons.arrow_back_ios_new),
- onLeadingPressed: Navigator.of(context).pop,
- ),
- Expanded(child: Observer(
- builder: (_) {
- return Column(
- spacing: 24,
- children: [
- Column(
- spacing: 4,
- children: [
- Text(
- "${buySellViewModel.fiatAmount} ${buySellViewModel.fiatCurrency}",
- style: TextStyle(fontSize: 32),
- ),
- Text(
- "≈ ${buySellViewModel.amountForQuote(buySellViewModel.selectedQuote!)?.toStringWithSymbol(fractionalDigits: 8)}",
- style: TextStyle(
- color: Theme.of(context).colorScheme.onSurfaceVariant,
- fontWeight: FontWeight.w500),
- )
- ],
- ),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 18.0),
- child: NewListSections(sections: {
- "": [
- ListItemRegularRow(
- keyValue: "provider",
- label: S.of(context).provider,
- trailingWidget: Row(
- spacing: 8,
- children: [
- CakeImageWidget(
- imageUrl: Theme.of(context).brightness == Brightness.light
- ? buySellViewModel.selectedQuote!.lightIconPath
- : buySellViewModel.selectedQuote!.darkIconPath,
- width: 24,
- height: 24,
- ),
- Text(
- buySellViewModel.selectedQuote!.rampName ??
- buySellViewModel.selectedQuote!.provider.title,
- style: TextStyle(
+ child: SafeArea(
+ child: Column(
+ children: [
+ ModalTopBar(
+ title: _pageTitle,
+ leadingIcon: const Icon(Icons.arrow_back_ios_new),
+ onLeadingPressed: Navigator.of(context).pop,
+ leadingSemanticLabel: S.of(context).seed_alert_back,
+ ),
+ Expanded(
+ child: Observer(
+ builder: (_) => Column(
+ spacing: 24,
+ children: [
+ Column(
+ spacing: 4,
+ children: [
+ Text(
+ "${buySellViewModel.fiatAmount} ${buySellViewModel.fiatCurrency}",
+ style: const TextStyle(fontSize: 32),
+ ),
+ if (buySellViewModel.amountForQuote(buySellViewModel.selectedQuote!) !=
+ null)
+ Text(
+ "≈ ${buySellViewModel.amountForQuote(buySellViewModel.selectedQuote!)?.toStringWithSymbol(fractionalDigits: 8)}",
+ style: TextStyle(
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ],
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 18),
+ child: NewListSections(
+ sections: {
+ "": [
+ ListItemRegularRow(
+ keyValue: "provider",
+ label: S.of(context).provider,
+ trailingWidget: Row(
+ spacing: 8,
+ children: [
+ CakeImageWidget(
+ imageUrl: Theme.of(context).brightness == Brightness.light
+ ? buySellViewModel.selectedQuote!.lightIconPath
+ : buySellViewModel.selectedQuote!.darkIconPath,
+ width: 24,
+ height: 24,
+ ),
+ Text(
+ buySellViewModel.selectedQuote!.rampName ??
+ buySellViewModel.selectedQuote!.provider.title,
+ style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
- fontWeight: FontWeight.w500),
- ),
- SizedBox.shrink()
- ],
- )),
- if(buySellViewModel.selectedQuote!.paymentType.title?.isNotEmpty ?? false)
- ListItemRegularRow(
- showArrow: false,
- keyValue: "payment method",
- label: S.of(context).payment_method,
- trailingText: buySellViewModel.selectedQuote!.paymentType.title),
- ListItemRegularRow(
- showArrow: false,
- keyValue: "rate",
- label: S.of(context).rate,
- trailingText: buySellViewModel.selectedQuote!.topLeftSubTitle)
- ]
- }),
- )
- ],
- );
- },
- )),
- Padding(
- padding: EdgeInsets.all(18),
- child: NewPrimaryButton(
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ const SizedBox.shrink(),
+ ],
+ ),
+ ),
+ if (buySellViewModel.selectedQuote!.paymentType.title?.isNotEmpty ??
+ false)
+ ListItemRegularRow(
+ showArrow: false,
+ keyValue: "payment method",
+ label: S.of(context).payment_method,
+ trailingText: buySellViewModel.selectedQuote!.paymentType.title,
+ ),
+ ListItemRegularRow(
+ showArrow: false,
+ keyValue: "rate",
+ label: S.of(context).rate,
+ trailingText: buySellViewModel.selectedQuote!.topLeftSubTitle,
+ ),
+ ],
+ },
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(18),
+ child: NewPrimaryButton(
onPressed: () => confirm(context),
text: S.of(context).proceed,
color: Theme.of(context).colorScheme.primary,
- textColor: Theme.of(context).colorScheme.onPrimary),
- )
- ],
+ textColor: Theme.of(context).colorScheme.onPrimary,
+ ),
+ ),
+ ],
+ ),
),
- ),
- );
- }
+ );
String get _pageTitle =>
- (buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy : S.current.sell) +
- " " +
- (buySellViewModel.cryptoCurrency.fullName ?? "");
+ "${buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy : S.current.sell} ${buySellViewModel.cryptoCurrency.fullName ?? ""}";
void confirm(BuildContext context) {
final page = BuySellRedirectingPage(buySellViewModel: buySellViewModel);
Navigator.of(context, rootNavigator: true).pop();
- Navigator.of(context, rootNavigator: true).push(CupertinoPageRoute(
+ Navigator.of(context, rootNavigator: true).push(
+ CupertinoPageRoute(
builder: (context) => Material(
- child: page,
- )));
+ child: page,
+ ),
+ ),
+ );
}
}
diff --git a/lib/new-ui/pages/buy_sell/buy_sell_payment_method_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_payment_method_page.dart
index c4ad495d..2e5b3740 100644
--- a/lib/new-ui/pages/buy_sell/buy_sell_payment_method_page.dart
+++ b/lib/new-ui/pages/buy_sell/buy_sell_payment_method_page.dart
@@ -1,73 +1,81 @@
-import 'package:cake_wallet/buy/payment_method.dart';
-import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart';
-import 'package:cake_wallet/generated/i18n.dart';
-import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
-import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart';
-import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_mobx/flutter_mobx.dart';
+import "dart:async";
+
+import "package:cake_wallet/buy/payment_method.dart";
+import "package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart";
+import "package:cake_wallet/generated/i18n.dart";
+import "package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart";
+import "package:cake_wallet/src/widgets/new_list_row/new_list_section.dart";
+import "package:cake_wallet/view_model/buy/buy_sell_view_model.dart";
+import "package:flutter/material.dart";
+import "package:flutter_mobx/flutter_mobx.dart";
class BuySellPaymentMethodPage extends StatelessWidget {
- const BuySellPaymentMethodPage({super.key, required this.buySellViewModel});
+ const BuySellPaymentMethodPage({required this.buySellViewModel, super.key});
final BuySellViewModel buySellViewModel;
@override
- Widget build(BuildContext context) {
- return Container(
- decoration: BoxDecoration(
- borderRadius: BorderRadius.vertical(top: Radius.circular(18)),
- gradient: LinearGradient(
- colors: [
- Theme.of(context).colorScheme.surface,
- Theme.of(context).colorScheme.surfaceDim,
- ],
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
+ Widget build(BuildContext context) => Container(
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.vertical(top: Radius.circular(18)),
+ gradient: LinearGradient(
+ colors: [
+ Theme.of(context).colorScheme.surface,
+ Theme.of(context).colorScheme.surfaceDim,
+ ],
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ ),
),
- ),
- child: SafeArea(
+ child: SafeArea(
child: Column(
- children: [
- ModalTopBar(
- title: S.of(context).payment_method,
- leadingIcon: Icon(Icons.arrow_back_ios_new),
- onLeadingPressed: Navigator.of(context).pop,
+ children: [
+ ModalTopBar(
+ title: S.of(context).payment_method,
+ leadingIcon: const Icon(Icons.arrow_back_ios_new),
+ onLeadingPressed: Navigator.of(context).pop,
+ leadingSemanticLabel: S.of(context).seed_alert_back,
+ ),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 18),
+ child: Observer(
+ builder: (_) => NewListSections(
+ sections: {
+ "": buySellViewModel.paymentMethods
+ .map(
+ (item) => ListItemRegularRow(
+ keyValue: item.title,
+ label: item.title,
+ showArrow: false,
+ iconPath: Theme.of(context).brightness == Brightness.light
+ ? item.lightIconPath
+ : item.darkIconPath,
+ iconColor: item.paymentMethodType.isMonochromeIcon
+ ? Theme.of(context).colorScheme.onSurfaceVariant
+ : null,
+ trailingWidget: buySellViewModel.selectedPaymentMethod == item
+ ? Icon(
+ Icons.check,
+ color: Theme.of(context).colorScheme.primary,
+ size: 16,
+ )
+ : null,
+ onTap: () {
+ buySellViewModel.changeOption(item);
+ unawaited(buySellViewModel.calculateBestRate());
+ Navigator.of(context).pop();
+ },
+ ),
+ )
+ .toList(),
+ },
+ ),
+ ),
+ ),
+ ),
+ ],
),
- Expanded(
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 18.0),
- child: Observer(
- builder: (_) => NewListSections(sections: {
- "": buySellViewModel.paymentMethods
- .map((item) => ListItemRegularRow(
- keyValue: item.title,
- label: item.title,
- showArrow: false,
- iconPath: Theme.of(context).brightness == Brightness.light
- ? item.lightIconPath
- : item.darkIconPath,
- iconColor: item.paymentMethodType.isMonochromeIcon
- ? Theme.of(context).colorScheme.onSurfaceVariant
- : null,
- trailingWidget: buySellViewModel.selectedPaymentMethod == item
- ? Icon(
- Icons.check,
- color: Theme.of(context).colorScheme.primary,
- size: 16,
- )
- : null,
- onTap: () async {
- buySellViewModel.changeOption(item);
- buySellViewModel.calculateBestRate();
- Navigator.of(context).pop();
- }))
- .toList()
- }),
- ),
- ))
- ],
- )),
- );
- }
+ ),
+ );
}
diff --git a/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart
index 99c91c13..a9e5c1e1 100644
--- a/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart
+++ b/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart
@@ -1,23 +1,22 @@
-import 'package:cake_wallet/buy/buy_quote.dart';
-import 'package:cake_wallet/buy/sell_buy_states.dart';
-import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item.dart';
-import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_dropdown.dart';
-import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart';
-import 'package:cake_wallet/generated/i18n.dart';
-import 'package:cake_wallet/new-ui/pages/buy_sell/buy_sell_confirmation_page.dart';
-import 'package:cake_wallet/new-ui/pages/buy_sell/buy_sell_payment_method_page.dart';
-import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart';
+import "package:cake_wallet/buy/buy_quote.dart";
+import "package:cake_wallet/buy/sell_buy_states.dart";
+import "package:cake_wallet/entities/new_ui_entities/list_item/list_item.dart";
+import "package:cake_wallet/entities/new_ui_entities/list_item/list_item_dropdown.dart";
+import "package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart";
+import "package:cake_wallet/generated/i18n.dart";
+import "package:cake_wallet/new-ui/pages/buy_sell/buy_sell_confirmation_page.dart";
+import "package:cake_wallet/new-ui/pages/buy_sell/buy_sell_payment_method_page.dart";
import "package:cake_wallet/new-ui/widgets/money/money_text.dart";
-import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
-import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart';
-import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart';
-import 'package:flutter/cupertino.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_mobx/flutter_mobx.dart';
+import "package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart";
+import "package:cake_wallet/src/widgets/new_list_row/new_list_section.dart";
+import "package:cake_wallet/view_model/buy/buy_sell_view_model.dart";
+import "package:flutter/cupertino.dart";
+import "package:flutter/material.dart";
+import "package:flutter_mobx/flutter_mobx.dart";
import "package:modal_bottom_sheet/modal_bottom_sheet.dart";
class BuySellProviderPage extends StatefulWidget {
- const BuySellProviderPage({super.key, required this.buySellViewModel});
+ const BuySellProviderPage({required this.buySellViewModel, super.key});
final BuySellViewModel buySellViewModel;
@@ -29,116 +28,128 @@ class _BuySellProviderPageState extends State<BuySellProviderPage> {
bool _allProvidersExpanded = false;
@override
- Widget build(BuildContext context) {
- return Container(
- decoration: BoxDecoration(
- borderRadius: BorderRadius.vertical(top: Radius.circular(18)),
- gradient: LinearGradient(
- colors: [
- Theme.of(context).colorScheme.surface,
- Theme.of(context).colorScheme.surfaceDim,
- ],
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
+ Widget build(BuildContext context) => Container(
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.vertical(top: Radius.circular(18)),
+ gradient: LinearGradient(
+ colors: [
+ Theme.of(context).colorScheme.surface,
+ Theme.of(context).colorScheme.surfaceDim,
+ ],
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ ),
),
- ),
- child: SafeArea(
+ child: SafeArea(
child: Column(
- children: [
- ModalTopBar(
- title: _pageTitle,
- leadingIcon: Icon(Icons.arrow_back_ios_new),
- onLeadingPressed: Navigator.of(context).pop,
- ),
- Expanded(child: Observer(
- builder: (_) {
- if (widget.buySellViewModel.buySellQuotState is BuySellQuotFailed) {
- return Column(
- mainAxisAlignment: MainAxisAlignment.center,
- spacing: 24,
- children: [
- Icon(Icons.warning_amber_outlined, size: 48),
- Column(
- spacing: 10,
- children: [
- Text(
- S.of(context).could_not_load_quotes,
- style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
- ),
- Text((widget.buySellViewModel.buySellQuotState as BuySellQuotFailed)
- .errorMessage ??
- S.of(context).please_try_again_later)
- ],
- )
- ],
- );
- }
+ children: [
+ ModalTopBar(
+ title: _pageTitle,
+ leadingIcon: const Icon(Icons.arrow_back_ios_new),
+ onLeadingPressed: Navigator.of(context).pop,
+ leadingSemanticLabel: S.of(context).seed_alert_back,
+ ),
+ Expanded(
+ child: Observer(
+ builder: (_) {
+ if (widget.buySellViewModel.buySellQuotState is BuySellQuotFailed) {
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ spacing: 24,
+ children: [
+ const Icon(Icons.warning_amber_outlined, size: 48),
+ Column(
+ spacing: 10,
+ children: [
+ Text(
+ S.of(context).could_not_load_quotes,
+ style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
+ ),
+ Text(
+ (widget.buySellViewModel.buySellQuotState as BuySellQuotFailed)
+ .errorMessage ??
+ S.of(context).please_try_again_later,
+ ),
+ ],
+ ),
+ ],
+ );
+ }
- if (widget.buySellViewModel.buySellQuotState is BuySellQuotLoading) {
- return Center(
- child: Row(
- mainAxisAlignment: MainAxisAlignment.center,
- spacing: 8,
- children: [
- CupertinoActivityIndicator(),
- Text(
- S.of(context).loading_rates,
- style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
- )
- ],
- ),
- );
- }
+ if (widget.buySellViewModel.buySellQuotState is BuySellQuotLoading) {
+ return Center(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ spacing: 8,
+ children: [
+ const CupertinoActivityIndicator(),
+ Text(
+ S.of(context).loading_rates,
+ style:
+ TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
+ ),
+ ],
+ ),
+ );
+ }
- return SingleChildScrollView(
- controller: ModalScrollController.of(context),
- child: Padding(
- padding: EdgeInsets.symmetric(horizontal: 18),
- child: NewListSections(showHeader: true, sections: {
- "": [
- ListItemRegularRow(
- keyValue: "payment method",
- label: S.of(context).payment_method,
- showArrow: true,
- onTap: () {
- final page =
- BuySellPaymentMethodPage(buySellViewModel: widget.buySellViewModel);
- Navigator.of(context).push(CupertinoPageRoute(
- builder: (context) => Material(
- color: Colors.transparent,
- child: page,
- )));
+ return SingleChildScrollView(
+ controller: ModalScrollController.of(context),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 18),
+ child: NewListSections(
+ showHeader: true,
+ sections: {
+ "": [
+ ListItemRegularRow(
+ keyValue: "payment method",
+ label: S.of(context).payment_method,
+ showArrow: true,
+ onTap: () {
+ final page = BuySellPaymentMethodPage(
+ buySellViewModel: widget.buySellViewModel,
+ );
+ Navigator.of(context).push(
+ CupertinoPageRoute(
+ builder: (context) => Material(
+ color: Colors.transparent,
+ child: page,
+ ),
+ ),
+ );
+ },
+ trailingText: widget.buySellViewModel.selectedPaymentMethod?.title,
+ ),
+ ],
+ S.of(context).available_providers: [
+ ...widget.buySellViewModel.sortedRecommendedQuotes.map(quoteListItem),
+ if (widget.buySellViewModel.sortedQuotes.isNotEmpty)
+ ListItemDropdown(
+ keyValue: "more options",
+ label: S.of(context).more_options,
+ onTap: () {
+ setState(() {
+ _allProvidersExpanded = !_allProvidersExpanded;
+ });
+ },
+ ),
+ if (_allProvidersExpanded)
+ ...widget.buySellViewModel.sortedQuotes.map(quoteListItem),
+ ],
},
- trailingText: widget.buySellViewModel.selectedPaymentMethod?.title)
- ],
- S.of(context).available_providers: [
- ...widget.buySellViewModel.sortedRecommendedQuotes.map(quoteListItem),
- if (widget.buySellViewModel.sortedQuotes.isNotEmpty)
- ListItemDropdown(
- keyValue: "more options",
- label: S.of(context).more_options,
- onTap: () {
- setState(() {
- _allProvidersExpanded = !_allProvidersExpanded;
- });
- }),
- if (_allProvidersExpanded)
- ...widget.buySellViewModel.sortedQuotes.map(quoteListItem)
- ]
- }),
+ ),
+ ),
+ );
+ },
),
- );
- },
- ))
- ],
- )),
- );
- }
+ ),
+ ],
+ ),
+ ),
+ );
String get _pageTitle =>
- (widget.buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy : S.current.sell) +
- " " +
- (widget.buySellViewModel.cryptoCurrency.fullName ?? "");
+ "${widget.buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy : S.current.sell} ${widget.buySellViewModel.cryptoCurrency.fullName ?? ""}";
ListItem quoteListItem(Quote quote) {
final fiatAmount = widget.buySellViewModel.fiatAmountForQuote(quote);
@@ -148,10 +159,10 @@ class _BuySellProviderPageState extends State<BuySellProviderPage> {
secondaryLabel: quote.rampName != null ? quote.provider.title : null,
subtitle: quote.badges.isEmpty ? null : quote.badges.join(" - "),
subtitleColor: Theme.of(context).colorScheme.primary,
- iconPath: Theme.of(context).brightness == Brightness.light
- ? quote.lightIconPath
- : quote.darkIconPath,
- onTap: () {
+ iconPath: Theme.of(context).brightness == Brightness.light
+ ? quote.lightIconPath
+ : quote.darkIconPath,
+ onTap: () {
widget.buySellViewModel.changeOption(quote);
navigateToConfirmation(context);
},
@@ -161,21 +172,26 @@ class _BuySellProviderPageState extends State<BuySellProviderPage> {
mainAxisAlignment: MainAxisAlignment.center,
spacing: 4,
children: [
- MoneyText.optional(widget.buySellViewModel.amountForQuote(quote)),
- if (fiatAmount != null)
- Text(
+ MoneyText.optional(widget.buySellViewModel.amountForQuote(quote)),
+ if (fiatAmount != null)
+ Text(
"= ${fiatAmount.toStringWithSymbol(fractionalDigits: 2, trimZeros: false)}",
- style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant))
+ style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant),
+ ),
],
- ));
+ ),
+ );
}
void navigateToConfirmation(BuildContext context) {
final page = BuySellConfirmationPage(buySellViewModel: widget.buySellViewModel);
- Navigator.of(context).push(CupertinoPageRoute(
+ Navigator.of(context).push(
+ CupertinoPageRoute(
builder: (context) => Material(
- color: Colors.transparent,
- child: page,
- )));
+ color: Colors.transparent,
+ child: page,
+ ),
+ ),
+ );
}
}
diff --git a/lib/new-ui/pages/buy_sell/buy_sell_redirecting_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_redirecting_page.dart
index bf7d716f..39c94971 100644
--- a/lib/new-ui/pages/buy_sell/buy_sell_redirecting_page.dart
+++ b/lib/new-ui/pages/buy_sell/buy_sell_redirecting_page.dart
@@ -1,13 +1,13 @@
-import 'package:cake_wallet/buy/sell_buy_states.dart';
-import 'package:cake_wallet/generated/i18n.dart';
-import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
-import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
-import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_mobx/flutter_mobx.dart';
+import "package:cake_wallet/buy/sell_buy_states.dart";
+import "package:cake_wallet/generated/i18n.dart";
+import "package:cake_wallet/new-ui/widgets/new_primary_button.dart";
+import "package:cake_wallet/src/widgets/cake_image_widget.dart";
+import "package:cake_wallet/view_model/buy/buy_sell_view_model.dart";
+import "package:flutter/material.dart";
+import "package:flutter_mobx/flutter_mobx.dart";
class BuySellRedirectingPage extends StatefulWidget {
- const BuySellRedirectingPage({super.key, required this.buySellViewModel});
+ const BuySellRedirectingPage({required this.buySellViewModel, super.key});
final BuySellViewModel buySellViewModel;
@@ -21,7 +21,7 @@ class _BuySellRedirectingPageState extends State<BuySellRedirectingPage> {
@override
void initState() {
super.initState();
- Future.delayed(Duration(seconds: 2)).then((_) async {
+ Future.delayed(const Duration(seconds: 2)).then((_) {
WidgetsBinding.instance
.addPostFrameCallback((_) => widget.buySellViewModel.launchTrade(context));
setState(() {
@@ -31,97 +31,99 @@ class _BuySellRedirectingPageState extends State<BuySellRedirectingPage> {
}
@override
- Widget build(BuildContext context) {
- return PopScope(
- canPop: false,
- child: Container(
- decoration: BoxDecoration(
- borderRadius: BorderRadius.vertical(top: Radius.circular(18)),
- gradient: LinearGradient(
- colors: [
- Theme.of(context).colorScheme.surface,
- Theme.of(context).colorScheme.surfaceDim,
- ],
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
+ Widget build(BuildContext context) => PopScope(
+ canPop: false,
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.vertical(top: Radius.circular(18)),
+ gradient: LinearGradient(
+ colors: [
+ Theme.of(context).colorScheme.surface,
+ Theme.of(context).colorScheme.surfaceDim,
+ ],
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ ),
),
- ),
- child: Observer(
- builder: (_) {
- final showExitButton =
- _hasRedirected || widget.buySellViewModel.buySellQuotState is BuySellQuotFailed;
+ child: Observer(
+ builder: (_) {
+ final showExitButton =
+ _hasRedirected || widget.buySellViewModel.buySellQuotState is BuySellQuotFailed;
- return SafeArea(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- SizedBox.shrink(),
- Observer(
- builder: (_) {
- if (widget.buySellViewModel.buySellQuotState is BuySellQuotFailed) {
+ return SafeArea(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ const SizedBox.shrink(),
+ Observer(
+ builder: (_) {
+ if (widget.buySellViewModel.buySellQuotState is BuySellQuotFailed) {
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ spacing: 24,
+ children: [
+ const Icon(Icons.warning_amber_outlined, size: 48),
+ Column(
+ spacing: 10,
+ children: [
+ Text(
+ S.of(context).could_not_proceed_with_trade,
+ style:
+ const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
+ ),
+ Text(
+ (widget.buySellViewModel.buySellQuotState as BuySellQuotFailed)
+ .errorMessage ??
+ S.of(context).please_try_again_later,
+ ),
+ ],
+ ),
+ ],
+ );
+ }
return Column(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 24,
children: [
- Icon(Icons.warning_amber_outlined, size: 48),
+ CakeImageWidget(
+ imageUrl: Theme.of(context).brightness == Brightness.light
+ ? widget.buySellViewModel.selectedQuote!.lightIconPath
+ : widget.buySellViewModel.selectedQuote!.darkIconPath,
+ width: 64,
+ height: 64,
+ ),
Column(
spacing: 10,
children: [
Text(
- S.of(context).could_not_proceed_with_trade,
- style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
+ "${S.of(context).connecting_you_to} ${widget.buySellViewModel.selectedQuote!.provider.title}...",
+ style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
+ ),
+ Text(
+ "${widget.buySellViewModel.fiatAmount} ${widget.buySellViewModel.fiatCurrency} → ${widget.buySellViewModel.amountForQuote(widget.buySellViewModel.selectedQuote!)?.toStringWithSymbol(fractionalDigits: 8)}",
),
- Text((widget.buySellViewModel.buySellQuotState as BuySellQuotFailed)
- .errorMessage ??
- S.of(context).please_try_again_later)
],
- )
+ ),
],
);
- }
- return Column(
- mainAxisAlignment: MainAxisAlignment.center,
- spacing: 24,
- children: [
- CakeImageWidget(
- imageUrl: Theme.of(context).brightness == Brightness.light
- ? widget.buySellViewModel.selectedQuote!.lightIconPath
- : widget.buySellViewModel.selectedQuote!.darkIconPath,
- width: 64,
- height: 64,
- ),
- Column(
- spacing: 10,
- children: [
- Text(
- "${S.of(context).connecting_you_to} ${widget.buySellViewModel.selectedQuote!.provider.title}...",
- style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
- ),
- Text(
- "${widget.buySellViewModel.fiatAmount} ${widget.buySellViewModel.fiatCurrency} → ${widget.buySellViewModel.amountForQuote(widget.buySellViewModel.selectedQuote!)?.toStringWithSymbol(fractionalDigits: 8)}")
- ],
+ },
+ ),
+ showExitButton
+ ? Padding(
+ padding: const EdgeInsets.all(18),
+ child: NewPrimaryButton(
+ onPressed: Navigator.of(context).pop,
+ text: S.of(context).close,
+ color: Theme.of(context).colorScheme.primary,
+ textColor: Theme.of(context).colorScheme.onPrimary,
+ ),
)
- ],
- );
- },
- ),
- showExitButton
- ? Padding(
- padding: EdgeInsets.all(18),
- child: NewPrimaryButton(
- onPressed: Navigator.of(context).pop,
- text: S.of(context).close,
- color: Theme.of(context).colorScheme.primary,
- textColor: Theme.of(context).colorScheme.onPrimary,
- ),
- )
- : SizedBox.shrink()
- ],
- ),
- );
- },
+ : const SizedBox.shrink(),
+ ],
+ ),
+ );
+ },
+ ),
),
- ),
- );
- }
+ );
}
diff --git a/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart b/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart
index 36f70f35..ed107339 100644
--- a/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart
+++ b/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart
@@ -1,22 +1,20 @@
-import 'package:cake_wallet/di.dart';
-import 'package:cake_wallet/generated/i18n.dart';
+import "package:cake_wallet/di.dart";
+import "package:cake_wallet/generated/i18n.dart";
import "package:cake_wallet/main.dart";
-import 'package:cake_wallet/new-ui/modal_navigator.dart';
-import 'package:cake_wallet/new-ui/pages/buy_sell/buy_sell_amount_page.dart';
-import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
-import 'package:cake_wallet/themes/core/theme_extension.dart';
-import 'package:flutter/material.dart';
-
-enum BuySellPageMode { buy, sell }
+import "package:cake_wallet/new-ui/modal_navigator.dart";
+import "package:cake_wallet/new-ui/pages/buy_sell/buy_sell_amount_page.dart";
+import "package:cake_wallet/src/widgets/cake_image_widget.dart";
+import "package:cake_wallet/themes/core/theme_extension.dart";
+import "package:cake_wallet/view_model/buy/buy_sell_view_model.dart";
+import "package:flutter/material.dart";
class BuySellSelectorModal extends StatelessWidget {
const BuySellSelectorModal({super.key});
@override
- Widget build(BuildContext context) {
- return Container(
+ Widget build(BuildContext context) => Container(
decoration: BoxDecoration(
- borderRadius: BorderRadius.vertical(top: Radius.circular(18)),
+ borderRadius: const BorderRadius.vertical(top: Radius.circular(18)),
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.surface,
@@ -27,12 +25,15 @@ class BuySellSelectorModal extends StatelessWidget {
),
),
child: SafeArea(
- top: false,
- child: Column(spacing: 24, mainAxisSize: MainAxisSize.min, children: [
- SizedBox.shrink(),
+ top: false,
+ child: Column(
+ spacing: 24,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const SizedBox.shrink(),
Text(
S.of(context).buy_or_sell,
- style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
+ style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
),
Text(
S.of(context).buy_or_sell_desc,
@@ -40,7 +41,7 @@ class BuySellSelectorModal extends StatelessWidget {
TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant),
),
Padding(
- padding: EdgeInsets.symmetric(horizontal: 18.0),
+ padding: const EdgeInsets.symmetric(horizontal: 18),
child: Column(
spacing: 12,
children: [
@@ -51,39 +52,43 @@ class BuySellSelectorModal extends StatelessWidget {
onTap: () => openBuySellPage(context, BuySellPageMode.buy),
),
BuySellSelectorModalButton(
- title: S.of(context).sell_crypto,
- description: S.of(context).sell_crypto_desc,
- iconPath: "assets/new-ui/sell.svg",
- onTap: () => openBuySellPage(context, BuySellPageMode.sell))
+ title: S.of(context).sell_crypto,
+ description: S.of(context).sell_crypto_desc,
+ iconPath: "assets/new-ui/sell.svg",
+ onTap: () => openBuySellPage(context, BuySellPageMode.sell),
+ ),
],
),
),
- SizedBox.shrink()
- ])));
- }
+ const SizedBox.shrink(),
+ ],
+ ),
+ ),
+ );
void openBuySellPage(BuildContext context, BuySellPageMode mode) {
Navigator.of(context).pop();
final page = getIt.get<NewBuySellAmountPage>(param1: mode);
showModalBottomSheet(
- useSafeArea: true,
- isScrollControlled: true,
- context: navigatorKey.currentContext!,
- builder: (modalContext) => ModalNavigator(
- rootPage: page,
- parentContext: modalContext,
- ));
+ useSafeArea: true,
+ isScrollControlled: true,
+ context: navigatorKey.currentContext!,
+ builder: (modalContext) => ModalNavigator(
+ rootPage: page,
+ parentContext: modalContext,
+ ),
+ );
}
-
}
class BuySellSelectorModalButton extends StatelessWidget {
- const BuySellSelectorModalButton(
- {super.key,
- required this.title,
- required this.description,
- required this.iconPath,
- required this.onTap});
+ const BuySellSelectorModalButton({
+ required this.title,
+ required this.description,
+ required this.iconPath,
+ required this.onTap,
+ super.key,
+ });
final String title;
final String description;
@@ -91,56 +96,58 @@ class BuySellSelectorModalButton extends StatelessWidget {
final VoidCallback onTap;
@override
- Widget build(BuildContext context) {
- return GestureDetector(
- onTap: onTap,
- child: Container(
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(16),
- border: Border.all(
- width: 1,
- color: Theme.of(context).colorScheme.surfaceContainerHigh,
- ),
- gradient: LinearGradient(
- colors: [
- context.customColors.cardGradientColorPrimary,
- context.customColors.cardGradientColorSecondary
- ],
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
+ Widget build(BuildContext context) => GestureDetector(
+ onTap: onTap,
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(16),
+ border: Border.all(
+ width: 1,
+ color: Theme.of(context).colorScheme.surfaceContainerHigh,
+ ),
+ gradient: LinearGradient(
+ colors: [
+ context.customColors.cardGradientColorPrimary,
+ context.customColors.cardGradientColorSecondary,
+ ],
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ ),
),
- ),
- child: Padding(
- padding: EdgeInsets.all(24),
- child: Row(
- spacing: 20,
- children: [
- CakeImageWidget(
- imageUrl: iconPath,
- width: 55,
- height: 55,
- colorFilter: ColorFilter.mode(
- Theme.of(context).colorScheme.onSurfaceVariant, BlendMode.srcIn),
- ),
- Column(
- spacing: 8,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- title,
- style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16),
+ child: Padding(
+ padding: const EdgeInsets.all(24),
+ child: Row(
+ spacing: 20,
+ children: [
+ CakeImageWidget(
+ imageUrl: iconPath,
+ width: 55,
+ height: 55,
+ colorFilter: ColorFilter.mode(
+ Theme.of(context).colorScheme.onSurfaceVariant,
+ BlendMode.srcIn,
),
- Text(
- description,
- style: TextStyle(
- fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant),
- )
- ],
- )
- ],
+ ),
+ Column(
+ spacing: 8,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ title,
+ style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16),
+ ),
+ Text(
+ description,
+ style: TextStyle(
+ fontSize: 12,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
),
),
- ),
- );
- }
+ );
}
diff --git a/lib/new-ui/widgets/floating_amount_input.dart b/lib/new-ui/widgets/floating_amount_input.dart
index 237f8582..5dd5b1d8 100644
--- a/lib/new-ui/widgets/floating_amount_input.dart
+++ b/lib/new-ui/widgets/floating_amount_input.dart
@@ -1,17 +1,18 @@
import "package:cake_wallet/new-ui/widgets/money/currency_symbol_text.dart";
-import 'package:cw_core/currency.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
+import "package:cw_core/currency.dart";
+import "package:flutter/material.dart";
+import "package:flutter/services.dart";
class FloatingAmountInput extends StatefulWidget {
- const FloatingAmountInput(
- {super.key,
- required this.currency,
- required this.controller,
- this.focusNode,
- this.inputFormatters,
- this.onChanged,
- this.validator});
+ const FloatingAmountInput({
+ required this.currency,
+ required this.controller,
+ super.key,
+ this.focusNode,
+ this.inputFormatters,
+ this.onChanged,
+ this.validator,
+ });
final Currency currency;
final TextEditingController controller;
@@ -35,64 +36,70 @@ class _FloatingAmountInputState extends State<FloatingAmountInput> {
}
@override
- Widget build(BuildContext context) {
- return Center(
- child: Row(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.baseline,
- textBaseline: TextBaseline.alphabetic,
- children: [
- IntrinsicWidth(
- child: TextFormField(
- controller: widget.controller,
- focusNode: focusNode,
- maxLines: 1,
- onChanged: widget.onChanged,
- autovalidateMode: AutovalidateMode.always,
- validator: widget.validator,
- keyboardType: TextInputType.numberWithOptions(
- signed: false,
- decimal: true,
- ),
- inputFormatters: <TextInputFormatter>[
- FilteringTextInputFormatter.allow(
- RegExp(r'^\d*[.,]?\d*$'),
+ void dispose() {
+ if (focusNode != widget.focusNode) {
+ focusNode.dispose();
+ }
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) => Center(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.baseline,
+ textBaseline: TextBaseline.alphabetic,
+ children: [
+ IntrinsicWidth(
+ child: TextFormField(
+ controller: widget.controller,
+ focusNode: focusNode,
+ maxLines: 1,
+ onChanged: widget.onChanged,
+ autovalidateMode: AutovalidateMode.always,
+ validator: widget.validator,
+ keyboardType: const TextInputType.numberWithOptions(
+ signed: false,
+ decimal: true,
),
- ],
- decoration: InputDecoration(
- isDense: true,
- isCollapsed: true,
- contentPadding: EdgeInsets.zero,
- fillColor: Colors.transparent,
- hoverColor: Colors.transparent,
- focusedBorder: InputBorder.none,
- enabledBorder: InputBorder.none,
- hintText: _amountFocused || widget.controller.text.isNotEmpty ? null : "0.00",
- hintStyle: Theme.of(context).textTheme.displayMedium?.copyWith(
+ inputFormatters: <TextInputFormatter>[
+ FilteringTextInputFormatter.allow(
+ RegExp(r"^\d*[.,]?\d*$"),
+ ),
+ ],
+ decoration: InputDecoration(
+ isDense: true,
+ isCollapsed: true,
+ contentPadding: EdgeInsets.zero,
+ fillColor: Colors.transparent,
+ hoverColor: Colors.transparent,
+ focusedBorder: InputBorder.none,
+ enabledBorder: InputBorder.none,
+ hintText: _amountFocused || widget.controller.text.isNotEmpty ? null : "0.00",
+ hintStyle: Theme.of(context).textTheme.displayMedium?.copyWith(
+ fontWeight: FontWeight.w400,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ ),
+ style: Theme.of(context).textTheme.displayMedium?.copyWith(
fontWeight: FontWeight.w400,
- color: Theme.of(context).colorScheme.onSurfaceVariant,
+ fontSize: 45,
+ color: Theme.of(context).colorScheme.onSurface,
),
),
+ ),
+ const SizedBox(width: 8),
+ CurrencySymbolText(
+ widget.currency,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.displayMedium?.copyWith(
fontWeight: FontWeight.w400,
fontSize: 45,
- color: Theme.of(context).colorScheme.onSurface,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
- ),
- const SizedBox(width: 8),
- CurrencySymbolText(
- widget.currency,
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- style: Theme.of(context).textTheme.displayMedium?.copyWith(
- fontWeight: FontWeight.w400,
- fontSize: 45,
- color: Theme.of(context).colorScheme.onSurfaceVariant,
- ),
- ),
- ],
- ),
- );
- }
+ ],
+ ),
+ );
}
diff --git a/lib/router.dart b/lib/router.dart
index 2b01b079..736ec922 100644
--- a/lib/router.dart
+++ b/lib/router.dart
@@ -7,11 +7,13 @@ import 'package:cake_wallet/new-ui/pages/about_page.dart';
import 'package:cake_wallet/new-ui/pages/bridge/bridge_history_page.dart';
import 'package:cake_wallet/new-ui/pages/bridge/bridge_network_page.dart';
import 'package:cake_wallet/new-ui/pages/bridge/bridge_receiving_wallet_page.dart';
+import "package:cake_wallet/new-ui/pages/buy_sell/buy_sell_amount_page.dart";
import 'package:cake_wallet/new-ui/pages/coin_control_page.dart';
import 'package:cake_wallet/new-ui/pages/addresses_page.dart';
import 'package:cake_wallet/new-ui/pages/lightning_username_page.dart';
import "package:cake_wallet/new-ui/pages/receive_page.dart";
import 'package:cake_wallet/new-ui/pages/send_page.dart';
+import "package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart";
import 'package:cake_wallet/new-ui/widgets/hardware_wallet/sync_key_images_sheet.dart';
import 'package:cake_wallet/order/order.dart';
import 'package:cake_wallet/core/new_wallet_type_arguments.dart';
@@ -142,6 +144,7 @@ import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/bridge/bridge_view_model.dart';
import 'package:cake_wallet/view_model/bridge/bridge_history_view_model.dart';
+import "package:cake_wallet/view_model/buy/buy_sell_view_model.dart";
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/sign_view_model.dart';
@@ -703,9 +706,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
builder: (_) => getIt.get<OrderDetailsPage>(param1: settings.arguments as Order));
case Routes.buySellPage:
- final args = settings.arguments as bool?;
return handleRouteWithPlatformAwareness(
- (context) => getIt.get<BuySellPage>(param1: args),
+ (context) => Material(child: getIt.get<NewBuySellAmountPage>(param1: BuySellPageMode.buy)),
);
case Routes.buyOptionsPage:
diff --git a/lib/view_model/buy/buy_sell_view_model.dart b/lib/view_model/buy/buy_sell_view_model.dart
index 5e87267c..048e6162 100644
--- a/lib/view_model/buy/buy_sell_view_model.dart
+++ b/lib/view_model/buy/buy_sell_view_model.dart
@@ -25,6 +25,8 @@ import 'package:mobx/mobx.dart';
part 'buy_sell_view_model.g.dart';
+enum BuySellPageMode { buy, sell }
+
class BuySellViewModel = BuySellViewModelBase with _$BuySellViewModel;
abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with Store {
@@ -89,9 +91,16 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
return mode == BuySellPageMode.buy
? formattedFiatAmount ?? 200.0
- : formattedCryptoAmount ?? (cryptoCurrency == CryptoCurrency.btc ? 0.001 : 1);
+ : formattedCryptoAmount ?? (cryptoCurrency == CryptoCurrency.btc ? 0.01 : 1);
}
+ // sets based on the absolute amout (from the fiat/charts api)
+ // works even if you have no rates
+ Future<void> setCryptoAmountFromFiat(String fiatAmount) async => changeCryptoAmount(
+ amount: (double.parse(fiatAmount) / (fiatConversionStore.prices[cryptoCurrency] ?? 0))
+ .toString(),
+ );
+
final AppStore _appStore;
AmountParsingProxy get amountParsingProxy => _appStore.amountParsingProxy;
@@ -190,18 +199,22 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
// based on usd values, should have roughly equal worth (was done with ai though so it's subject to correction)
static final Map<FiatCurrency, List<String>> _defaultAmountsMap = {
FiatCurrency.amd: ["20000", "40000", "200000", "400000", "1000000"],
+ FiatCurrency.ars: ["50000", "100000", "500000", "1000000", "2500000"],
FiatCurrency.aud: ["100", "200", "1000", "2000", "5000"],
+ FiatCurrency.bdt: ["5000", "10000", "50000", "100000", "250000"],
FiatCurrency.bgn: ["100", "200", "1000", "2000", "5000"],
FiatCurrency.brl: ["250", "500", "2500", "5000", "12500"],
FiatCurrency.cad: ["50", "100", "500", "1000", "2500"],
FiatCurrency.chf: ["50", "100", "500", "1000", "2500"],
FiatCurrency.clp: ["50000", "100000", "500000", "1000000", "2500000"],
+ FiatCurrency.cny: ["500", "1000", "5000", "10000", "25000"],
FiatCurrency.cop: ["200000", "400000", "2000000", "4000000", "10000000"],
FiatCurrency.czk: ["1000", "2000", "10000", "20000", "50000"],
FiatCurrency.dkk: ["400", "800", "4000", "8000", "20000"],
FiatCurrency.egp: ["2500", "5000", "25000", "50000", "125000"],
FiatCurrency.eur: ["50", "100", "500", "1000", "2500"],
FiatCurrency.gbp: ["50", "100", "500", "1000", "2500"],
+ FiatCurrency.ghs: ["500", "1000", "5000", "10000", "25000"],
FiatCurrency.gtq: ["400", "800", "4000", "8000", "20000"],
FiatCurrency.hkd: ["400", "800", "4000", "8000", "20000"],
FiatCurrency.hrk: ["400", "800", "4000", "8000", "20000"],
@@ -209,8 +222,10 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
FiatCurrency.idr: ["800000", "1600000", "8000000", "16000000", "40000000"],
FiatCurrency.ils: ["200", "400", "2000", "4000", "10000"],
FiatCurrency.inr: ["5000", "10000", "50000", "100000", "250000"],
+ FiatCurrency.irr: ["2000000", "4000000", "20000000", "40000000", "100000000"],
FiatCurrency.isk: ["7000", "14000", "70000", "140000", "350000"],
FiatCurrency.jpy: ["10000", "20000", "100000", "200000", "500000"],
+ FiatCurrency.kes: ["5000", "10000", "50000", "100000", "250000"],
FiatCurrency.krw: ["50000", "100000", "500000", "1000000", "2500000"],
FiatCurrency.mad: ["500", "1000", "5000", "10000", "25000"],
FiatCurrency.mxn: ["1000", "2000", "10000", "20000", "50000"],
@@ -222,17 +237,21 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
FiatCurrency.pkr: ["15000", "30000", "150000", "300000", "750000"],
FiatCurrency.pln: ["200", "400", "2000", "4000", "10000"],
FiatCurrency.ron: ["250", "500", "2500", "5000", "12500"],
+ FiatCurrency.rub: ["5000", "10000", "50000", "100000", "250000"],
+ FiatCurrency.sar: ["200", "400", "2000", "4000", "10000"],
FiatCurrency.sek: ["500", "1000", "5000", "10000", "25000"],
FiatCurrency.sgd: ["50", "100", "500", "1000", "2500"],
FiatCurrency.thb: ["2000", "4000", "20000", "40000", "100000"],
FiatCurrency.tur: ["1500", "3000", "15000", "30000", "75000"],
FiatCurrency.twd: ["1500", "3000", "15000", "30000", "75000"],
+ FiatCurrency.uah: ["2000", "4000", "20000", "40000", "100000"],
FiatCurrency.usd: ["50", "100", "500", "1000", "2500"],
+ FiatCurrency.vef: ["2000", "4000", "20000", "40000", "100000"],
FiatCurrency.vnd: ["1000000", "2000000", "10000000", "20000000", "50000000"],
FiatCurrency.zar: ["1000", "2000", "10000", "20000", "50000"],
- FiatCurrency.kes: ["5000", "10000", "50000", "100000", "250000"],
};
+
// the fallback is just the usd values.
// not great but this fallback shouldn't be triggered anyway
List<String> get defaultAmounts =>
@@ -445,50 +464,45 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
await calculateBestRate();
}
- String _getInitialCryptoCurrencyAddress() {
- if (cryptoCurrency == wallet.currency) {
- if ([CryptoCurrency.zec, CryptoCurrency.btc].contains(cryptoCurrency)) {
- return wallet.walletAddresses.addressForBuy;
- }
-
- return wallet.walletAddresses.address;
- }
- return '';
- }
+ String _getInitialCryptoCurrencyAddress() => wallet.walletAddresses.addressForBuy;
@action
Future<void> _getAvailablePaymentTypes() async {
- paymentMethodState = PaymentMethodLoading();
- selectedPaymentMethod = null;
- final result = await Future.wait(providerList.map((element) => element
- .getAvailablePaymentTypes(fiatCurrency.title, cryptoCurrency, mode == BuySellPageMode.buy)
- .timeout(
- Duration(seconds: 10),
- onTimeout: () => [],
- )));
-
- final List<PaymentMethod> tempPaymentMethods = [];
-
- for (var methods in result) {
- for (var method in methods) {
- final alreadyExists = tempPaymentMethods.any((m) {
- return m.paymentMethodType == method.paymentMethodType;
- });
-
- if (!alreadyExists) {
- tempPaymentMethods.add(method);
+ try{
+ paymentMethodState = PaymentMethodLoading();
+ selectedPaymentMethod = null;
+ final result = await Future.wait(providerList.map((element) => element
+ .getAvailablePaymentTypes(fiatCurrency.title, cryptoCurrency, mode == BuySellPageMode.buy)
+ .timeout(
+ Duration(seconds: 10),
+ onTimeout: () => [],
+ )));
+
+ final List<PaymentMethod> tempPaymentMethods = [];
+
+ for (var methods in result) {
+ for (var method in methods) {
+ final alreadyExists = tempPaymentMethods.any((m) {
+ return m.paymentMethodType == method.paymentMethodType;
+ });
+
+ if (!alreadyExists) {
+ tempPaymentMethods.add(method);
+ }
}
}
- }
- paymentMethods = ObservableList<PaymentMethod>.of(tempPaymentMethods);
+ paymentMethods = ObservableList<PaymentMethod>.of(tempPaymentMethods);
- if (paymentMethods.isNotEmpty) {
- paymentMethods.insert(0, PaymentMethod.all());
- selectedPaymentMethod = paymentMethods.first;
- selectedPaymentMethod!.isSelected = true;
- paymentMethodState = PaymentMethodLoaded();
- } else {
+ if (paymentMethods.isNotEmpty) {
+ paymentMethods.insert(0, PaymentMethod.all());
+ selectedPaymentMethod = paymentMethods.first;
+ selectedPaymentMethod!.isSelected = true;
+ paymentMethodState = PaymentMethodLoaded();
+ } else {
+ paymentMethodState = PaymentMethodFailed();
+ }
+ } catch(e) {
paymentMethodState = PaymentMethodFailed();
}
}
@@ -502,12 +516,10 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
return provider.supportedCryptoList.any((pair) =>
pair.from.symbol == cryptoCurrency.symbol &&
pair.from.tag == cryptoCurrency.tag &&
- pair.to.symbol == fiatCurrency.symbol &&
- pair.to.tag == fiatCurrency.tag);
+ pair.to.symbol == fiatCurrency.symbol);
} else {
return provider.supportedFiatList.any((pair) =>
pair.from.symbol == fiatCurrency.symbol &&
- pair.from.tag == fiatCurrency.tag &&
pair.to.symbol == cryptoCurrency.symbol &&
pair.to.tag == cryptoCurrency.tag);
}
@@ -515,7 +527,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
if (validProviders.isEmpty) {
buySellQuotState = BuySellQuotFailed(
- errorMessage: "Couldn't find a provider that supports ${cryptoCurrency.fullName}.");
+ errorMessage: S.current.no_buy_providers(cryptoCurrency.fullName??cryptoCurrency.title));
return;
}
@@ -544,7 +556,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
if (validQuotes.isEmpty) {
buySellQuotState = BuySellQuotFailed(
- errorMessage: "No provider could create a quote for ${cryptoCurrency.fullName}");
+ errorMessage: S.current.no_buy_providers(cryptoCurrency.fullName??cryptoCurrency.title));
return;
}
diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb
index 09f01168..c5dce28c 100644
--- a/res/values/strings_en.arb
+++ b/res/values/strings_en.arb
@@ -646,6 +646,7 @@
"long_press_to_copy": "Long press to copy",
"low_fee": "Low fee",
"low_fee_alert": "You currently are using a low network fee priority. This could cause long waits, different rates, or canceled trades. We recommend setting a higher fee for a better experience.",
+ "low_kyc": "Low KYC",
"made_easy": "made easy",
"manage_nodes": "Manage nodes",
"manage_pow_nodes": "Manage PoW nodes",
@@ -714,6 +715,7 @@
"newConnection": "New Connection",
"nfts": "NFTs",
"no": "No",
+ "no_buy_providers": "No providers found for ${curr}.",
"no_cards_found": "No cards found",
"no_extra_detail": "No extra details available",
"no_id_needed": "No ID needed!",
Why this scored 23/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.