From aa62166b9e8743415ddc441a9543225db23255f9 Mon Sep 17 00:00:00 2001 From: Jacob Turley Date: Wed, 29 Apr 2020 15:53:22 -0700 Subject: [PATCH 1/3] Add new formatter --- lib/numeric_formatter.dart | 70 ++++++++++++++++++++++++-- test/pattern_input_formatter_test.dart | 29 ++++++++++- 2 files changed, 95 insertions(+), 4 deletions(-) diff --git a/lib/numeric_formatter.dart b/lib/numeric_formatter.dart index 40c8e83..690ca96 100644 --- a/lib/numeric_formatter.dart +++ b/lib/numeric_formatter.dart @@ -1,9 +1,9 @@ -import 'package:flutter/services.dart'; +import 'dart:math'; + import 'package:flutter/material.dart' show TextField; +import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; -import 'dart:math'; - /// /// An implementation of [NumberInputFormatter] automatically inserts thousands /// separators to numeric input. For example, a input of `1234` should be @@ -166,3 +166,67 @@ abstract class NumberInputFormatter extends TextInputFormatter { TextEditingValue _formatValue( TextEditingValue oldValue, TextEditingValue newValue); } + +/// Formatter that restricts the number of digits before and after a decimal point +class DecimalFormatter extends TextInputFormatter { + final int decimalCount; + final int numberCount; + + DecimalFormatter({this.decimalCount = 3, this.numberCount = 7}) : super(); + + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, TextEditingValue newValue) { + /// Blank string is allowed + if (newValue.text == "") { + return newValue; + } + try { + /// Check if the number is parseable. This throws out anything that would make it invalid i.e. alpha characters. + final isNotANumber = double.tryParse(newValue.text).isNaN; + if (isNotANumber) { + return oldValue; + } + } catch (e) { + return oldValue; + } + + /// Check how many decimal characters are in the string. If more than 2, kick it out. + final segments = newValue.text.split('.'); + final numberOfDecimal = segments.length; + if (numberOfDecimal > 2) { + return oldValue; + } else { + /// If we have a decimal + if (segments.length == 2) { + /// Check that both the number count and decimal pass + if (isDecimalPass(segments[1]) && isNumberPass(segments[0])) { + return newValue; + } + return oldValue; + } else { + /// If we don't have a decimal, just check that the number passes + if (isNumberPass(segments[0])) { + return newValue; + } + return oldValue; + } + } + } + + /// checks string against decimal count + isDecimalPass(String text) { + if (text.length > decimalCount) { + return false; + } + return true; + } + + /// checks string against number count + isNumberPass(String text) { + if (text.length > numberCount) { + return false; + } + return true; + } +} diff --git a/test/pattern_input_formatter_test.dart b/test/pattern_input_formatter_test.dart index f53ca16..e617dde 100644 --- a/test/pattern_input_formatter_test.dart +++ b/test/pattern_input_formatter_test.dart @@ -1,7 +1,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:intl/intl.dart'; - import 'package:pattern_formatter/pattern_formatter.dart'; void main() { @@ -17,6 +16,9 @@ _numericFormatterSmokeTest() { formatter: NumberFormat.decimalPattern('en_US'), allowFraction: true); final CreditCardFormatter creditCardFormatter = CreditCardFormatter(); + final DecimalFormatter decimalFormatterTwoDigits = + DecimalFormatter(decimalCount: 2); + test('numeric filter smoke test', () { final newValue1 = thousandsFormatter.formatEditUpdate( TextEditingValue( @@ -36,11 +38,36 @@ _numericFormatterSmokeTest() { TextEditingValue( text: '12a', selection: TextSelection.collapsed(offset: 3))); + final twoDigitTestCharCheck = decimalFormatterTwoDigits.formatEditUpdate( + TextEditingValue( + text: '12', selection: TextSelection.collapsed(offset: 2)), + TextEditingValue( + text: '12a', selection: TextSelection.collapsed(offset: 3))); + + final twoDigitTestCheck = decimalFormatterTwoDigits.formatEditUpdate( + TextEditingValue( + text: '12', selection: TextSelection.collapsed(offset: 2)), + TextEditingValue( + text: '12.1', selection: TextSelection.collapsed(offset: 3))); + + final twoDigitTestTooManyDecimalsCheck = + decimalFormatterTwoDigits.formatEditUpdate( + TextEditingValue( + text: '12', selection: TextSelection.collapsed(offset: 2)), + TextEditingValue( + text: '12.133', selection: TextSelection.collapsed(offset: 3))); + expect(newValue1.text, equals('12')); expect(newValue2.text, equals('12')); expect(newValue3.text, equals('12')); + + expect(twoDigitTestCharCheck.text, equals('12')); + + expect(twoDigitTestCheck.text, equals('12.1')); + + expect(twoDigitTestTooManyDecimalsCheck.text, equals('12')); }); test('thousands grouping smoke test', () { From 506951a887372d22e3ab490a657c2c261a97eda2 Mon Sep 17 00:00:00 2001 From: Jacob Turley Date: Thu, 30 Apr 2020 07:57:34 -0700 Subject: [PATCH 2/3] add dynamic decimal sep --- lib/numeric_formatter.dart | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/numeric_formatter.dart b/lib/numeric_formatter.dart index 690ca96..5edffc3 100644 --- a/lib/numeric_formatter.dart +++ b/lib/numeric_formatter.dart @@ -171,12 +171,18 @@ abstract class NumberInputFormatter extends TextInputFormatter { class DecimalFormatter extends TextInputFormatter { final int decimalCount; final int numberCount; + static final NumberFormat _formatter = NumberFormat.decimalPattern(); + final NumberFormat formatter; - DecimalFormatter({this.decimalCount = 3, this.numberCount = 7}) : super(); + DecimalFormatter( + {this.decimalCount = 3, this.numberCount = 7, this.formatter}) + : super(); @override TextEditingValue formatEditUpdate( TextEditingValue oldValue, TextEditingValue newValue) { + final _decimalSeparator = (formatter ?? _formatter).symbols.DECIMAL_SEP; + /// Blank string is allowed if (newValue.text == "") { return newValue; @@ -192,7 +198,7 @@ class DecimalFormatter extends TextInputFormatter { } /// Check how many decimal characters are in the string. If more than 2, kick it out. - final segments = newValue.text.split('.'); + final segments = newValue.text.split(_decimalSeparator); final numberOfDecimal = segments.length; if (numberOfDecimal > 2) { return oldValue; From fa7f0eb9670d577347d72d8b2babeaec5d45618d Mon Sep 17 00:00:00 2001 From: Jacob Turley Date: Thu, 30 Apr 2020 08:59:26 -0700 Subject: [PATCH 3/3] add allowNegatives --- lib/numeric_formatter.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/numeric_formatter.dart b/lib/numeric_formatter.dart index 5edffc3..f9709bd 100644 --- a/lib/numeric_formatter.dart +++ b/lib/numeric_formatter.dart @@ -171,11 +171,15 @@ abstract class NumberInputFormatter extends TextInputFormatter { class DecimalFormatter extends TextInputFormatter { final int decimalCount; final int numberCount; + final bool allowNegatives; static final NumberFormat _formatter = NumberFormat.decimalPattern(); final NumberFormat formatter; DecimalFormatter( - {this.decimalCount = 3, this.numberCount = 7, this.formatter}) + {this.decimalCount = 3, + this.numberCount = 7, + this.formatter, + this.allowNegatives = true}) : super(); @override @@ -187,6 +191,10 @@ class DecimalFormatter extends TextInputFormatter { if (newValue.text == "") { return newValue; } + if (newValue.text.contains("-") && !allowNegatives) { + return oldValue; + } + try { /// Check if the number is parseable. This throws out anything that would make it invalid i.e. alpha characters. final isNotANumber = double.tryParse(newValue.text).isNaN;