Closed BenjiFarquhar closed 3 years ago
You are extending ReactiveFormField
and wrapping a ReactiveTextField
, both are bound to the same control. Only the "inner" ReactiveTextField
uses the custom value accessor.
It seems you don't need the outer ReactiveFormField
, in your case this can just be a StatelessWidget
that wraps the ReactiveTextField
.
@kuhnroyal Thanks, I will take that on board to make a change. For the short term, I added the DoubleValueAccessor to the outer element like this:
: super(
valueAccessor: DoubleValueAccessor(),
Which solved the value accessor issue. The validation messages are still very confused as mentioned in the original issue.
The validation messages are still very confused as mentioned in the original issue.
Likely when you enter a word, the DoubleValueAccessor
evaluates the input to null
and this the required message shows up. I am not sure what the number validator actually checks. This could be due to int/double, locale problems.
You can try to add an input formatter on the field that only allows valid number characters to be entered.
@kuhnroyal Thank you, after extending StatelessWidget the value accessor issue is resolved. It was getting confused between the two reactive form controls as you suggested. When control.value
is 88.00 it still displays "Product Price must be a number". I think the number validator is not good for double
number types. So I don't think an input formatter will work because you can't convert a double to an int with precision. I would have to change the control to a String control, which maybe I will need to do, not sure, but it seems like more work than is necessary.
Yea the number validator uses this regex:
static final RegExp numberRegex = RegExp(r'^-?[0-9]+$');
You probably have to add a custom decimal validator but be aware of locale specific decimal separators.
You can add a FilteringTextInputFormatter.allow
with a regex that fits your double requirements.
Like this (not tested): '^\\d{0,4}(\\${format.symbols.DECIMAL_SEP}{0,2})?)'
.
An another hint from my personal experience, TextInputType.numberWithOptions
doesn't work with Samsung keyboards.
Hi guys,
Thanks to @BenjaminFarquhar for the issue and thanks once again to @kuhnroyal for such a great help solving issues, I really appreciate it.
Yeap, the Validators.number is actually just validating integers (maybe we will need to create two differents validators one for integers and another for floating points that let you specify the precision). And @BenjaminFarquhar you can of course for the moment create your own validator that validates floating points and restrict the input with a Formatter.
Thanks heaps guys. I'm nearly there. I have used an input formatter:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
class CurrencyFormatter extends TextInputFormatter {
CurrencyFormatter({this.maxDigits, required this.context});
final int? maxDigits;
final BuildContext context;
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
if (newValue.selection.baseOffset == 0) {
return newValue;
}
if (maxDigits != null && newValue.selection.baseOffset > maxDigits!) {
return oldValue;
}
final double value = double.parse(newValue.text);
final Locale locale = Localizations.localeOf(context);
final formatter = NumberFormat.simpleCurrency(locale: locale.toString());
final String newText = formatter.format(value / 100);
return newValue.copyWith(
text: newText,
selection: TextSelection.collapsed(offset: newText.length));
}
}
and pass into the ReactiveTextField
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
CurrencyFormatter(maxDigits: 8, context: context),
],
However, inside the package method: Map<String, String> _getValidationMessages(FormControl<dynamic> control) {
, control.value
is still always null, even if the textField has 8.00 entered. Any ideas why that is? Thank you so much.
Ah, I forgot to mention I have a very basic double validator:
import 'package:reactive_forms/reactive_forms.dart';
Map<String, bool>? floatValidator(AbstractControl<dynamic> control) {
if (control.isNotNull && control.value is double) {
return null;
} else {
return <String, bool>{'float': true};
}
}
class VpValidators {
static ValidatorFunction get optionSelected => optionSelectedValidator
as Map<String, Object>? Function(AbstractControl<dynamic>);
static ValidatorFunction get float =>
floatValidator as Map<String, Object>? Function(AbstractControl<dynamic>);
}
FormGroup createItemLocationForm() {
return FormGroup({
GroceryItemFieldNames.price: FormControl<double>(
validators: [Validators.required, VpValidators.float]),
GroceryItemFieldNames.groceryStore: FormControl<GroceryStoreDomainEntity>(
validators: [Validators.required],
),
});
}
After I added the CurrencyFormatter
, it added currency characters such as '$' and ',' and then the standard DoubleValueAccessor
can't cope with that.
I needed to create a CurrencyValueAccessor
to convert the string created by the formatter back to a double:
import 'package:flutter/material.dart';
import 'package:reactive_forms/reactive_forms.dart';
import 'package:intl/intl.dart';
class CurrencyValueAccessor extends ControlValueAccessor<double, String> {
CurrencyValueAccessor({this.fractionDigits = 2, required this.context});
final int fractionDigits;
final BuildContext context;
@override
String modelToViewValue(double? modelValue) {
return modelValue == null ? '' : modelValue.toStringAsFixed(fractionDigits);
}
@override
double? viewToModelValue(String? viewValue) {
if (viewValue == '' || viewValue == null) {
return null;
}
final Locale locale = Localizations.localeOf(context);
final formatter = NumberFormat.simpleCurrency(locale: locale.toString());
return formatter.parse(viewValue).toDouble();
}
}
Thanks @kuhnroyal for mentioning that
"the DoubleValueAccessor evaluates the input to null"
.
Thanks @joanpablo for also explaining the issue and the options I can take :)
It could be interesting to have a CurrencyValueAccessor built-in in the package.
Yep that could be very handy
Actually a CurrencyValueAccessor that automatically converts an int (in cents) to a double for display and entering would be pretty cool.
Actually a CurrencyValueAccessor that automatically converts an int (in cents) to a double for display and entering would be pretty cool.
I'm agree with you guys. Please create an issue for this new feature (and any Pull Request will be also nicely received :) )
In this thread I see two possible improvement: 1-) Create a CurrencyValueAccessor 2-) Create a Validators.floatingNumber (or something like that)
version:
reactive_forms: ^10.2.0
I have a number text field which has a model value of type double, but somehow the defaultValueAccessor ends up being used, even when I explicitly pass in a DoubleValueAccessor(). This causes this error to occur:
Unhandled Exception: type 'double' is not a subtype of type 'String?' in type cast
Is there anything wrong with my code or is this a bug?
This is my usage of it:
The validation messages are also mixed up. When I enter a number it says "Product Price must be a number" and when I enter a word it says: "Please enter the Product Price".
This is the form used: