mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-03-23 07:38:49 +00:00
add fiat selection view
This commit is contained in:
parent
9724b40848
commit
2d6447eedd
4 changed files with 644 additions and 23 deletions
123
lib/models/buy/response_objects/fiat.dart
Normal file
123
lib/models/buy/response_objects/fiat.dart
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
class Fiat {
|
||||||
|
/// Fiat ticker
|
||||||
|
final String ticker;
|
||||||
|
|
||||||
|
/// Fiat name
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// Fiat network
|
||||||
|
final String network;
|
||||||
|
|
||||||
|
/// Fiat logo url
|
||||||
|
final String image;
|
||||||
|
|
||||||
|
/// Indicates if a currency has an Extra ID
|
||||||
|
final bool hasExternalId;
|
||||||
|
|
||||||
|
/// external id if it exists
|
||||||
|
final String? externalId;
|
||||||
|
|
||||||
|
/// Indicates if a currency is a fiat currency (EUR, USD)
|
||||||
|
final bool isFiat;
|
||||||
|
|
||||||
|
/// Indicates if a currency is popular
|
||||||
|
final bool featured;
|
||||||
|
|
||||||
|
/// Indicates if a currency is stable
|
||||||
|
final bool isStable;
|
||||||
|
|
||||||
|
/// Indicates if a currency is available on a fixed-rate flow
|
||||||
|
final bool supportsFixedRate;
|
||||||
|
|
||||||
|
/// (Optional - based on api call) Indicates whether the pair is
|
||||||
|
/// currently supported by change now
|
||||||
|
final bool? isAvailable;
|
||||||
|
|
||||||
|
Fiat({
|
||||||
|
required this.ticker,
|
||||||
|
required this.name,
|
||||||
|
required this.network,
|
||||||
|
required this.image,
|
||||||
|
required this.hasExternalId,
|
||||||
|
this.externalId,
|
||||||
|
required this.isFiat,
|
||||||
|
required this.featured,
|
||||||
|
required this.isStable,
|
||||||
|
required this.supportsFixedRate,
|
||||||
|
this.isAvailable,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Fiat.fromJson(Map<String, dynamic> json) {
|
||||||
|
try {
|
||||||
|
return Fiat(
|
||||||
|
ticker: json["ticker"] as String,
|
||||||
|
name: json["name"] as String,
|
||||||
|
network: json["network"] as String? ?? "",
|
||||||
|
image: json["image"] as String,
|
||||||
|
hasExternalId: json["hasExternalId"] as bool,
|
||||||
|
externalId: json["externalId"] as String?,
|
||||||
|
isFiat: json["isFiat"] as bool,
|
||||||
|
featured: json["featured"] as bool,
|
||||||
|
isStable: json["isStable"] as bool,
|
||||||
|
supportsFixedRate: json["supportsFixedRate"] as bool,
|
||||||
|
isAvailable: json["isAvailable"] as bool?,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final map = {
|
||||||
|
"ticker": ticker,
|
||||||
|
"name": name,
|
||||||
|
"network": network,
|
||||||
|
"image": image,
|
||||||
|
"hasExternalId": hasExternalId,
|
||||||
|
"externalId": externalId,
|
||||||
|
"isFiat": isFiat,
|
||||||
|
"featured": featured,
|
||||||
|
"isStable": isStable,
|
||||||
|
"supportsFixedRate": supportsFixedRate,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isAvailable != null) {
|
||||||
|
map["isAvailable"] = isAvailable!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
Fiat copyWith({
|
||||||
|
String? ticker,
|
||||||
|
String? name,
|
||||||
|
String? network,
|
||||||
|
String? image,
|
||||||
|
bool? hasExternalId,
|
||||||
|
String? externalId,
|
||||||
|
bool? isFiat,
|
||||||
|
bool? featured,
|
||||||
|
bool? isStable,
|
||||||
|
bool? supportsFixedRate,
|
||||||
|
bool? isAvailable,
|
||||||
|
}) {
|
||||||
|
return Fiat(
|
||||||
|
ticker: ticker ?? this.ticker,
|
||||||
|
name: name ?? this.name,
|
||||||
|
network: network ?? this.network,
|
||||||
|
image: image ?? this.image,
|
||||||
|
hasExternalId: hasExternalId ?? this.hasExternalId,
|
||||||
|
externalId: externalId ?? this.externalId,
|
||||||
|
isFiat: isFiat ?? this.isFiat,
|
||||||
|
featured: featured ?? this.featured,
|
||||||
|
isStable: isStable ?? this.isStable,
|
||||||
|
supportsFixedRate: supportsFixedRate ?? this.supportsFixedRate,
|
||||||
|
isAvailable: isAvailable ?? this.isAvailable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return "Fiat: ${toJson()}";
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,9 +2,11 @@ import 'package:decimal/decimal.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/models/buy/response_objects/crypto.dart';
|
import 'package:stackwallet/models/buy/response_objects/crypto.dart';
|
||||||
|
import 'package:stackwallet/models/buy/response_objects/fiat.dart';
|
||||||
import 'package:stackwallet/models/buy/response_objects/pair.dart';
|
import 'package:stackwallet/models/buy/response_objects/pair.dart';
|
||||||
import 'package:stackwallet/pages/buy_view/buy_coin_selection/crypto_selection_view.dart';
|
import 'package:stackwallet/pages/buy_view/sub_widgets/crypto_selection_view.dart';
|
||||||
import 'package:stackwallet/pages/buy_view/sub_widgets/fiat_crypto_toggle.dart';
|
import 'package:stackwallet/pages/buy_view/sub_widgets/fiat_crypto_toggle.dart';
|
||||||
|
import 'package:stackwallet/pages/buy_view/sub_widgets/fiat_selection_view.dart';
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
@ -57,12 +59,6 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectFiatCrypto() async {
|
|
||||||
// await Future<void>.delayed(const Duration(milliseconds: 300));
|
|
||||||
//
|
|
||||||
// Navigator.of(context, rootNavigator: true).pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cryptoFieldOnChanged(String value) async {
|
void cryptoFieldOnChanged(String value) async {
|
||||||
if (_cryptoFocusNode.hasFocus) {
|
if (_cryptoFocusNode.hasFocus) {
|
||||||
final newCryptoAmount = Decimal.tryParse(value);
|
final newCryptoAmount = Decimal.tryParse(value);
|
||||||
|
@ -80,8 +76,8 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectCryptoCrypto() async {
|
void selectCrypto() async {
|
||||||
final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? "-";
|
final fromTicker = ref.read(buyFormStateProvider).fromTicker ?? "-";
|
||||||
// ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? "-";
|
// ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? "-";
|
||||||
|
|
||||||
// if (walletInitiated &&
|
// if (walletInitiated &&
|
||||||
|
@ -91,18 +87,18 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
List<Crypto> coins;
|
List<Crypto> coins;
|
||||||
switch (ref.read(currentExchangeNameStateProvider.state).state) {
|
// switch (ref.read(currentExchangeNameStateProvider.state).state) {
|
||||||
// case ChangeNowExchange.exchangeName:
|
// // case ChangeNowExchange.exchangeName:
|
||||||
// coins = ref.read(availableChangeNowCurrenciesProvider).coins;
|
// // coins = ref.read(availableChangeNowCurrenciesProvider).coins;
|
||||||
// break;
|
// // break;
|
||||||
// case SimpleSwapExchange.exchangeName:
|
// // case SimpleSwapExchange.exchangeName:
|
||||||
// coins = ref
|
// // coins = ref
|
||||||
// .read(availableSimpleswapCurrenciesProvider)
|
// // .read(availableSimpleswapCurrenciesProvider)
|
||||||
// .floatingRateCurrencies;
|
// // .floatingRateCurrencies;
|
||||||
// break;
|
// // break;
|
||||||
default:
|
// default:
|
||||||
coins = [];
|
coins = [];
|
||||||
}
|
// }
|
||||||
|
|
||||||
await _showFloatingCryptoSelectionSheet(
|
await _showFloatingCryptoSelectionSheet(
|
||||||
coins: coins,
|
coins: coins,
|
||||||
|
@ -251,6 +247,146 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void selectFiat() async {
|
||||||
|
final fromTicker = ref.read(buyFormStateProvider).fromTicker ?? "-";
|
||||||
|
// ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? "-";
|
||||||
|
|
||||||
|
// if (walletInitiated &&
|
||||||
|
// fromTicker.toLowerCase() == coin!.ticker.toLowerCase()) {
|
||||||
|
// // do not allow changing away from wallet coin
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
List<Crypto> coins;
|
||||||
|
// switch (ref.read(currentExchangeNameStateProvider.state).state) {
|
||||||
|
// // case ChangeNowExchange.exchangeName:
|
||||||
|
// // coins = ref.read(availableChangeNowCurrenciesProvider).coins;
|
||||||
|
// // break;
|
||||||
|
// // case SimpleSwapExchange.exchangeName:
|
||||||
|
// // coins = ref
|
||||||
|
// // .read(availableSimpleswapCurrenciesProvider)
|
||||||
|
// // .floatingRateCurrencies;
|
||||||
|
// // break;
|
||||||
|
// default:
|
||||||
|
coins = [];
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showFloatingFiatSelectionSheet({
|
||||||
|
required List<Fiat> fiats,
|
||||||
|
required String excludedTicker,
|
||||||
|
required String fromTicker,
|
||||||
|
required void Function(Fiat) onSelected,
|
||||||
|
}) async {
|
||||||
|
_fiatFocusNode.unfocus();
|
||||||
|
_cryptoFocusNode.unfocus();
|
||||||
|
|
||||||
|
List<Pair> allPairs;
|
||||||
|
|
||||||
|
// switch (ref.read(currentExchangeNameStateProvider.state).state) {
|
||||||
|
// // case ChangeNowExchange.exchangeName:
|
||||||
|
// // allPairs = ref.read(availableChangeNowCurrenciesProvider).pairs;
|
||||||
|
// // break;
|
||||||
|
// // case SimpleSwapExchange.exchangeName:
|
||||||
|
// // allPairs = ref.read(exchangeFormStateProvider).exchangeType ==
|
||||||
|
// // ExchangeRateType.fixed
|
||||||
|
// // ? ref.read(availableSimpleswapCurrenciesProvider).fixedRatePairs
|
||||||
|
// // : ref.read(availableSimpleswapCurrenciesProvider).floatingRatePairs;
|
||||||
|
// // break;
|
||||||
|
// default:
|
||||||
|
allPairs = [];
|
||||||
|
// }
|
||||||
|
|
||||||
|
List<Pair> availablePairs;
|
||||||
|
if (fromTicker.isEmpty ||
|
||||||
|
fromTicker == "-" ||
|
||||||
|
excludedTicker.isEmpty ||
|
||||||
|
excludedTicker == "-") {
|
||||||
|
availablePairs = allPairs;
|
||||||
|
} else if (excludedTicker == fromTicker) {
|
||||||
|
availablePairs = allPairs
|
||||||
|
.where((e) => e.from == excludedTicker)
|
||||||
|
.toList(growable: false);
|
||||||
|
} else {
|
||||||
|
availablePairs =
|
||||||
|
allPairs.where((e) => e.to == excludedTicker).toList(growable: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Fiat> tickers = fiats.where((e) {
|
||||||
|
if (excludedTicker == fromTicker) {
|
||||||
|
return e.ticker != excludedTicker &&
|
||||||
|
availablePairs.where((e2) => e2.to == e.ticker).isNotEmpty;
|
||||||
|
} else {
|
||||||
|
return e.ticker != excludedTicker &&
|
||||||
|
availablePairs.where((e2) => e2.from == e.ticker).isNotEmpty;
|
||||||
|
}
|
||||||
|
}).toList(growable: false);
|
||||||
|
|
||||||
|
final result = isDesktop
|
||||||
|
? await showDialog<Crypto?>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return DesktopDialog(
|
||||||
|
maxHeight: 700,
|
||||||
|
maxWidth: 580,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 32,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
"Choose a fiat with which to pay",
|
||||||
|
style: STextStyles.desktopH3(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const DesktopDialogCloseButton(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 32,
|
||||||
|
right: 32,
|
||||||
|
bottom: 32,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: RoundedWhiteContainer(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
borderColor: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.background,
|
||||||
|
child: FiatSelectionView(
|
||||||
|
coins: tickers,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: await Navigator.of(context).push(
|
||||||
|
MaterialPageRoute<dynamic>(
|
||||||
|
builder: (_) => FiatSelectionView(
|
||||||
|
coins: tickers,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mounted && result is Fiat) {
|
||||||
|
onSelected(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String? _fetchIconUrlFromTicker(String? ticker) {
|
String? _fetchIconUrlFromTicker(String? ticker) {
|
||||||
if (ticker == null) return null;
|
if (ticker == null) return null;
|
||||||
|
|
||||||
|
@ -364,13 +500,16 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onChanged: cryptoFieldOnChanged,
|
onChanged: cryptoFieldOnChanged,
|
||||||
onButtonTap: selectCryptoCrypto,
|
onButtonTap: selectCrypto,
|
||||||
isWalletCoin: isWalletCoin(coin, true),
|
isWalletCoin: isWalletCoin(coin, true),
|
||||||
image: _fetchIconUrlFromTicker(ref
|
image: _fetchIconUrlFromTicker(ref
|
||||||
.watch(buyFormStateProvider.select((value) => value.fromTicker))),
|
.watch(buyFormStateProvider.select((value) => value.fromTicker))),
|
||||||
ticker: ref
|
ticker: ref
|
||||||
.watch(buyFormStateProvider.select((value) => value.fromTicker)),
|
.watch(buyFormStateProvider.select((value) => value.fromTicker)),
|
||||||
),
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: isDesktop ? 20 : 12,
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
@ -386,6 +525,33 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: isDesktop ? 10 : 4,
|
height: isDesktop ? 10 : 4,
|
||||||
),
|
),
|
||||||
|
BuyTextField(
|
||||||
|
controller: _fiatController,
|
||||||
|
focusNode: _fiatFocusNode,
|
||||||
|
textStyle: STextStyles.smallMed14(context).copyWith(
|
||||||
|
color: Theme.of(context).extension<StackColors>()!.textDark,
|
||||||
|
),
|
||||||
|
buttonColor:
|
||||||
|
Theme.of(context).extension<StackColors>()!.buttonBackSecondary,
|
||||||
|
borderRadius: Constants.size.circularBorderRadius,
|
||||||
|
background:
|
||||||
|
Theme.of(context).extension<StackColors>()!.textFieldDefaultBG,
|
||||||
|
onTap: () {
|
||||||
|
if (_fiatController.text == "-") {
|
||||||
|
_fiatController.text = "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onChanged: fiatFieldOnChanged,
|
||||||
|
onButtonTap: selectFiat,
|
||||||
|
isWalletCoin: isWalletCoin(coin, true),
|
||||||
|
image: _fetchIconUrlFromTicker(ref
|
||||||
|
.watch(buyFormStateProvider.select((value) => value.fromTicker))),
|
||||||
|
ticker: ref
|
||||||
|
.watch(buyFormStateProvider.select((value) => value.fromTicker)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: isDesktop ? 20 : 12,
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
@ -422,7 +588,7 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onChanged: fiatFieldOnChanged,
|
onChanged: fiatFieldOnChanged,
|
||||||
onButtonTap: selectFiatCrypto,
|
onButtonTap: selectFiat,
|
||||||
// isWalletCoin: isWalletCoin(coin, true),
|
// isWalletCoin: isWalletCoin(coin, true),
|
||||||
isWalletCoin: false,
|
isWalletCoin: false,
|
||||||
// image: _fetchIconUrlFromTicker(ref
|
// image: _fetchIconUrlFromTicker(ref
|
||||||
|
|
332
lib/pages/buy_view/sub_widgets/fiat_selection_view.dart
Normal file
332
lib/pages/buy_view/sub_widgets/fiat_selection_view.dart
Normal file
|
@ -0,0 +1,332 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:stackwallet/models/buy/response_objects/fiat.dart';
|
||||||
|
import 'package:stackwallet/utilities/assets.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/background.dart';
|
||||||
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||||
|
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||||
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
|
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||||
|
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||||
|
|
||||||
|
class FiatSelectionView extends StatefulWidget {
|
||||||
|
const FiatSelectionView({
|
||||||
|
Key? key,
|
||||||
|
required this.coins,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final List<Fiat> coins;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FiatSelectionView> createState() => _FiatSelectionViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FiatSelectionViewState extends State<FiatSelectionView> {
|
||||||
|
late TextEditingController _searchController;
|
||||||
|
final _searchFocusNode = FocusNode();
|
||||||
|
|
||||||
|
late final List<Fiat> coins;
|
||||||
|
late List<Fiat> _coins;
|
||||||
|
|
||||||
|
void filter(String text) {
|
||||||
|
setState(() {
|
||||||
|
_coins = [
|
||||||
|
...coins.where((e) =>
|
||||||
|
e.name.toLowerCase().contains(text.toLowerCase()) ||
|
||||||
|
e.ticker.toLowerCase().contains(text.toLowerCase()))
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_searchController = TextEditingController();
|
||||||
|
|
||||||
|
coins = [...widget.coins];
|
||||||
|
coins.sort(
|
||||||
|
(a, b) => a.ticker.toLowerCase().compareTo(b.ticker.toLowerCase()));
|
||||||
|
for (Coin coin in Coin.values.reversed) {
|
||||||
|
int index = coins.indexWhere((element) =>
|
||||||
|
element.ticker.toLowerCase() == coin.ticker.toLowerCase());
|
||||||
|
if (index > 0) {
|
||||||
|
final currency = coins.removeAt(index);
|
||||||
|
coins.insert(0, currency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_coins = [...coins];
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_searchController.dispose();
|
||||||
|
_searchFocusNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final isDesktop = Util.isDesktop;
|
||||||
|
return ConditionalParent(
|
||||||
|
condition: !isDesktop,
|
||||||
|
builder: (child) {
|
||||||
|
return Background(
|
||||||
|
child: Scaffold(
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(context).extension<StackColors>()!.background,
|
||||||
|
appBar: AppBar(
|
||||||
|
leading: AppBarBackButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (FocusScope.of(context).hasFocus) {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
await Future<void>.delayed(
|
||||||
|
const Duration(milliseconds: 50));
|
||||||
|
}
|
||||||
|
if (mounted) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
"Choose a crypto to buy",
|
||||||
|
style: STextStyles.pageTitleH2(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: isDesktop ? MainAxisSize.min : MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
if (!isDesktop)
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(
|
||||||
|
Constants.size.circularBorderRadius,
|
||||||
|
),
|
||||||
|
child: TextField(
|
||||||
|
autofocus: isDesktop,
|
||||||
|
autocorrect: !isDesktop,
|
||||||
|
enableSuggestions: !isDesktop,
|
||||||
|
controller: _searchController,
|
||||||
|
focusNode: _searchFocusNode,
|
||||||
|
onChanged: filter,
|
||||||
|
style: STextStyles.field(context),
|
||||||
|
decoration: standardInputDecoration(
|
||||||
|
"Search",
|
||||||
|
_searchFocusNode,
|
||||||
|
context,
|
||||||
|
desktopMed: isDesktop,
|
||||||
|
).copyWith(
|
||||||
|
prefixIcon: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 10,
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.svg.search,
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
suffixIcon: _searchController.text.isNotEmpty
|
||||||
|
? Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 0),
|
||||||
|
child: UnconstrainedBox(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
TextFieldIconButton(
|
||||||
|
child: const XIcon(),
|
||||||
|
onTap: () async {
|
||||||
|
setState(() {
|
||||||
|
_searchController.text = "";
|
||||||
|
});
|
||||||
|
filter("");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Popular coins",
|
||||||
|
style: STextStyles.smallMed12(context),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: Builder(builder: (context) {
|
||||||
|
final items = _coins
|
||||||
|
.where((e) => Coin.values
|
||||||
|
.where((coin) =>
|
||||||
|
coin.ticker.toLowerCase() == e.ticker.toLowerCase())
|
||||||
|
.isNotEmpty)
|
||||||
|
.toList(growable: false);
|
||||||
|
|
||||||
|
return RoundedWhiteContainer(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
|
child: ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
primary: isDesktop ? false : null,
|
||||||
|
itemCount: items.length,
|
||||||
|
itemBuilder: (builderContext, index) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop(items[index]);
|
||||||
|
},
|
||||||
|
child: RoundedWhiteContainer(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
child: SvgPicture.network(
|
||||||
|
items[index].image,
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
placeholderBuilder: (_) =>
|
||||||
|
const LoadingIndicator(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
items[index].name,
|
||||||
|
style: STextStyles.largeMedium14(context),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 2,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
items[index].ticker.toUpperCase(),
|
||||||
|
style: STextStyles.smallMed12(context)
|
||||||
|
.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textSubtitle1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"All coins",
|
||||||
|
style: STextStyles.smallMed12(context),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: RoundedWhiteContainer(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
|
child: ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
primary: isDesktop ? false : null,
|
||||||
|
itemCount: _coins.length,
|
||||||
|
itemBuilder: (builderContext, index) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop(_coins[index]);
|
||||||
|
},
|
||||||
|
child: RoundedWhiteContainer(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
child: SvgPicture.network(
|
||||||
|
_coins[index].image,
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
placeholderBuilder: (_) =>
|
||||||
|
const LoadingIndicator(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
_coins[index].name,
|
||||||
|
style: STextStyles.largeMedium14(context),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 2,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
_coins[index].ticker.toUpperCase(),
|
||||||
|
style: STextStyles.smallMed12(context)
|
||||||
|
.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textSubtitle1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue