mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-02-02 03:06:29 +00:00
insert and handle localised group and decimal separators in textfield amounts
This commit is contained in:
parent
f88d7f200c
commit
c0eb85ce5a
8 changed files with 213 additions and 76 deletions
|
@ -40,6 +40,7 @@ import 'package:stackwallet/themes/stack_colors.dart';
|
|||
import 'package:stackwallet/utilities/address_utils.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_input_formatter.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_unit.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
||||
|
@ -128,8 +129,6 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
if (!_cryptoAmountChangeLock) {
|
||||
final cryptoAmount = ref.read(pAmountFormatter(coin)).tryParse(
|
||||
cryptoAmountController.text,
|
||||
locale: ref.read(localeServiceChangeNotifierProvider).locale,
|
||||
coin: coin,
|
||||
);
|
||||
if (cryptoAmount != null) {
|
||||
_amountToSend = cryptoAmount;
|
||||
|
@ -1538,13 +1537,20 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
),
|
||||
textAlign: TextAlign.right,
|
||||
inputFormatters: [
|
||||
AmountInputFormatter(
|
||||
decimals: coin.decimals,
|
||||
locale: locale,
|
||||
),
|
||||
|
||||
// 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),
|
||||
// TextInputFormatter.withFunction((oldValue,
|
||||
// newValue) =>
|
||||
// // RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
||||
// // RegExp(r'^\d{1,3}([,\.]\d+)?|[,\.\d]+$')
|
||||
// getAmountRegex(locale, coin.decimals)
|
||||
// .hasMatch(newValue.text)
|
||||
// ? newValue
|
||||
// : oldValue),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.only(
|
||||
|
@ -1599,13 +1605,18 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
),
|
||||
textAlign: TextAlign.right,
|
||||
inputFormatters: [
|
||||
AmountInputFormatter(
|
||||
decimals: 2,
|
||||
locale: locale,
|
||||
),
|
||||
// regex to validate a fiat amount with 2 decimal places
|
||||
TextInputFormatter.withFunction((oldValue,
|
||||
newValue) =>
|
||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
||||
.hasMatch(newValue.text)
|
||||
? newValue
|
||||
: oldValue),
|
||||
// TextInputFormatter.withFunction((oldValue,
|
||||
// newValue) =>
|
||||
// // RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
||||
// getAmountRegex(locale, 2)
|
||||
// .hasMatch(newValue.text)
|
||||
// ? newValue
|
||||
// : oldValue),
|
||||
],
|
||||
onChanged: (baseAmountString) {
|
||||
final baseAmount = Amount.tryParseFiatString(
|
||||
|
|
|
@ -31,6 +31,7 @@ import 'package:stackwallet/themes/stack_colors.dart';
|
|||
import 'package:stackwallet/utilities/address_utils.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_input_formatter.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
||||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
|
@ -269,8 +270,6 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
|||
if (!_cryptoAmountChangeLock) {
|
||||
final cryptoAmount = ref.read(pAmountFormatter(coin)).tryParse(
|
||||
cryptoAmountController.text,
|
||||
locale: ref.read(localeServiceChangeNotifierProvider).locale,
|
||||
coin: coin,
|
||||
ethContract: tokenContract,
|
||||
);
|
||||
if (cryptoAmount != null) {
|
||||
|
@ -937,13 +936,17 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
|||
),
|
||||
textAlign: TextAlign.right,
|
||||
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),
|
||||
AmountInputFormatter(
|
||||
decimals: tokenContract.decimals,
|
||||
locale: locale,
|
||||
),
|
||||
// // 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),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.only(
|
||||
|
@ -996,13 +999,17 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
|||
),
|
||||
textAlign: TextAlign.right,
|
||||
inputFormatters: [
|
||||
// regex to validate a fiat amount with 2 decimal places
|
||||
TextInputFormatter.withFunction((oldValue,
|
||||
newValue) =>
|
||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
||||
.hasMatch(newValue.text)
|
||||
? newValue
|
||||
: oldValue),
|
||||
AmountInputFormatter(
|
||||
decimals: 2,
|
||||
locale: locale,
|
||||
),
|
||||
// // regex to validate a fiat amount with 2 decimal places
|
||||
// TextInputFormatter.withFunction((oldValue,
|
||||
// newValue) =>
|
||||
// RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
||||
// .hasMatch(newValue.text)
|
||||
// ? newValue
|
||||
// : oldValue),
|
||||
],
|
||||
onChanged: _onFiatAmountFieldChanged,
|
||||
decoration: InputDecoration(
|
||||
|
|
|
@ -10,16 +10,17 @@
|
|||
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_rounded_date_picker/flutter_rounded_date_picker.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/models/transaction_filter.dart';
|
||||
import 'package:stackwallet/providers/global/locale_provider.dart';
|
||||
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
|
||||
import 'package:stackwallet/themes/stack_colors.dart';
|
||||
import 'package:stackwallet/themes/theme_providers.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_input_formatter.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
|
@ -757,12 +758,19 @@ class _TransactionSearchViewState
|
|||
decimal: true,
|
||||
),
|
||||
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),
|
||||
AmountInputFormatter(
|
||||
decimals: widget.coin.decimals,
|
||||
locale: ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
),
|
||||
),
|
||||
// // 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),
|
||||
],
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
|
|
|
@ -39,6 +39,7 @@ import 'package:stackwallet/themes/stack_colors.dart';
|
|||
import 'package:stackwallet/utilities/address_utils.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_input_formatter.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_unit.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
||||
|
@ -1040,12 +1041,16 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
),
|
||||
textAlign: TextAlign.right,
|
||||
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),
|
||||
AmountInputFormatter(
|
||||
decimals: coin.decimals,
|
||||
locale: locale,
|
||||
),
|
||||
// // 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),
|
||||
],
|
||||
onChanged: (newValue) {},
|
||||
decoration: InputDecoration(
|
||||
|
@ -1097,12 +1102,16 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
),
|
||||
textAlign: TextAlign.right,
|
||||
inputFormatters: [
|
||||
// regex to validate a fiat amount with 2 decimal places
|
||||
TextInputFormatter.withFunction((oldValue, newValue) =>
|
||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
||||
.hasMatch(newValue.text)
|
||||
? newValue
|
||||
: oldValue),
|
||||
AmountInputFormatter(
|
||||
decimals: 2,
|
||||
locale: locale,
|
||||
),
|
||||
// // regex to validate a fiat amount with 2 decimal places
|
||||
// TextInputFormatter.withFunction((oldValue, newValue) =>
|
||||
// RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
||||
// .hasMatch(newValue.text)
|
||||
// ? newValue
|
||||
// : oldValue),
|
||||
],
|
||||
onChanged: fiatTextFieldOnChanged,
|
||||
decoration: InputDecoration(
|
||||
|
|
|
@ -31,6 +31,7 @@ import 'package:stackwallet/themes/stack_colors.dart';
|
|||
import 'package:stackwallet/utilities/address_utils.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_input_formatter.dart';
|
||||
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
||||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
|
@ -50,7 +51,7 @@ import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
|||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
const _kCryptoAmountRegex = r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$';
|
||||
// const _kCryptoAmountRegex = r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$';
|
||||
|
||||
class DesktopTokenSend extends ConsumerStatefulWidget {
|
||||
const DesktopTokenSend({
|
||||
|
@ -717,15 +718,22 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
|||
),
|
||||
textAlign: TextAlign.right,
|
||||
inputFormatters: [
|
||||
AmountInputFormatter(
|
||||
decimals: tokenContract.decimals,
|
||||
locale: ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
),
|
||||
),
|
||||
// regex to validate a crypto amount with 8 decimal places
|
||||
TextInputFormatter.withFunction((oldValue, newValue) => RegExp(
|
||||
_kCryptoAmountRegex.replaceAll(
|
||||
"0,8",
|
||||
"0,${tokenContract.decimals}",
|
||||
),
|
||||
).hasMatch(newValue.text)
|
||||
? newValue
|
||||
: oldValue),
|
||||
// TextInputFormatter.withFunction((oldValue, newValue) => RegExp(
|
||||
// _kCryptoAmountRegex.replaceAll(
|
||||
// "0,8",
|
||||
// "0,${tokenContract.decimals}",
|
||||
// ),
|
||||
// ).hasMatch(newValue.text)
|
||||
// ? newValue
|
||||
// : oldValue),
|
||||
],
|
||||
onChanged: (newValue) {},
|
||||
decoration: InputDecoration(
|
||||
|
@ -777,12 +785,19 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
|||
),
|
||||
textAlign: TextAlign.right,
|
||||
inputFormatters: [
|
||||
// regex to validate a fiat amount with 2 decimal places
|
||||
TextInputFormatter.withFunction((oldValue, newValue) =>
|
||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
||||
.hasMatch(newValue.text)
|
||||
? newValue
|
||||
: oldValue),
|
||||
AmountInputFormatter(
|
||||
decimals: 2,
|
||||
locale: ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
),
|
||||
),
|
||||
// // regex to validate a fiat amount with 2 decimal places
|
||||
// TextInputFormatter.withFunction((oldValue, newValue) =>
|
||||
// RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
||||
// .hasMatch(newValue.text)
|
||||
// ? newValue
|
||||
// : oldValue),
|
||||
],
|
||||
onChanged: fiatTextFieldOnChanged,
|
||||
decoration: InputDecoration(
|
||||
|
|
|
@ -66,8 +66,6 @@ class AmountFormatter {
|
|||
|
||||
Amount? tryParse(
|
||||
String string, {
|
||||
required String locale,
|
||||
required Coin coin,
|
||||
EthContract? ethContract,
|
||||
}) {
|
||||
return unit.tryParse(string, locale: locale, coin: coin);
|
||||
|
|
82
lib/utilities/amount/amount_input_formatter.dart
Normal file
82
lib/utilities/amount/amount_input_formatter.dart
Normal file
|
@ -0,0 +1,82 @@
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:intl/number_symbols.dart';
|
||||
import 'package:intl/number_symbols_data.dart';
|
||||
|
||||
class AmountInputFormatter extends TextInputFormatter {
|
||||
final int decimals;
|
||||
final String locale;
|
||||
|
||||
AmountInputFormatter({required this.decimals, required this.locale});
|
||||
|
||||
@override
|
||||
TextEditingValue formatEditUpdate(
|
||||
TextEditingValue oldValue, TextEditingValue newValue) {
|
||||
// get number symbols for decimal place and group separator
|
||||
final numberSymbols = numberFormatSymbols[locale] as NumberSymbols? ??
|
||||
numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?;
|
||||
|
||||
final decimalSeparator = numberSymbols?.DECIMAL_SEP ?? ".";
|
||||
final groupSeparator = numberSymbols?.GROUP_SEP ?? ",";
|
||||
|
||||
String newText = newValue.text.replaceAll(groupSeparator, "");
|
||||
|
||||
final selectionIndexFromTheRight =
|
||||
newValue.text.length - newValue.selection.end;
|
||||
|
||||
String? fraction;
|
||||
if (newText.contains(decimalSeparator)) {
|
||||
final parts = newText.split(decimalSeparator);
|
||||
|
||||
if (parts.length > 2) {
|
||||
return oldValue;
|
||||
}
|
||||
if (newText.startsWith(decimalSeparator)) {
|
||||
return TextEditingValue(
|
||||
text: newText,
|
||||
selection: TextSelection.collapsed(
|
||||
offset: newText.length - selectionIndexFromTheRight,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
newText = parts.first;
|
||||
if (parts.length == 2) {
|
||||
fraction = parts.last;
|
||||
} else {
|
||||
fraction = "";
|
||||
}
|
||||
|
||||
if (fraction.length > decimals) {
|
||||
return oldValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (newText.trim() == '' || newText.trim() == '0') {
|
||||
return newValue.copyWith(text: '');
|
||||
} else if (BigInt.parse(newText) < BigInt.one) {
|
||||
return newValue.copyWith(text: '');
|
||||
}
|
||||
|
||||
// insert group separator
|
||||
final regex = RegExp(r'\B(?=(\d{3})+(?!\d))');
|
||||
|
||||
String newString = newText.replaceAllMapped(
|
||||
regex,
|
||||
(m) => "${m.group(0)}${numberSymbols?.GROUP_SEP ?? ","}",
|
||||
);
|
||||
|
||||
if (fraction != null) {
|
||||
newString += decimalSeparator;
|
||||
if (fraction.isNotEmpty) {
|
||||
newString += fraction;
|
||||
}
|
||||
}
|
||||
|
||||
return TextEditingValue(
|
||||
text: newString,
|
||||
selection: TextSelection.collapsed(
|
||||
offset: newString.length - selectionIndexFromTheRight,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -9,17 +9,19 @@
|
|||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/models/exchange/aggregate_currency.dart';
|
||||
import 'package:stackwallet/pages/buy_view/sub_widgets/crypto_selection_view.dart';
|
||||
import 'package:stackwallet/providers/global/locale_provider.dart';
|
||||
import 'package:stackwallet/themes/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_input_formatter.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
|
||||
class ExchangeTextField extends StatefulWidget {
|
||||
class ExchangeTextField extends ConsumerStatefulWidget {
|
||||
const ExchangeTextField({
|
||||
Key? key,
|
||||
this.borderRadius = 0,
|
||||
|
@ -55,10 +57,10 @@ class ExchangeTextField extends StatefulWidget {
|
|||
final AggregateCurrency? currency;
|
||||
|
||||
@override
|
||||
State<ExchangeTextField> createState() => _ExchangeTextFieldState();
|
||||
ConsumerState<ExchangeTextField> createState() => _ExchangeTextFieldState();
|
||||
}
|
||||
|
||||
class _ExchangeTextFieldState extends State<ExchangeTextField> {
|
||||
class _ExchangeTextFieldState extends ConsumerState<ExchangeTextField> {
|
||||
late final TextEditingController controller;
|
||||
late final FocusNode focusNode;
|
||||
late final TextStyle textStyle;
|
||||
|
@ -130,12 +132,17 @@ class _ExchangeTextFieldState extends State<ExchangeTextField> {
|
|||
),
|
||||
),
|
||||
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),
|
||||
AmountInputFormatter(
|
||||
decimals: 8, // todo change this
|
||||
locale: ref.watch(localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale)),
|
||||
),
|
||||
// // 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),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue