Oranekwu Gabriel Ekene
Posted on May 26, 2024
Eureka!
That was my expression a few days ago, when I found the perfect algorithm to XYZ. You should have seen the excitement on my face, I couldn't wait for the weekend to finally share the experience 😁.
Since Flutter is used to develop Iphone and Android applications (two different platforms -> with two different keypads), I'll explain my thoughts clearly with images from an Emulator (Android) and a Simulator (iOS).
Let's dive in ...
NUMBER INPUT ON FLUTTER TEXTFIELD
In Flutter, when we want to collect number inputs from users, we set the keyboardType property of a TextField widget to TextInputType.number
Once you do this, you're simply asking the device's keyboard to display it's number display.
From the two images above, we are able to collect what we need, right? and, it's largely sufficient, right?
Most Flutter developers will stop at doing this. However, this article aims to expose the RIGHT WAY!
While the above code is enough to collect a user's age, it's not entirely good user experience, because, on a Platform like Android, user's will be able to type hyphens, commas, blank spaces and period (This particular concern will not be an issue on iOS, however, we are coming to that).
What we can do is; Use the inputFormatters property of a TextField object to filter for just numbers. This ensures we constrain our inputs to DIGITS only.
DECIMAL INPUT ON FLUTTER TEXTFIELD
While the above solutions does exactly what we want, it is important I point out that, this is recommended if you're collecting just WHOLE NUMBERS from the user, a typical example being an age. If we are collecting decimals, say, a person's weight, we need to go through another path. Because, our first constraint does not allow period(decimals).
While this seems like a very nice approach, we are faced with a challenge, users are able to type a period more than once 😱, and we don't want that, AT ALL!
We need to find a way to stop users from entering a period more than once, luckily, the inputFormatters takes a List of TextInputFormatter, we can create our own custom TextInputFormatter.
class SinglePeriodEnforcer extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
final newText = newValue.text;
// Allow only one period
if ('.'.allMatches(newText).length <= 1) {
return newValue;
}
return oldValue;
}
}
Awesome, there's yet another issue, and that is with Iphones, our current keyboardType doesn't show a period on Iphones, remember the purpose of a cross-platform framework like Flutter is to write once and deploy everywhere.
Our solution would be to change the keyboard type to TextInputType.numberWithOptions and set the decimal parameter to true. This will ensure the period key appears at the bottom left.
You can explore the signed property of the TextInputType.numberWithOptions.
FORMAT LIKE A PRO 😎
While the two solutions above are largely sufficient for the kind of data we intend to collect from the user, I want to take the TextInputFormatter up a notch; that is where the "Aha" moment came, the "XYZ" solution.
The Challenge
When we want to collect information, such as an amount from a user, solution 2 above will be very nice, since the user is allowed to type decimals, however, I want to seperate the digits entered with "commas" automatically, without the user needing to do anything. This approach, I believe is very intuitive, it helps users visualise whatever amount they are entering, since it's comma seperated.
The Solution
For this solution, we need the intl package. Just include it in your pubspec.yaml file.
import 'package:intl/intl.dart';
import 'package:flutter/services.dart';
class ThousandsSeparatorInputFormatter extends TextInputFormatter {
// Setup a formatter that supports both commas for thousands and decimals
final formatter = NumberFormat("#,##0.###");
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
if (newValue.text.isEmpty) {
return newValue;
}
// Remove commas to check the new input and for parsing
final newText = newValue.text.replaceAll(',', '');
// Try parsing the input as a double
final num? newTextAsNum = num.tryParse(newText);
if (newTextAsNum == null) {
return oldValue; // Return old value if new value is not a number
}
// Split the input into whole number and decimal parts
final parts = newText.split('.');
if (parts.length > 1) {
// If there's a decimal part, format accordingly
final integerPart = int.tryParse(parts[0]) ?? 0;
final decimalPart = parts[1];
// Handle edge case where decimal part is present but empty (user just typed the dot)
final formattedText = '${formatter.format(integerPart)}.$decimalPart';
return TextEditingValue(
text: formattedText,
selection: updateCursorPosition(formattedText),
);
} else {
// No decimal part, format the whole number
final newFormattedText = formatter.format(newTextAsNum);
return TextEditingValue(
text: newFormattedText,
selection: updateCursorPosition(newFormattedText),
);
}
}
TextSelection updateCursorPosition(String text) {
return TextSelection.collapsed(offset: text.length);
}
}
Then, we use it, like so:
We end up with something like this, a nicely formatted text input filter for collecting information, such as an amount
However 😩, we have one more tiny thing to fix, the value collected from the field has a "comma", we need to do away with that (We cannot be sending values with commas to the backend), you can create a function to strip the comma off, or you create an extension. I prefer the later though (Just a personal preference).
So, let's go ahead and create an extension on String
You just need to use it whereever you are collecting the value from the Field.
Here, I used the extension on the onChanged parameter of a TextField.
It's also important to note that; all the solutions we did works just as well on a TextFormField.
If you have any questions related to this article, you can leave them in the comment section and I'll attend to them as soon as I can 😊.
HAPPY CODING! ✌️
Feel free to connect with me:
Posted on May 26, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.