From c746eb88651bcea23b31b04127441db1c49451ec Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 11 Jan 2023 09:54:39 -0600 Subject: [PATCH] 23-1-10 updates nonatomic updates --- lib/hive/db.dart | 1 + lib/models/buy/response_objects/buy.dart | 46 +--- lib/pages/buy_view/buy_form.dart | 211 ++++++++------- lib/pages/buy_view/buy_view.dart | 159 +----------- .../sub_widgets/wallet_navigation_bar.dart | 2 +- lib/providers/providers.dart | 2 + lib/services/buy/buy_response.dart | 24 ++ lib/services/buys_service.dart | 18 +- lib/widgets/textfields/buy_textfield.dart | 242 ++++++++++++++++++ 9 files changed, 410 insertions(+), 295 deletions(-) create mode 100644 lib/services/buy/buy_response.dart create mode 100644 lib/widgets/textfields/buy_textfield.dart diff --git a/lib/hive/db.dart b/lib/hive/db.dart index 557f4ef30..cc6422ed9 100644 --- a/lib/hive/db.dart +++ b/lib/hive/db.dart @@ -33,6 +33,7 @@ class DB { static const String boxNameDBInfo = "dbInfo"; static const String boxNameTheme = "theme"; static const String boxNameDesktopData = "desktopData"; + static const String boxNameBuys = "buysBox"; String boxNameTxCache({required Coin coin}) => "${coin.name}_txCache"; String boxNameSetCache({required Coin coin}) => diff --git a/lib/models/buy/response_objects/buy.dart b/lib/models/buy/response_objects/buy.dart index 4317faf99..6c074ecf4 100644 --- a/lib/models/buy/response_objects/buy.dart +++ b/lib/models/buy/response_objects/buy.dart @@ -1,6 +1,6 @@ import 'package:hive/hive.dart'; -part 'buy.g.dart'; +// part 'buy.g.dart'; @HiveType(typeId: Buy.typeId) class Buy { @@ -10,7 +10,7 @@ class Buy { final String uuid; @HiveField(1) - final String tradeId; + final String buyId; @HiveField(2) final String rateType; @@ -74,7 +74,7 @@ class Buy { const Buy({ required this.uuid, - required this.tradeId, + required this.buyId, required this.rateType, required this.direction, required this.timestamp, @@ -97,8 +97,8 @@ class Buy { required this.exchangeName, }); - Trade copyWith({ - String? tradeId, + Buy copyWith({ + String? buyId, String? rateType, String? direction, DateTime? timestamp, @@ -122,7 +122,7 @@ class Buy { }) { return Buy( uuid: uuid, - tradeId: tradeId ?? this.tradeId, + buyId: buyId ?? this.buyId, rateType: rateType ?? this.rateType, direction: direction ?? this.direction, timestamp: timestamp ?? this.timestamp, @@ -149,7 +149,7 @@ class Buy { Map toMap() { return { "uuid": uuid, - "tradeId": tradeId, + "buyId": buyId, "rateType": rateType, "direction": direction, "timestamp": timestamp.toIso8601String(), @@ -176,7 +176,7 @@ class Buy { factory Buy.fromMap(Map map) { return Buy( uuid: map["uuid"] as String, - tradeId: map["tradeId"] as String, + buyId: map["buyId"] as String, rateType: map["rateType"] as String, direction: map["direction"] as String, timestamp: DateTime.parse(map["timestamp"] as String), @@ -200,36 +200,6 @@ class Buy { ); } - // factory Trade.fromExchangeTransaction( - // ExchangeTransaction exTx, bool reversed) { - // return Buy( - // uuid: exTx.uuid, - // tradeId: exTx.id, - // rateType: "", - // direction: reversed ? "reverse" : "direct", - // timestamp: exTx.date, - // updatedAt: DateTime.tryParse(exTx.statusObject!.updatedAt) ?? exTx.date, - // payInCurrency: exTx.fromCurrency, - // payInAmount: exTx.statusObject!.amountSendDecimal.isEmpty - // ? exTx.statusObject!.expectedSendAmountDecimal - // : exTx.statusObject!.amountSendDecimal, - // payInAddress: exTx.payinAddress, - // payInNetwork: "", - // payInExtraId: exTx.payinExtraId, - // payInTxid: exTx.statusObject!.payinHash, - // payOutCurrency: exTx.toCurrency, - // payOutAmount: exTx.amount, - // payOutAddress: exTx.payoutAddress, - // payOutNetwork: "", - // payOutExtraId: exTx.payoutExtraId, - // payOutTxid: exTx.statusObject!.payoutHash, - // refundAddress: exTx.refundAddress, - // refundExtraId: exTx.refundExtraId, - // status: exTx.statusObject!.status.name, - // exchangeName: ChangeNowExchange.exchangeName, - // ); - // } - @override String toString() { return toMap().toString(); diff --git a/lib/pages/buy_view/buy_form.dart b/lib/pages/buy_view/buy_form.dart index 31515815a..35f5d3d7b 100644 --- a/lib/pages/buy_view/buy_form.dart +++ b/lib/pages/buy_view/buy_form.dart @@ -1,16 +1,13 @@ import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:stackwallet/providers/buy/buy_form_state_provider.dart'; -import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/providers/providers.dart'; +import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/util.dart'; -import 'package:stackwallet/widgets/conditional_parent.dart'; -import 'package:stackwallet/widgets/rounded_container.dart'; +import 'package:stackwallet/widgets/textfields/buy_textfield.dart'; class BuyForm extends ConsumerStatefulWidget { const BuyForm({ @@ -30,7 +27,62 @@ class _BuyFormState extends ConsumerState { late final String? walletId; late final Coin? coin; + late final TextEditingController _fiatController; + late final TextEditingController _cryptoController; final isDesktop = Util.isDesktop; + final FocusNode _fiatFocusNode = FocusNode(); + final FocusNode _cryptoFocusNode = FocusNode(); + + void fiatFieldOnChanged(String value) async { + if (_fiatFocusNode.hasFocus) { + final newFromAmount = Decimal.tryParse(value); + + await ref.read(buyFormStateProvider).setFromAmountAndCalculateToAmount( + newFromAmount ?? Decimal.zero, true); + + if (newFromAmount == null) { + // _cryptoController.text = + // ref.read(prefsChangeNotifierProvider).exchangeRateType == + // ExchangeRateType.estimated + // ? "-" + // : ""; + } + } + } + + void selectFiatCurrency() async { + // await Future.delayed(const Duration(milliseconds: 300)); + // + // Navigator.of(context, rootNavigator: true).pop(); + } + @override + void initState() { + _fiatController = TextEditingController(); + _cryptoController = TextEditingController(); + + walletId = widget.walletId; + coin = widget.coin; + // walletInitiated = walletId != null && coin != null; + // + // if (walletInitiated) { + // WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + // ref.read(buyFormStateProvider).clearAmounts(true); + // // ref.read(fixedRateExchangeFormProvider); + // }); + // } else { + // final isEstimated = + // ref.read(prefsChangeNotifierProvider).exchangeRateType == + // ExchangeRateType.estimated; + _fiatController.text = ref.read(buyFormStateProvider).fromAmountString; + _cryptoController + .text = /*isEstimated + ? "-" //ref.read(estimatedRateExchangeFormProvider).toAmountString + :*/ + ref.read(buyFormStateProvider).toAmountString; + // } + + super.initState(); + } @override void dispose() { @@ -46,7 +98,7 @@ class _BuyFormState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( - "You will send", + "I want to buy", style: STextStyles.itemSubtitle(context).copyWith( color: Theme.of(context).extension()!.textDark3, ), @@ -54,30 +106,6 @@ class _BuyFormState extends ConsumerState { SizedBox( height: isDesktop ? 10 : 4, ), - // ExchangeTextField( - // controller: _sendController, - // focusNode: _sendFocusNode, - // textStyle: STextStyles.smallMed14(context).copyWith( - // color: Theme.of(context).extension()!.textDark, - // ), - // buttonColor: - // Theme.of(context).extension()!.buttonBackSecondary, - // borderRadius: Constants.size.circularBorderRadius, - // background: - // Theme.of(context).extension()!.textFieldDefaultBG, - // onTap: () { - // if (_sendController.text == "-") { - // _sendController.text = ""; - // } - // }, - // onChanged: sendFieldOnChanged, - // onButtonTap: selectSendCurrency, - // isWalletCoin: isWalletCoin(coin, true), - // image: _fetchIconUrlFromTicker(ref.watch( - // buyFormStateProvider.select((value) => value.fromTicker))), - // ticker: ref.watch( - // buyFormStateProvider.select((value) => value.fromTicker)), - // ), SizedBox( height: isDesktop ? 10 : 4, ), @@ -97,48 +125,16 @@ class _BuyFormState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "You will receive", + "I want to pay with", style: STextStyles.itemSubtitle(context).copyWith( color: Theme.of(context).extension()!.textDark3, ), ), - ConditionalParent( - condition: isDesktop, - builder: (child) => MouseRegion( - cursor: SystemMouseCursors.click, - child: child, - ), - child: RoundedContainer( - padding: isDesktop - ? const EdgeInsets.all(6) - : const EdgeInsets.all(2), - color: Theme.of(context) - .extension()! - .buttonBackSecondary, - radiusMultiplier: 0.75, - child: GestureDetector( - // onTap: () async { - // await _swap(); - // }, - child: Padding( - padding: const EdgeInsets.all(4), - child: SvgPicture.asset( - Assets.svg.swap, - width: 20, - height: 20, - color: Theme.of(context) - .extension()! - .accentColorDark, - ), - ), - ), - ), - ), ], ), - SizedBox( - height: isDesktop ? 10 : 7, - ), + // SizedBox( + // height: isDesktop ? 10 : 7, + // ), // ExchangeTextField( // focusNode: _receiveFocusNode, // controller: _receiveController, @@ -188,39 +184,64 @@ class _BuyFormState extends ConsumerState { // onChanged: onRateTypeChanged, // ), // ), - // these reads should be watch - if (ref.watch(buyFormStateProvider).fromAmount != null && - ref.watch(buyFormStateProvider).fromAmount != Decimal.zero) - SizedBox( - height: isDesktop ? 20 : 12, - ), - // these reads should be watch + Row( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Enter amount", + style: STextStyles.itemSubtitle(context).copyWith( + color: Theme.of(context).extension()!.textDark3, + ), + ), + ], + ), + // // these reads should be watch // if (ref.watch(buyFormStateProvider).fromAmount != null && // ref.watch(buyFormStateProvider).fromAmount != Decimal.zero) - // ExchangeProviderOptions( - // from: ref.watch(buyFormStateProvider).fromTicker, - // to: ref.watch(buyFormStateProvider).toTicker, - // fromAmount: ref.watch(buyFormStateProvider).fromAmount, - // toAmount: ref.watch(buyFormStateProvider).toAmount, - // fixedRate: ref.watch(prefsChangeNotifierProvider - // .select((value) => value.exchangeRateType)) == - // ExchangeRateType.fixed, - // reversed: ref - // .watch(buyFormStateProvider.select((value) => value.reversed)), - // ), SizedBox( height: isDesktop ? 20 : 12, ), - // PrimaryButton( - // buttonHeight: isDesktop ? ButtonHeight.l : null, - // enabled: ref - // .watch(buyFormStateProvider.select((value) => value.canExchange)), - // // onPressed: ref.watch(buyFormStateProvider - // // .select((value) => value.canExchange)) - // // ? onExchangePressed - // // : null, - // label: "Exchange", - // ) + BuyTextField( + controller: _fiatController, + focusNode: _fiatFocusNode, + textStyle: STextStyles.smallMed14(context).copyWith( + color: Theme.of(context).extension()!.textDark, + ), + buttonColor: + Theme.of(context).extension()!.buttonBackSecondary, + borderRadius: Constants.size.circularBorderRadius, + background: + Theme.of(context).extension()!.textFieldDefaultBG, + onTap: () { + if (_fiatController.text == "-") { + _fiatController.text = ""; + } + }, + onChanged: fiatFieldOnChanged, + onButtonTap: selectFiatCurrency, + // isWalletCoin: isWalletCoin(coin, true), + isWalletCoin: false, + // image: _fetchIconUrlFromTicker(ref + // .watch(buyFormStateProvider.select((value) => value.fromTicker))), + // ticker: ref + // .watch(buyFormStateProvider.select((value) => value.fromTicker)), + ), + SizedBox( + height: isDesktop ? 20 : 12, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Enter receiving address", + style: STextStyles.itemSubtitle(context).copyWith( + color: Theme.of(context).extension()!.textDark3, + ), + ), + ], + ), ], ); } diff --git a/lib/pages/buy_view/buy_view.dart b/lib/pages/buy_view/buy_view.dart index 32cb38082..164346e76 100644 --- a/lib/pages/buy_view/buy_view.dart +++ b/lib/pages/buy_view/buy_view.dart @@ -1,8 +1,5 @@ import 'package:flutter/material.dart'; import 'package:stackwallet/pages/buy_view/buy_form.dart'; -import 'package:stackwallet/utilities/constants.dart'; -import 'package:stackwallet/utilities/text_styles.dart'; -import 'package:stackwallet/utilities/theme/stack_colors.dart'; class BuyView extends StatefulWidget { const BuyView({Key? key}) : super(key: key); @@ -18,155 +15,13 @@ class _BuyViewState extends State { // debugPrint("BUILD: BuyView"); return SafeArea( - child: NestedScrollView( - floatHeaderSlivers: true, - headerSliverBuilder: (context, innerBoxIsScrolled) { - return [ - SliverOverlapAbsorber( - handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), - sliver: const SliverToBoxAdapter( - child: Padding( - padding: EdgeInsets.only( - left: 16, - right: 16, - top: 16, - ), - child: BuyForm(), - ), - ), - ) - ]; - }, - body: Builder( - builder: (buildContext) { - // final buys = - // ref.watch(buysServiceProvider.select((value) => value.buys)); - // final buyCount = buys.length; - // final hasHistory = buyCount > 0; - const hasHistory = false; - - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: CustomScrollView( - slivers: [ - SliverOverlapInjector( - handle: NestedScrollView.sliverOverlapAbsorberHandleFor( - buildContext, - ), - ), - SliverToBoxAdapter( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const SizedBox( - height: 12, - ), - Text( - "Trades", - style: STextStyles.itemSubtitle(context).copyWith( - color: Theme.of(context) - .extension()! - .textDark3, - ), - ), - const SizedBox( - height: 12, - ), - ], - ), - ), - ), - // if (hasHistory) - // SliverList( - // delegate: SliverChildBuilderDelegate((context, index) { - // return Padding( - // padding: const EdgeInsets.all(4), - // child: TradeCard( - // key: Key("tradeCard_${trades[index].uuid}"), - // trade: trades[index], - // onTap: () async { - // final String tradeId = trades[index].tradeId; - // - // final lookup = ref - // .read(tradeSentFromStackLookupProvider) - // .all; - // - // //todo: check if print needed - // // debugPrint("ALL: $lookup"); - // - // final String? txid = ref - // .read(tradeSentFromStackLookupProvider) - // .getTxidForTradeId(tradeId); - // final List? walletIds = ref - // .read(tradeSentFromStackLookupProvider) - // .getWalletIdsForTradeId(tradeId); - // - // if (txid != null && - // walletIds != null && - // walletIds.isNotEmpty) { - // final manager = ref - // .read(walletsChangeNotifierProvider) - // .getManager(walletIds.first); - // - // //todo: check if print needed - // // debugPrint("name: ${manager.walletName}"); - // - // // TODO store tx data completely locally in isar so we don't lock up ui here when querying txData - // final txData = await manager.transactionData; - // - // final tx = txData.getAllTransactions()[txid]; - // - // if (mounted) { - // unawaited(Navigator.of(context).pushNamed( - // TradeDetailsView.routeName, - // arguments: Tuple4(tradeId, tx, - // walletIds.first, manager.walletName), - // )); - // } - // } else { - // unawaited(Navigator.of(context).pushNamed( - // TradeDetailsView.routeName, - // arguments: Tuple4( - // tradeId, null, walletIds?.first, null), - // )); - // } - // }, - // ), - // ); - // }, childCount: tradeCount), - // ), - if (!hasHistory) - SliverToBoxAdapter( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Container( - decoration: BoxDecoration( - color: Theme.of(context) - .extension()! - .popupBG, - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - ), - child: Padding( - padding: const EdgeInsets.all(12), - child: Text( - "Trades will appear here", - textAlign: TextAlign.center, - style: STextStyles.itemSubtitle(context), - ), - ), - ), - ), - ), - ], - ), - ); - }, - ), + child: Padding( + padding: EdgeInsets.only( + left: 16, + right: 16, + top: 16, ), - ); + child: BuyForm(), + )); } } diff --git a/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart b/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart index 27601a361..c191d2616 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart @@ -222,7 +222,7 @@ class WalletNavigationBar extends StatelessWidget { height: 4, ), Text( - "Buy2", + "Buy", style: STextStyles.buttonSmall(context), ), Spacer(), diff --git a/lib/providers/providers.dart b/lib/providers/providers.dart index 5cef38830..8f3f2fbfd 100644 --- a/lib/providers/providers.dart +++ b/lib/providers/providers.dart @@ -1,3 +1,4 @@ +export './buy/buy_form_state_provider.dart'; export './exchange/available_changenow_currencies_provider.dart'; export './exchange/available_simpleswap_currencies_provider.dart'; export './exchange/changenow_initial_load_status.dart'; @@ -8,6 +9,7 @@ export './exchange/exchange_provider.dart'; export './exchange/exchange_send_from_wallet_id_provider.dart'; export './exchange/trade_note_service_provider.dart'; export './exchange/trade_sent_from_stack_lookup_provider.dart'; +export './global/buys_service_provider.dart'; export './global/favorites_provider.dart'; export './global/locale_provider.dart'; export './global/node_service_provider.dart'; diff --git a/lib/services/buy/buy_response.dart b/lib/services/buy/buy_response.dart new file mode 100644 index 000000000..59441fb9b --- /dev/null +++ b/lib/services/buy/buy_response.dart @@ -0,0 +1,24 @@ +enum ExchangeExceptionType { generic, serializeResponseError } + +class ExchangeException implements Exception { + String errorMessage; + ExchangeExceptionType type; + ExchangeException(this.errorMessage, this.type); + + @override + String toString() { + return errorMessage; + } +} + +class ExchangeResponse { + late final T? value; + late final ExchangeException? exception; + + ExchangeResponse({this.value, this.exception}); + + @override + String toString() { + return "{error: $exception, value: $value}"; + } +} diff --git a/lib/services/buys_service.dart b/lib/services/buys_service.dart index f8d4d4fdc..291d9acc5 100644 --- a/lib/services/buys_service.dart +++ b/lib/services/buys_service.dart @@ -11,22 +11,22 @@ class BuysService extends ChangeNotifier { return list; } - Buy? get(String BuyId) { + Buy? get(String buyId) { try { return DB.instance .values(boxName: DB.boxNameBuys) - .firstWhere((e) => e.BuyId == BuyId); + .firstWhere((e) => e.buyId == buyId); } catch (_) { return null; } } Future add({ - required Buy Buy, + required Buy buy, required bool shouldNotifyListeners, }) async { await DB.instance - .put(boxName: DB.boxNameBuys, key: Buy.uuid, value: Buy); + .put(boxName: DB.boxNameBuys, key: buy.uuid, value: buy); if (shouldNotifyListeners) { notifyListeners(); @@ -34,23 +34,23 @@ class BuysService extends ChangeNotifier { } Future edit({ - required Buy Buy, + required Buy buy, required bool shouldNotifyListeners, }) async { - if (DB.instance.get(boxName: DB.boxNameBuys, key: Buy.uuid) == null) { + if (DB.instance.get(boxName: DB.boxNameBuys, key: buy.uuid) == null) { throw Exception("Attempted to edit a Buy that does not exist in Hive!"); } // add overwrites so this edit function is just a wrapper with an extra check - await add(Buy: Buy, shouldNotifyListeners: shouldNotifyListeners); + await add(buy: buy, shouldNotifyListeners: shouldNotifyListeners); } Future delete({ - required Buy Buy, + required Buy buy, required bool shouldNotifyListeners, }) async { await deleteByUuid( - uuid: Buy.uuid, shouldNotifyListeners: shouldNotifyListeners); + uuid: buy.uuid, shouldNotifyListeners: shouldNotifyListeners); } Future deleteByUuid({ diff --git a/lib/widgets/textfields/buy_textfield.dart b/lib/widgets/textfields/buy_textfield.dart new file mode 100644 index 000000000..461708ef8 --- /dev/null +++ b/lib/widgets/textfields/buy_textfield.dart @@ -0,0 +1,242 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/loading_indicator.dart'; + +class BuyTextField extends StatefulWidget { + const BuyTextField({ + Key? key, + this.borderRadius = 0, + this.background, + required this.controller, + this.buttonColor, + required this.focusNode, + this.buttonContent, + required this.textStyle, + this.onButtonTap, + this.onChanged, + this.onSubmitted, + this.onTap, + required this.isWalletCoin, + this.image, + this.ticker, + this.readOnly = false, + }) : super(key: key); + + final double borderRadius; + final Color? background; + final Color? buttonColor; + final Widget? buttonContent; + final TextEditingController controller; + final FocusNode focusNode; + final TextStyle textStyle; + final VoidCallback? onTap; + final VoidCallback? onButtonTap; + final void Function(String)? onChanged; + final void Function(String)? onSubmitted; + + final bool isWalletCoin; + final bool readOnly; + final String? image; + final String? ticker; + + @override + State createState() => _BuyTextFieldState(); +} + +class _BuyTextFieldState extends State { + late final TextEditingController controller; + late final FocusNode focusNode; + late final TextStyle textStyle; + + late final double borderRadius; + + late final Color? background; + late final Color? buttonColor; + late final Widget? buttonContent; + late final VoidCallback? onButtonTap; + late final VoidCallback? onTap; + late final void Function(String)? onChanged; + late final void Function(String)? onSubmitted; + + final isDesktop = Util.isDesktop; + + @override + void initState() { + borderRadius = widget.borderRadius; + background = widget.background; + buttonColor = widget.buttonColor; + controller = widget.controller; + focusNode = widget.focusNode; + buttonContent = widget.buttonContent; + textStyle = widget.textStyle; + onButtonTap = widget.onButtonTap; + onChanged = widget.onChanged; + onSubmitted = widget.onSubmitted; + onTap = widget.onTap; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: background, + borderRadius: BorderRadius.circular(borderRadius), + ), + child: IntrinsicHeight( + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: TextField( + style: textStyle, + controller: controller, + focusNode: focusNode, + onChanged: onChanged, + onTap: onTap, + enableSuggestions: false, + autocorrect: false, + readOnly: widget.readOnly, + keyboardType: isDesktop + ? null + : const TextInputType.numberWithOptions( + signed: false, + decimal: true, + ), + decoration: InputDecoration( + contentPadding: const EdgeInsets.only( + top: 12, + left: 12, + ), + hintText: "0", + hintStyle: STextStyles.fieldLabel(context).copyWith( + fontSize: 14, + ), + ), + inputFormatters: [ + // regex to validate a crypto amount with 8 decimal places + TextInputFormatter.withFunction((oldValue, newValue) => + RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$') + .hasMatch(newValue.text) + ? newValue + : oldValue), + ], + ), + ), + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () => onButtonTap?.call(), + child: Container( + decoration: BoxDecoration( + color: buttonColor, + borderRadius: BorderRadius.horizontal( + right: Radius.circular( + borderRadius, + ), + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + ), + child: Row( + children: [ + Container( + width: 18, + height: 18, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(18), + ), + child: Builder( + builder: (context) { + final image = widget.image; + + if (image != null && image.isNotEmpty) { + return Center( + child: SvgPicture.network( + image, + height: 18, + placeholderBuilder: (_) => Container( + width: 18, + height: 18, + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + borderRadius: BorderRadius.circular( + 18, + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular( + 18, + ), + child: const LoadingIndicator(), + ), + ), + ), + ); + } else { + return Container( + width: 18, + height: 18, + decoration: BoxDecoration( + // color: Theme.of(context).extension()!.accentColorDark + borderRadius: BorderRadius.circular(18), + ), + child: SvgPicture.asset( + Assets.svg.circleQuestion, + width: 18, + height: 18, + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + ), + ); + } + }, + ), + ), + const SizedBox( + width: 6, + ), + Text( + widget.ticker?.toUpperCase() ?? "-", + style: STextStyles.smallMed14(context).copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ), + ), + if (!widget.isWalletCoin) + const SizedBox( + width: 6, + ), + if (!widget.isWalletCoin) + SvgPicture.asset( + Assets.svg.chevronDown, + width: 5, + height: 2.5, + color: Theme.of(context) + .extension()! + .textDark, + ), + ], + ), + ), + ), + ), + ), + ], + ), + ), + ); + } +}