Je veux forcer l'utilisateur à entrer un seul point et 3 décimales.
J'ai trouvé le code ci-dessous:
class NumberRemoveExtraDotFormatter extends TextInputFormatter { NumberRemoveExtraDotFormatter({this.decimalRange = 3}) : assert(decimalRange == null || decimalRange > 0); final int decimalRange; @override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { var nValue = newValue.text; var nSelection = newValue.selection; Pattern p = RegExp(r'(\d+\.?)|(\.?\d+)|(\.?)'); nValue = p.allMatches(nValue).map<String>((Match match) => match.group(0)).join(); if (nValue.startsWith('.')) { nValue = '0.'; } else if (nValue.contains('.')) { if (nValue.substring(nValue.indexOf('.') + 1).length > decimalRange) { nValue = oldValue.text; } else { if (nValue.split('.').length > 2) { var split = nValue.split('.'); nValue = split[0] + '.' + split[1]; } } } nSelection = newValue.selection.copyWith( baseOffset: math.min(nValue.length, nValue.length + 1), extentOffset: math.min(nValue.length, nValue.length + 1), ); return TextEditingValue(text: Utils.addCommad(nValue), selection: nSelection, composing: TextRange.empty); } }
mais le problème est que lorsque l'utilisateur entre plus de 3 décimales et souhaite ensuite supprimer, ce n'est pas le cas. parce que les nombres sauvegardent dans le champ textform et ils doivent être supprimés jusqu'à ce qu'ils atteignent 3 décimales et aussi lors de la saisie du milieu du curseur d'entrée à la fin.
Aussi, je veux décaler le nombre de droite si l'utilisateur entre plus de 3 décimales.
Comment puis-je atteindre cet objectif?
3 Réponses :
Essayez d'utiliser ceci:
FilteringTextInputFormatter(RegExp(r'(^[0-9]*(?:\.[0-9]{0,3})?$)'), allow: true),
Fondamentalement, l'expression régulière essaiera de faire correspondre 0 ou plusieurs occurrences de chiffres suivis d'un décimal facultatif suivi de jusqu'à 3 chiffres après le décimal. Vous pouvez le modifier pour utiliser également une valeur négative ^(?:\-)?[0-9]*(?:\.[0-9]{0,3})?$
.
J'essaye ça. mais ne fonctionne pas du tout. quand j'entre le 4ème nombre en partie décimale. toute la valeur supprimée
@BeHappy oui. J'ai essayé sur dartpad.dev et je suis arrivé avec moi aussi. Je peux modifier la réponse une fois que j'ai du temps libre.
Vérifiez aussi mon commentaire pour une autre réponse.
Si vous souhaitez simplement forcer l'utilisateur à entrer un seul point et 3 décimales, cela peut fonctionner.
class NumberInputFormatter extends TextInputFormatter { final int maximumFractionDigits; NumberInputFormatter({ this.maximumFractionDigits = 3, }) : assert(maximumFractionDigits != null && maximumFractionDigits >= 0); @override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { var newText = newValue.text; var selectionOffset = newValue.selection.extent.offset; bool isNegative = false; if (newText.startsWith('-')) { newText = newText.substring(1); isNegative = true; } if (newText.isEmpty) { return newValue; } if (newText.indexOf('.') != newText.lastIndexOf('.')) { // inputted more than one dot. return oldValue; } if (newText.startsWith('.') && maximumFractionDigits > 0) { newText = '0$newText'; selectionOffset += 1; } while (newText.length > 1 && !newText.startsWith('0.') && newText.startsWith('0')) { newText = newText.substring(1); selectionOffset -= 1; } if (_decimalDigitsOf(newText) > maximumFractionDigits) { // delete the extra digits. newText = newText.substring(0, newText.indexOf('.') + 1 + maximumFractionDigits); } if (newValue.text.length == oldValue.text.length - 1 && oldValue.text.substring(newValue.selection.extentOffset, newValue.selection.extentOffset + 1) == ',') { // in this case, user deleted the thousands separator, we should delete the digit number before the cursor. newText = newText.replaceRange(newValue.selection.extentOffset - 1, newValue.selection.extentOffset, ''); selectionOffset -= 1; } if (newText.endsWith('.')) { // in order to calculate the selection offset correctly, we delete the last decimal point first. newText = newText.replaceRange(newText.length - 1, newText.length, ''); } int lengthBeforeFormat = newText.length; newText = _removeComma(newText); if (double.tryParse(newText) == null) { // invalid decimal number return oldValue; } newText = _addComma(newText); selectionOffset += newText.length - lengthBeforeFormat; // thousands separator newly added if (maximumFractionDigits > 0 && newValue.text.endsWith('.')) { // decimal point is at the last digit, we need to append it back. newText = '$newText.'; } if (isNegative) { newText = '-$newText'; } return TextEditingValue( text: newText, selection: TextSelection.collapsed(offset: min(selectionOffset, newText.length)), ); } static int _decimalDigitsOf(String text) { var index = text?.indexOf('.') ?? -1; return index == -1 ? 0 : text.length - index - 1; } static String _addComma(String text) { StringBuffer sb = StringBuffer(); var pointIndex = text.indexOf('.'); String integerPart; String decimalPart; if (pointIndex >= 0) { integerPart = text.substring(0, pointIndex); decimalPart = text.substring(pointIndex); } else { integerPart = text; decimalPart = ''; } List<String> parts = []; while (integerPart.length > 3) { parts.add(integerPart.substring(integerPart.length - 3)); integerPart = integerPart.substring(0, integerPart.length - 3); } parts.add(integerPart); sb.writeAll(parts.reversed, ','); sb.write(decimalPart); return sb.toString(); } static String _removeComma(String text) { return text.replaceAll(',', ''); } }
Selon votre commentaire:
- Comment ajouter un séparateur de milliers?
- Le changement de numéro ne fonctionne pas. Je veux décaler le nombre si l'utilisateur commence à taper en partie décimale lorsque la virgule décimale est atteinte au maximum. Par exemple, la valeur actuelle est 0,333 et le curseur défini par l'utilisateur sur la seconde 3 (0,3 | 33) et tapez 2. alors la valeur doit être 0,323.
Nous pouvons utiliser intl NumberFormat pour formater le nombre.
Ceci est mon code, je n'ai pas eu de test approfondi et détaillé. Si vous trouvez des bogues, veuillez les signaler.
lorsque vous entrez un nombre long avec 0 maximumFractionDigits, un nombre incorrect sera ajouté. => cela ne dépend pas de maximumFractionDigits. ça arrive toujours.
Je pense qu'il y a un comportement inattendu dans le NumberFormat, et je l'ai changé en méthode personnalisée et il prend en charge le nombre négatif maintenant.
FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,3}'))
Bien que ce code puisse résoudre la question, inclure une explication sur comment et pourquoi cela résout le problème aiderait vraiment à améliorer la qualité de votre publication et entraînerait probablement plus de votes à la hausse. N'oubliez pas que vous répondez à la question des lecteurs à l'avenir, pas seulement à la personne qui la pose maintenant. Veuillez modifier votre réponse pour ajouter des explications et donner une indication des limites et des hypothèses applicables.
Il y a 2 problèmes: 1) Comment ajouter un séparateur de milliers? 2) Le décalage du numéro ne fonctionne pas. Je veux décaler le nombre si l'utilisateur commence à taper en partie décimale lorsque la virgule décimale est atteinte au maximum. Par exemple, la valeur actuelle est 0,333 et le curseur défini par l'utilisateur sur la seconde 3 (0,3 | 33) et tapez 2. alors la valeur doit être 0,323. de toute façon tnx pour réponse :)
C'est très bien. mais un bogue est lorsque vous entrez un nombre long avec 0 maximumFractionDigits, un mauvais numéro sera ajouté. De plus, le retour arrière ne fonctionne pas lorsque vous entrez plus de maximumFractionDigits dans iOS.
et aussi avec 0 maximumFractionDigits, l'utilisateur peut toujours entrer un point.
lorsque vous entrez un nombre long avec 0 maximumFractionDigits, un nombre incorrect sera ajouté. => cela ne dépend pas de maximumFractionDigits. ça arrive toujours.
Je le teste sur iOS et cela fonctionne bien. Vous devriez peut-être mettre à jour votre version Flutter.
tnx, fonctionne comme un charme.
le code complet est ici, (mise à jour, vous pouvez également modifier les données par curseur)
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'dart:math' as math; class DecimalChecker extends TextInputFormatter { DecimalChecker({this.decimalRange = 3}) : assert(decimalRange == null || decimalRange > 0); final int decimalRange; @override TextEditingValue formatEditUpdate( TextEditingValue oldValue, TextEditingValue newValue) { String valueTxt = newValue.text; TextSelection valueSet = newValue.selection; var newlength = newValue.text.length; var oldlength = oldValue.text.length; if (oldlength < newlength) { Pattern p = RegExp(r'(\d+\.?)|(\.?\d+)|(\.?)'); valueTxt = p .allMatches(valueTxt) .map<String>((Match match) => match.group(0)) .join(); print("------>"); if (valueTxt.startsWith('.')) { valueTxt = '0.'; } else if (valueTxt.contains('.')) { if (valueTxt.substring(valueTxt.indexOf('.') + 1).length > decimalRange) { valueTxt = oldValue.text; } else { if (valueTxt.split('.').length > 2) { List<String> split = valueTxt.split('.'); valueTxt = split[0] + '.' + split[1]; } } } valueSet = newValue.selection.copyWith( baseOffset: math.min(valueTxt.length, valueTxt.length + 1), extentOffset: math.min(valueTxt.length, valueTxt.length + 1), ); return TextEditingValue( text: valueTxt, selection: valueSet, composing: TextRange.empty); } else { return TextEditingValue( text: valueTxt, selection: valueSet, composing: TextRange.empty); } } } void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter App', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'My Decimal Check App'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { TextEditingController numberController = TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Padding( padding: EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0), child: TextField( controller: numberController, keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [DecimalChecker()], decoration: InputDecoration( hintText: "Please enter Number", ), ), ), ], ), ), ); } }
Lorsque l'utilisateur entre plus de chiffres à la partie décimale, le retour arrière ne fonctionne pas.
vérifier à nouveau, je mets à jour mon image gif.it fonctionne
Il y a 2 problèmes: 1) Comment ajouter Utils.addComma (qui est pour séparateur de milliers)?. 2) Le décalage du numéro ne fonctionne pas. Je veux décaler le nombre si l'utilisateur commence à taper en partie décimale lorsque la virgule décimale est atteinte au maximum. Par exemple, la valeur actuelle est 0,333 et le curseur défini par l'utilisateur sur la seconde 3 (0,3 | 33) et tapez 2. alors la valeur doit être 0,323. de toute façon tnx pour réponse :)
vérifiez à nouveau, je mets à jour mon code pour ce cas également, maintenant vous changez la valeur par le curseur ..