A minimalistic and configurable number formatter to use with the TextField
widget.
Features
This package provides TextInputFormatter
that solves the most common formatting use cases with number inputs:
- Separating digit groups in an integral part of the decimal number.
- Controlling the maximum length of integral and decimal parts of the decimal number.
- Different input caret moving behavior in integral and decimal parts.
- Defaulting to the formatted zero if the whole number is deleted.
- Controlling the group separator symbol and decimal separator symbol.
- Easily accessing the underlying
double
value of the formattedString
.
Why use this instead of intl
?
The intl
package also provides the powerful NumberFormat
class that handles formatting the numbers in the context of the given locale, but it is only intended to operate with static text, and incorporating it into the TextField
widget might be a bit problematic and can lead to weird behavior.
The same is true for packages like money2
and money_formatter
.
This package intends to provide minimalistic, configurable, and convenient functionality that most clients will expect to see when interacting with number inputs.
Be Aware
While this package mainly operates on String values it also provides access to the double value of the formatted decimal number, as well as number parsing. So it is susceptible to issues that are tied to Dart's standard double implementation. It is based on the IEEE 754 standard for floating-point arithmetic, which has a finite precision. As a result, some numbers cannot be represented exactly, leading to small rounding errors in calculations.
But you should be alright as long as the maximum precision of fractional digits needed for your case is under four digits.
If not, or if you need to perform mathematical operations on decimals, then you should consider developing a custom solution using something in line with the decimal
.
Getting started
Add the dependency to your pubspec.yaml
and run flutter pub get
dependencies:
amount_input_formatter: ^0.1.0
or run
$ dart pub add amount_input_formatter
then add import in your code:
import 'package:amount_input_formatter/amount_input_formatter.dart';
Usage
AmmountInputFormatter
extends Flutter's TextInputFormatter
so it can be easily integrated into TextField
or TextFormField
by adding it to the inputFormatters
argument list.
TextField(
inputFormatters: [AmountInputFormatter()],
),
But most likely you will need to extract the double
value of the formatted number from the TextField
.
In this case save the AmmountInputFormatter
instance somewhere, for example in the state of your widget.
class _ExampleWidgetState extends State<ExampleWidget> {
final _formatter = AmountInputFormatter();
then pass the object to your TextField
and access the double
value with _formatter.doubleValue
getter
TextField(
inputFormatters: [_formatter],
onChanged: (text) {
final double result = _formatter.doubleValue;
},
),
Also, there might be a case when you need to dynamically update the value of the field with a new value from some external source.
Unfortunately, it is not possible from the formatter instance alone.
You will need to add a TextEditingController
instance to your TextField
and use one of the provided methods to sync the TextField content with the formatted.
class _ExampleWidgetState extends State<ExampleWidget> {
final _controller = TextEditingController();
final _formatter = AmountInputFormatter();
/// You can use [setNumber] method from formatter with the [attachedController] parameter
/// to sync the [TextField] content with formatter
void updateNumberValue(num number) {
_formatter.setNumber(
number,
attachedController: _controller,
);
}
/// Or set and format the text value with extension method [setAndFormatText]
/// for [TextEditingController]
void updateTextValue(String text) {
_controller.setAndFormatText(
text: text,
formatter: _formatter,
);
}
@override
Widget build(BuildContext context) {
return TextField(
controller: _controller,
inputFormatters: [_formatter],
);
}
}
Full Example
class ExamplePage extends StatefulWidget {
const ExamplePage({
super.key,
});
@override
State<ExamplePage> createState() => _ExamplePageState();
}
class _ExamplePageState extends State<ExamplePage> {
final _controller = TextEditingController();
final _formatter = AmountInputFormatter(
integralLength: 10,
groupSeparator: ',',
fractionalDigits: 3,
decimalSeparator: '.',
);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Amount Input Formatter'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 36),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ListenableBuilder(
listenable: _controller,
builder: (
context,
child,
) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Underlying double value: ${_formatter.doubleValue}',
),
const SizedBox(height: 10),
Text(
'Formatted value: ${_formatter.formattedValue}',
),
],
);
},
),
const SizedBox(height: 20),
TextField(
controller: _controller,
inputFormatters: [_formatter],
decoration: const InputDecoration(
contentPadding: EdgeInsets.symmetric(
vertical: 8,
horizontal: 16,
),
),
),
],
),
),
),
);
}
}
Libraries
- amount_input_formatter
- This package provides a configurable number formatter to use with the TextField widget.