mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
reformat
This commit is contained in:
@@ -1,435 +1,435 @@
|
||||
import 'dart:async';
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cake_wallet/buy/payment_method.dart';
|
||||
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/floating_amount_input.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/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/themes/core/theme_extension.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/buy/payment_method.dart';
|
||||
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/floating_amount_input.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/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/themes/core/theme_extension.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class NewBuySellAmountPage extends StatefulWidget {
|
||||
const NewBuySellAmountPage({super.key, required this.buySellViewModel});
|
||||
class NewBuySellAmountPage extends StatefulWidget {
|
||||
const NewBuySellAmountPage({super.key, required this.buySellViewModel});
|
||||
|
||||
final BuySellViewModel buySellViewModel;
|
||||
final BuySellViewModel buySellViewModel;
|
||||
|
||||
@override
|
||||
State<NewBuySellAmountPage> createState() => _NewBuySellAmountPageState();
|
||||
}
|
||||
|
||||
class _NewBuySellAmountPageState extends State<NewBuySellAmountPage> {
|
||||
bool _customAmountMode = false;
|
||||
bool _isLoadingPaymentMethods = false;
|
||||
final customInputController = TextEditingController();
|
||||
final customInputFocusNode = FocusNode();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// 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.calculateBestRate();
|
||||
when(
|
||||
(_) => widget.buySellViewModel.paymentMethodState is PaymentMethodLoaded,
|
||||
() => widget.buySellViewModel.selectedPaymentMethod = widget.buySellViewModel.paymentMethods
|
||||
.firstWhere((item) => item.paymentMethodType == PaymentType.all));
|
||||
@override
|
||||
State<NewBuySellAmountPage> createState() => _NewBuySellAmountPageState();
|
||||
}
|
||||
|
||||
@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,
|
||||
),
|
||||
),
|
||||
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: Icon(Icons.close),
|
||||
onLeadingPressed: Navigator.of(context, rootNavigator: true).pop,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Observer(
|
||||
builder: (_) => AnimatedSwitcher(
|
||||
duration: 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: ValueKey(0),
|
||||
defaultAmounts: widget.buySellViewModel.defaultAmounts,
|
||||
fiatCurrency: widget.buySellViewModel.fiatCurrency,
|
||||
currentAmount: widget.buySellViewModel.fiatAmount,
|
||||
hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies,
|
||||
onCurrencySelectorPressed: () => selectCryptoCurrency(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 {
|
||||
await widget.buySellViewModel.changeFiatAmount(amount: amount);
|
||||
navigateToProviders(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
class _NewBuySellAmountPageState extends State<NewBuySellAmountPage> {
|
||||
bool _customAmountMode = false;
|
||||
bool _isLoadingPaymentMethods = false;
|
||||
final customInputController = TextEditingController();
|
||||
final customInputFocusNode = FocusNode();
|
||||
|
||||
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,
|
||||
));
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
String get _pageTitle => widget.buySellViewModel.mode == BuySellPageMode.buy
|
||||
? S.current.buy
|
||||
: S.current.sell +
|
||||
((widget.buySellViewModel.cryptoCurrencies.length == 1)
|
||||
? " ${widget.buySellViewModel.cryptoCurrencies.first.fullName}"
|
||||
: "");
|
||||
|
||||
Future<void> navigateToProviders(BuildContext context) async {
|
||||
if (_isLoadingPaymentMethods) {
|
||||
return;
|
||||
// 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.calculateBestRate();
|
||||
when(
|
||||
(_) => widget.buySellViewModel.paymentMethodState is PaymentMethodLoaded,
|
||||
() => widget.buySellViewModel.selectedPaymentMethod = widget.buySellViewModel.paymentMethods
|
||||
.firstWhere((item) => item.paymentMethodType == PaymentType.all));
|
||||
}
|
||||
|
||||
try {
|
||||
setState(() {
|
||||
_isLoadingPaymentMethods = true;
|
||||
});
|
||||
|
||||
await asyncWhen((_) => [PaymentMethodLoaded, PaymentMethodFailed]
|
||||
.contains(widget.buySellViewModel.paymentMethodState.runtimeType));
|
||||
|
||||
if (widget.buySellViewModel.paymentMethodState is PaymentMethodFailed) {
|
||||
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));
|
||||
return;
|
||||
}
|
||||
|
||||
widget.buySellViewModel.selectedPaymentMethod = widget.buySellViewModel.paymentMethods
|
||||
.firstWhere((item) => item.paymentMethodType == PaymentType.all);
|
||||
|
||||
// unawaited because BuySellProviderPage has a nice little "loading rates..." ui thingie
|
||||
unawaited(widget.buySellViewModel.calculateBestRate());
|
||||
|
||||
final page = BuySellProviderPage(buySellViewModel: widget.buySellViewModel);
|
||||
Navigator.of(context).push(CupertinoPageRoute(
|
||||
builder: (context) => Material(color: Colors.transparent, child: page)));
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoadingPaymentMethods = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BuySellCustomAmountInput extends StatelessWidget {
|
||||
const BuySellCustomAmountInput(
|
||||
{super.key,
|
||||
required this.fiatCurrency,
|
||||
required this.cryptoCurrency,
|
||||
required this.cryptoAmount,
|
||||
required this.controller,
|
||||
required this.onContinuePressed,
|
||||
required this.isLoading,
|
||||
required this.onChanged,
|
||||
required this.focusNode,
|
||||
required this.hasCurrencySelector,
|
||||
required this.onCurrencySelectorPressed});
|
||||
|
||||
final FiatCurrency fiatCurrency;
|
||||
final CryptoCurrency cryptoCurrency;
|
||||
final String cryptoAmount;
|
||||
final TextEditingController controller;
|
||||
final FocusNode focusNode;
|
||||
final bool isLoading;
|
||||
final bool hasCurrencySelector;
|
||||
final VoidCallback onCurrencySelectorPressed;
|
||||
final VoidCallback onContinuePressed;
|
||||
final Function(String) onChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox.shrink(),
|
||||
Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
BuySellCurrencyPickerPill(curr: cryptoCurrency, onTap: onCurrencySelectorPressed),
|
||||
SizedBox.shrink(),
|
||||
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),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(18.0),
|
||||
child: NewPrimaryButton(
|
||||
onPressed: onContinuePressed,
|
||||
isLoading: isLoading,
|
||||
text: S.of(context).continue_text,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BuySellDefaultAmountSelector extends StatelessWidget {
|
||||
const BuySellDefaultAmountSelector(
|
||||
{super.key,
|
||||
required this.defaultAmounts,
|
||||
required this.fiatCurrency,
|
||||
required this.mode,
|
||||
required this.onSelected,
|
||||
this.currentAmount,
|
||||
required this.isLoading,
|
||||
required this.hasCurrencySelector,
|
||||
required this.onCurrencySelectorPressed,
|
||||
required this.cryptoCurrency});
|
||||
|
||||
final List<String> defaultAmounts;
|
||||
final String? currentAmount;
|
||||
final bool isLoading;
|
||||
final bool hasCurrencySelector;
|
||||
final VoidCallback onCurrencySelectorPressed;
|
||||
final CryptoCurrency cryptoCurrency;
|
||||
final FiatCurrency fiatCurrency;
|
||||
final BuySellPageMode mode;
|
||||
final Function(String?) onSelected;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
spacing: 24,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
BuySellCurrencyPickerPill(curr: cryptoCurrency, onTap: onCurrencySelectorPressed),
|
||||
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),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
// +1 for "custom" option
|
||||
itemCount: defaultAmounts.length + 1,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 16, mainAxisExtent: 105),
|
||||
itemBuilder: (context, index) {
|
||||
final String? item = index == defaultAmounts.length ? null : defaultAmounts[index];
|
||||
|
||||
return BuySellAmountPill(
|
||||
isLoading: isLoading && item == currentAmount,
|
||||
amount: item == null ? null : Money.parse(item, fiatCurrency),
|
||||
onTap: () => onSelected(item),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BuySellAmountPill extends StatelessWidget {
|
||||
const BuySellAmountPill({super.key, this.amount, required this.onTap, required this.isLoading});
|
||||
|
||||
final Money? amount;
|
||||
final VoidCallback onTap;
|
||||
final bool isLoading;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return 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,
|
||||
),
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(9999999999),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(9999999999),
|
||||
onTap: onTap,
|
||||
child: isLoading
|
||||
? CupertinoActivityIndicator()
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 4,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (amount != null)
|
||||
Text(
|
||||
amount!.toStringWithPrecision(fractionalDigits: 0),
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
|
||||
),
|
||||
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),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BuySellCurrencyPickerPill extends StatelessWidget {
|
||||
const BuySellCurrencyPickerPill({super.key, required this.curr, required this.onTap});
|
||||
|
||||
final CryptoCurrency curr;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(999999999),
|
||||
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,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 5, bottom: 5, left: 5, right: 10),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 10,
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
CakeImageWidget(
|
||||
imageUrl: curr.iconPath,
|
||||
width: 30,
|
||||
height: 30,
|
||||
),
|
||||
Text(
|
||||
curr.fullName ?? curr.title,
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
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),
|
||||
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: Icon(Icons.close),
|
||||
onLeadingPressed: Navigator.of(context, rootNavigator: true).pop,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Observer(
|
||||
builder: (_) => AnimatedSwitcher(
|
||||
duration: 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: ValueKey(0),
|
||||
defaultAmounts: widget.buySellViewModel.defaultAmounts,
|
||||
fiatCurrency: widget.buySellViewModel.fiatCurrency,
|
||||
currentAmount: widget.buySellViewModel.fiatAmount,
|
||||
hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies,
|
||||
onCurrencySelectorPressed: () => selectCryptoCurrency(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 {
|
||||
await widget.buySellViewModel.changeFiatAmount(amount: amount);
|
||||
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,
|
||||
));
|
||||
|
||||
String get _pageTitle => widget.buySellViewModel.mode == BuySellPageMode.buy
|
||||
? S.current.buy
|
||||
: S.current.sell +
|
||||
((widget.buySellViewModel.cryptoCurrencies.length == 1)
|
||||
? " ${widget.buySellViewModel.cryptoCurrencies.first.fullName}"
|
||||
: "");
|
||||
|
||||
Future<void> navigateToProviders(BuildContext context) async {
|
||||
if (_isLoadingPaymentMethods) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setState(() {
|
||||
_isLoadingPaymentMethods = true;
|
||||
});
|
||||
|
||||
await asyncWhen((_) => [PaymentMethodLoaded, PaymentMethodFailed]
|
||||
.contains(widget.buySellViewModel.paymentMethodState.runtimeType));
|
||||
|
||||
if (widget.buySellViewModel.paymentMethodState is PaymentMethodFailed) {
|
||||
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));
|
||||
return;
|
||||
}
|
||||
|
||||
widget.buySellViewModel.selectedPaymentMethod = widget.buySellViewModel.paymentMethods
|
||||
.firstWhere((item) => item.paymentMethodType == PaymentType.all);
|
||||
|
||||
// unawaited because BuySellProviderPage has a nice little "loading rates..." ui thingie
|
||||
unawaited(widget.buySellViewModel.calculateBestRate());
|
||||
|
||||
final page = BuySellProviderPage(buySellViewModel: widget.buySellViewModel);
|
||||
Navigator.of(context).push(CupertinoPageRoute(
|
||||
builder: (context) => Material(color: Colors.transparent, child: page)));
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoadingPaymentMethods = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BuySellCustomAmountInput extends StatelessWidget {
|
||||
const BuySellCustomAmountInput(
|
||||
{super.key,
|
||||
required this.fiatCurrency,
|
||||
required this.cryptoCurrency,
|
||||
required this.cryptoAmount,
|
||||
required this.controller,
|
||||
required this.onContinuePressed,
|
||||
required this.isLoading,
|
||||
required this.onChanged,
|
||||
required this.focusNode,
|
||||
required this.hasCurrencySelector,
|
||||
required this.onCurrencySelectorPressed});
|
||||
|
||||
final FiatCurrency fiatCurrency;
|
||||
final CryptoCurrency cryptoCurrency;
|
||||
final String cryptoAmount;
|
||||
final TextEditingController controller;
|
||||
final FocusNode focusNode;
|
||||
final bool isLoading;
|
||||
final bool hasCurrencySelector;
|
||||
final VoidCallback onCurrencySelectorPressed;
|
||||
final VoidCallback onContinuePressed;
|
||||
final Function(String) onChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox.shrink(),
|
||||
Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
BuySellCurrencyPickerPill(curr: cryptoCurrency, onTap: onCurrencySelectorPressed),
|
||||
SizedBox.shrink(),
|
||||
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),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(18.0),
|
||||
child: NewPrimaryButton(
|
||||
onPressed: onContinuePressed,
|
||||
isLoading: isLoading,
|
||||
text: S.of(context).continue_text,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BuySellDefaultAmountSelector extends StatelessWidget {
|
||||
const BuySellDefaultAmountSelector(
|
||||
{super.key,
|
||||
required this.defaultAmounts,
|
||||
required this.fiatCurrency,
|
||||
required this.mode,
|
||||
required this.onSelected,
|
||||
this.currentAmount,
|
||||
required this.isLoading,
|
||||
required this.hasCurrencySelector,
|
||||
required this.onCurrencySelectorPressed,
|
||||
required this.cryptoCurrency});
|
||||
|
||||
final List<String> defaultAmounts;
|
||||
final String? currentAmount;
|
||||
final bool isLoading;
|
||||
final bool hasCurrencySelector;
|
||||
final VoidCallback onCurrencySelectorPressed;
|
||||
final CryptoCurrency cryptoCurrency;
|
||||
final FiatCurrency fiatCurrency;
|
||||
final BuySellPageMode mode;
|
||||
final Function(String?) onSelected;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
spacing: 24,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
BuySellCurrencyPickerPill(curr: cryptoCurrency, onTap: onCurrencySelectorPressed),
|
||||
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),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
// +1 for "custom" option
|
||||
itemCount: defaultAmounts.length + 1,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 16, mainAxisExtent: 105),
|
||||
itemBuilder: (context, index) {
|
||||
final String? item = index == defaultAmounts.length ? null : defaultAmounts[index];
|
||||
|
||||
return BuySellAmountPill(
|
||||
isLoading: isLoading && item == currentAmount,
|
||||
amount: item == null ? null : Money.parse(item, fiatCurrency),
|
||||
onTap: () => onSelected(item),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BuySellAmountPill extends StatelessWidget {
|
||||
const BuySellAmountPill({super.key, this.amount, required this.onTap, required this.isLoading});
|
||||
|
||||
final Money? amount;
|
||||
final VoidCallback onTap;
|
||||
final bool isLoading;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return 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,
|
||||
),
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(9999999999),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(9999999999),
|
||||
onTap: onTap,
|
||||
child: isLoading
|
||||
? CupertinoActivityIndicator()
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 4,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (amount != null)
|
||||
Text(
|
||||
amount!.toStringWithPrecision(fractionalDigits: 0),
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
|
||||
),
|
||||
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),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BuySellCurrencyPickerPill extends StatelessWidget {
|
||||
const BuySellCurrencyPickerPill({super.key, required this.curr, required this.onTap});
|
||||
|
||||
final CryptoCurrency curr;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(999999999),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 5, bottom: 5, left: 5, right: 10),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 10,
|
||||
children: [
|
||||
CakeImageWidget(
|
||||
imageUrl: curr.iconPath,
|
||||
width: 30,
|
||||
height: 30,
|
||||
),
|
||||
Text(
|
||||
curr.fullName ?? curr.title,
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,10 +48,9 @@ class _BuySellProviderPageState extends State<BuySellProviderPage> {
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
),
|
||||
Expanded(
|
||||
child: Observer(
|
||||
Expanded(child: Observer(
|
||||
builder: (_) {
|
||||
if(widget.buySellViewModel.buySellQuotState is BuySellQuotFailed) {
|
||||
if (widget.buySellViewModel.buySellQuotState is BuySellQuotFailed) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 24,
|
||||
@@ -64,7 +63,8 @@ class _BuySellProviderPageState extends State<BuySellProviderPage> {
|
||||
S.of(context).could_not_load_quotes,
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
|
||||
),
|
||||
Text((widget.buySellViewModel.buySellQuotState as BuySellQuotFailed).errorMessage ??
|
||||
Text((widget.buySellViewModel.buySellQuotState as BuySellQuotFailed)
|
||||
.errorMessage ??
|
||||
S.of(context).please_try_again_later)
|
||||
],
|
||||
)
|
||||
@@ -72,43 +72,56 @@ class _BuySellProviderPageState extends State<BuySellProviderPage> {
|
||||
);
|
||||
}
|
||||
|
||||
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: [
|
||||
CupertinoActivityIndicator(),
|
||||
Text(
|
||||
S.of(context).loading_rates,
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return 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,)));
|
||||
},
|
||||
trailingText: widget.buySellViewModel.selectedPaymentMethod?.title)
|
||||
],
|
||||
S.of(context).available_providers: [
|
||||
...widget.buySellViewModel.sortedRecommendedQuotes.map(quoteListItem),
|
||||
ListItemDropdown(
|
||||
keyValue: "more options",
|
||||
label: S.of(context).more_options,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_allProvidersExpanded = !_allProvidersExpanded;
|
||||
});
|
||||
}),
|
||||
if (_allProvidersExpanded)
|
||||
...widget.buySellViewModel.sortedQuotes.map(quoteListItem)
|
||||
]
|
||||
}),
|
||||
);
|
||||
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,
|
||||
)));
|
||||
},
|
||||
trailingText: widget.buySellViewModel.selectedPaymentMethod?.title)
|
||||
],
|
||||
S.of(context).available_providers: [
|
||||
...widget.buySellViewModel.sortedRecommendedQuotes.map(quoteListItem),
|
||||
ListItemDropdown(
|
||||
keyValue: "more options",
|
||||
label: S.of(context).more_options,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_allProvidersExpanded = !_allProvidersExpanded;
|
||||
});
|
||||
}),
|
||||
if (_allProvidersExpanded)
|
||||
...widget.buySellViewModel.sortedQuotes.map(quoteListItem)
|
||||
]
|
||||
}),
|
||||
);
|
||||
},
|
||||
))
|
||||
],
|
||||
@@ -116,18 +129,19 @@ class _BuySellProviderPageState extends State<BuySellProviderPage> {
|
||||
);
|
||||
}
|
||||
|
||||
String get _pageTitle => (widget.buySellViewModel.mode == BuySellPageMode.buy
|
||||
? S.current.buy
|
||||
: S.current.sell) + " " + (widget.buySellViewModel.cryptoCurrency.fullName ?? "");
|
||||
String get _pageTitle =>
|
||||
(widget.buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy : S.current.sell) +
|
||||
" " +
|
||||
(widget.buySellViewModel.cryptoCurrency.fullName ?? "");
|
||||
|
||||
ListItem quoteListItem(Quote quote) => ListItemRegularRow(
|
||||
keyValue: quote.provider.title,
|
||||
label: quote.rampName ?? quote.provider.title,
|
||||
secondaryLabel: quote.rampName != null? quote.provider.title : null,
|
||||
secondaryLabel: quote.rampName != null ? quote.provider.title : null,
|
||||
subtitle: quote.badges.isEmpty ? null : quote.badges.join(" - "),
|
||||
subtitleColor: Theme.of(context).colorScheme.primary,
|
||||
iconPath: quote.darkIconPath,
|
||||
onTap: (){
|
||||
onTap: () {
|
||||
widget.buySellViewModel.changeOption(quote);
|
||||
navigateToConfirmation(context);
|
||||
},
|
||||
@@ -137,14 +151,21 @@ class _BuySellProviderPageState extends State<BuySellProviderPage> {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
Text(widget.buySellViewModel.amountForQuote(quote).toStringWithSymbol(fractionalDigits: 8)),
|
||||
Text("= ${widget.buySellViewModel.fiatAmountForQuote(quote).toStringWithSymbol(fractionalDigits: 2, trimZeros: false)}", style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant))
|
||||
|
||||
],));
|
||||
Text(widget.buySellViewModel
|
||||
.amountForQuote(quote)
|
||||
.toStringWithSymbol(fractionalDigits: 8)),
|
||||
Text(
|
||||
"= ${widget.buySellViewModel.fiatAmountForQuote(quote).toStringWithSymbol(fractionalDigits: 2, trimZeros: false)}",
|
||||
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(builder: (context)=>Material(color: Colors.transparent,child: page,)));
|
||||
Navigator.of(context).push(CupertinoPageRoute(
|
||||
builder: (context) => Material(
|
||||
color: Colors.transparent,
|
||||
child: page,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,34 +14,51 @@ class BuySellSelectorModal extends StatelessWidget {
|
||||
@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,
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: Column(spacing:24, mainAxisSize: MainAxisSize.min, children: [
|
||||
SizedBox.shrink(),
|
||||
Text(S.of(context).buy_or_sell, style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),),
|
||||
Text(S.of(context).buy_or_sell_desc, style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant),),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: Column(spacing: 12,children: [
|
||||
BuySellSelectorModalButton(title: S.of(context).buy_crypto, description: S.of(context).buy_crypto_desc, iconPath: "assets/new-ui/plus.svg", 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))
|
||||
],),
|
||||
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,
|
||||
),
|
||||
SizedBox.shrink()
|
||||
])
|
||||
)
|
||||
);
|
||||
),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: Column(spacing: 24, mainAxisSize: MainAxisSize.min, children: [
|
||||
SizedBox.shrink(),
|
||||
Text(
|
||||
S.of(context).buy_or_sell,
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
|
||||
),
|
||||
Text(
|
||||
S.of(context).buy_or_sell_desc,
|
||||
style:
|
||||
TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
BuySellSelectorModalButton(
|
||||
title: S.of(context).buy_crypto,
|
||||
description: S.of(context).buy_crypto_desc,
|
||||
iconPath: "assets/new-ui/plus.svg",
|
||||
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))
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox.shrink()
|
||||
])));
|
||||
}
|
||||
|
||||
void openBuySellPage(BuildContext context, BuySellPageMode mode) {
|
||||
@@ -58,9 +75,13 @@ class BuySellSelectorModal extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BuySellSelectorModalButton extends StatelessWidget {
|
||||
const BuySellSelectorModalButton({super.key, required this.title, required this.description, required this.iconPath, required this.onTap});
|
||||
const BuySellSelectorModalButton(
|
||||
{super.key,
|
||||
required this.title,
|
||||
required this.description,
|
||||
required this.iconPath,
|
||||
required this.onTap});
|
||||
|
||||
final String title;
|
||||
final String description;
|
||||
@@ -75,8 +96,8 @@ class BuySellSelectorModalButton extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
width: 1, color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
|
||||
width: 1,
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
@@ -87,16 +108,36 @@ class BuySellSelectorModalButton extends StatelessWidget {
|
||||
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),),
|
||||
Text(description, style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant),)
|
||||
],)
|
||||
|
||||
],),),
|
||||
|
||||
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),
|
||||
),
|
||||
Text(
|
||||
description,
|
||||
style: TextStyle(
|
||||
fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -179,7 +179,8 @@ class _CardsViewState extends State<CardsView> {
|
||||
icon: Icons.arrow_forward_ios_rounded,
|
||||
iconSize: 12,
|
||||
onTap: () {
|
||||
showModalBottomSheet(context: context, builder: (context)=>BuySellSelectorModal());
|
||||
showModalBottomSheet(
|
||||
context: context, builder: (context) => BuySellSelectorModal());
|
||||
},
|
||||
)
|
||||
]
|
||||
|
||||
@@ -3,7 +3,14 @@ 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(
|
||||
{super.key,
|
||||
required this.currency,
|
||||
required this.controller,
|
||||
this.focusNode,
|
||||
this.inputFormatters,
|
||||
this.onChanged,
|
||||
this.validator});
|
||||
|
||||
final Currency currency;
|
||||
final TextEditingController controller;
|
||||
@@ -28,7 +35,7 @@ class _FloatingAmountInputState extends State<FloatingAmountInput> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
return Center(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
@@ -59,19 +66,17 @@ class _FloatingAmountInputState extends State<FloatingAmountInput> {
|
||||
hoverColor: Colors.transparent,
|
||||
focusedBorder: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
hintText: _amountFocused || widget.controller.text.isNotEmpty
|
||||
? null
|
||||
: "0.00",
|
||||
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,
|
||||
),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
style: Theme.of(context).textTheme.displayMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 45,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 45,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
@@ -80,10 +85,10 @@ class _FloatingAmountInputState extends State<FloatingAmountInput> {
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.displayMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 45,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 45,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -118,7 +118,7 @@ class ExceptionHandler {
|
||||
|
||||
if (kDebugMode || kProfileMode) {
|
||||
if (_ignoreError(errorDetails.exception.toString()) ||
|
||||
_ignoreError(errorDetails.stack.toString())) {
|
||||
_ignoreError(errorDetails.stack.toString()) || _flutterErrorIgnore(errorDetails)) {
|
||||
printV("(BELOW ERROR IS IGNORED AND WILL NOT TRIGGER POPUP IN PROD)");
|
||||
}
|
||||
FlutterError.presentError(errorDetails);
|
||||
|
||||
@@ -174,10 +174,15 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
|
||||
return maxAmount.toStringAsFixed(2);
|
||||
}
|
||||
|
||||
Money amountForQuote(Quote quote) => Money.parse(double.parse(fiatAmount)/quote.rate, cryptoCurrency);
|
||||
Money amountForQuote(Quote quote) =>
|
||||
Money.parse(double.parse(fiatAmount) / quote.rate, cryptoCurrency);
|
||||
|
||||
Money fiatAmountForQuote(Quote quote) {
|
||||
return Money.parse((fiatConversionStore.prices[cryptoCurrency]! * double.parse(amountForQuote(quote).toString())).toStringAsFixed(2), fiatCurrency);
|
||||
return Money.parse(
|
||||
(fiatConversionStore.prices[cryptoCurrency]! *
|
||||
double.parse(amountForQuote(quote).toString()))
|
||||
.toStringAsFixed(2),
|
||||
fiatCurrency);
|
||||
}
|
||||
|
||||
// based on usd values, should have roughly equal worth (was done with ai though so it's subject to correction)
|
||||
|
||||
Reference in New Issue
Block a user