diff --git a/lib/numeric_formatter.dart b/lib/numeric_formatter.dart index 40c8e83..f9709bd 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,81 @@ 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; + final bool allowNegatives; + static final NumberFormat _formatter = NumberFormat.decimalPattern(); + final NumberFormat formatter; + + DecimalFormatter( + {this.decimalCount = 3, + this.numberCount = 7, + this.formatter, + this.allowNegatives = true}) + : super(); + + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, TextEditingValue newValue) { + final _decimalSeparator = (formatter ?? _formatter).symbols.DECIMAL_SEP; + + /// Blank string is allowed + 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; + 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(_decimalSeparator); + 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', () {