Flutter TextFormField display value with NumberFormat - flutter

I'd like to be able to display the value entered in TextFormField with NumberFormat('###,##0.00', 'en_US').
There are no issues in the conversion, but I'm having issues displaying the value of formattedPrice in the TextFormField. I'd also like to set the default value of TextFormField as '0.00'. In my sample code below, the TextFormField restricts input to only have a single decimal symbol/separator and two decimal values.
I'm fine with .00 decimals not displayed when there are no decimals present in the TextFormField value during input, but I'd like the two decimal places format be placed when the value is entered.
TextFormField value displayed when typing
TextFormField value displayed when entered
123
123.00
123.5
123.50
123.54
123.54
12,345
12,345.00
12,345.6
12,345.60
Minimal Repro
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _subscriptionFormKey = GlobalKey<FormState>();
final _subscriptionPriceController = TextEditingController();
NumberFormat numFormat = NumberFormat('###,###.00', 'en_US');
NumberFormat numSanitizedFormat = NumberFormat('en_US');
var currency = 'USD';
void _validate() {
if (_subscriptionFormKey.currentState!.validate()) {}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Form(
key: _subscriptionFormKey,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
autofocus: true,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,2}')),
],
keyboardType: TextInputType.numberWithOptions(decimal: true),
validator: (cost) {
if (cost == null || cost.isEmpty) {
return 'Empty';
}
return null;
},
textInputAction: TextInputAction.next,
/// TODO TextFormField default value
/// I'd like to be to set '0.00' value by default
controller: _subscriptionPriceController,
decoration: InputDecoration(
hintText: 'Price',
prefixText:
NumberFormat.simpleCurrency(name: currency).currencySymbol,
),
onChanged: (price) {
/// TODO Display [formattedPrice] in TextFormField
var formattedPrice = numFormat.format(double.parse(price));
debugPrint('Formatted $formattedPrice');
var numSanitized = numSanitizedFormat.parse(price);
debugPrint('Sanitized: $numSanitized');
_subscriptionPriceController.value = TextEditingValue(
text: price,
selection: TextSelection.collapsed(offset: price.length),
);
},
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: _validate,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Update:
Thanks to #Andrej for suggesting to use onFieldSubmitted() instead of onChange(). I've updated my code with the fix included. I hope this will help other developers blocked with similar issue.
Code with the fix:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _subscriptionFormKey = GlobalKey<FormState>();
final _subscriptionPriceController = TextEditingController();
final NumberFormat numFormat = NumberFormat('###,##0.00', 'en_US');
final NumberFormat numSanitizedFormat = NumberFormat('en_US');
var currency = 'USD';
var defaultPrice = '0.00';
void _validate() {
if (_subscriptionFormKey.currentState!.validate()) {}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Form(
key: _subscriptionFormKey,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
autofocus: true,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,2}')),
],
keyboardType: TextInputType.numberWithOptions(decimal: true),
validator: (cost) {
if (cost == null || cost.isEmpty) {
return 'Empty';
}
return null;
},
textInputAction: TextInputAction.next,
/// Set TextFormField default value
controller: _subscriptionPriceController..text = defaultPrice,
decoration: InputDecoration(
hintText: 'Price',
prefixText:
NumberFormat.simpleCurrency(name: currency).currencySymbol,
),
onTap: () {
var textFieldNum = _subscriptionPriceController.value.text;
var numSanitized = numSanitizedFormat.parse(textFieldNum);
_subscriptionPriceController.value = TextEditingValue(
/// Clear if TextFormField value is 0
text: numSanitized == 0 ? '' : '$numSanitized',
selection:
TextSelection.collapsed(offset: '$numSanitized'.length),
);
},
onFieldSubmitted: (price) {
/// Set value to 0 if TextFormField value is empty
if (price == '') price = '0';
final formattedPrice = numFormat.format(double.parse(price));
debugPrint('Formatted $formattedPrice');
_subscriptionPriceController.value = TextEditingValue(
text: formattedPrice,
selection:
TextSelection.collapsed(offset: formattedPrice.length),
);
},
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: _validate,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

If I understood you correctly, you want the price to be formatted when the text field is entered.
If yes, just add this code to your TextFormField:
onFieldSubmitted: (price) {
final formattedPrice = numFormat.format(double.parse(price));
debugPrint('Formatted $formattedPrice');
_subscriptionPriceController.value = TextEditingValue(
text: formattedPrice,
selection:
TextSelection.collapsed(offset: formattedPrice.length),
);
},
To add the initial value to the text field:
final _subscriptionPriceController = TextEditingController(text: '0.00');

Related

flutter number textfield with right symbol

i am trying to implement text field with showing symbol of height at the right of the text. for example when user enter any input the TextField must be like:
180 cm
as you see above cm should be appear whenever any text changed and shouldn't be removable.
i am trying to achieve it by using following library but could success. any suggestion?
https://pub.dev/packages/flutter_masked_text2
here is what I tried:
var heightController = MaskedTextController(
mask: '000 cm',
);
you can try this
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Postfix Text Controller',
theme: ThemeData(
primarySwatch: Colors.blue,
brightness: Brightness.dark,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _textController = TextEditingController();
final String _userPostfix = " cm";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text("Postfix Text Controller"),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
controller: _textController,
onChanged: (value) {
if (value == _userPostfix) {
_textController.text = "";
return;
}
value.endsWith(_userPostfix)
? _textController.text = value
: _textController.text = value + _userPostfix;
_textController.selection = TextSelection.fromPosition(
TextPosition(
offset: _textController.text.length -
_userPostfix.length));
},
decoration: const InputDecoration(
labelText: "Height",
border: OutlineInputBorder(),
),
),
],
),
),
),
);
}
}
You can try this
TextField(
controller: _textController,
keyboardType: TextInputType.number,
onChanged: (val) {
_textController.text =
val.replaceAll("c", "").replaceAll("m", "") + " cm";
_textController.selection = TextSelection.fromPosition(
TextPosition(
offset: _textController.text.length - 3));
},
),

Flutter - multiply the values ​from different text fields

I need your help please,
I want to multiply the values ​​from different text fields. I am facing the problem that * cant be used with TexteditingController() in:
void _calculation() {
_volume = lenCon * widCon * higCon;
print(_volume);
}
Do I need to transfrom my Variables like lenCon to double before? How can I do that? var lenConInt = double.parse(lenCon); doesnt work.
The RaisedButton must execute _calculation and change the Text with the proper Value. Do I need a StatlessWidget for that or SetState?
Thanks in Advance!
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _volume = 0;
// var _lenght;
// var _width;
// var _hight;
void _calculation() {
_volume = lenCon * widCon * higCon;
print(_volume);
}
final lenCon = new TextEditingController();
final widCon = new TextEditingController();
final higCon = new TextEditingController();
// var lenConInt = double.parse(lenCon);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
controller: lenCon,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'Länge',
),
),
TextField(
controller: widCon,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'Breite',
),
),
TextField(
controller: higCon,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'Höhe',
),
),
RaisedButton(
onPressed: (_calculation),
child: Text('Berechnen'),
),
Text('Your Volume is: $_volume'),
],
),
),
),
);
}
}
Try this in order to get it functional:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _volume;
#override
initState(){
_volume = 0;
}
void _calculation() {
setState((){
_volume = int.parse(lenCon.text) * int.parse(widCon.text) * int.parse(higCon.text);
},
);
print(_volume);
}
final lenCon = TextEditingController();
final widCon = TextEditingController();
final higCon = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
controller: lenCon,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'Länge',
),
),
TextField(
controller: widCon,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'Breite',
),
),
TextField(
controller: higCon,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'Höhe',
),
),
RaisedButton(
onPressed: (_calculation),
child: Text('Berechnen'),
),
Text('Your Volume is: $_volume'),
],
),
),
),
);
}
}
First, you need some type of conversion between string and integers (or even double if you want). Second, you should put the resulting _volume in a state so that the UI refreshes when you update the state (via setState). Try to avoid var and use concrete types instead. It will help you in the long-run.
The variables lenCon, widCon and higCon are of type TextEditingConroller. The TextEditingController is a class that will help you control the information in the TextField.
In this case you'd probably want to access the value the user inputted into the TextField which you'd be able to access using the text property of the TextEditorController. If you'd update your calculation method to something like below it should work:
void _calculation() {
final length = double.parse(lenCon.text);
final width = double.parse(widCon.text);
final height = double.parse(higCon.text);
_volume = length * width * height;
print(_volume);
}
You still need to use the double.parse since the .text property will return a String which needs to be converted to something you make calculations on (a numeric value type).
As mentioned the TextEditingController will give you access to more information about the user's interaction with the TextField. For example the selected part of the text (using the selection property). If you would like to read up you could check out: https://api.flutter.dev/flutter/widgets/TextEditingController-class.html
Thanks to Maurits van Beusekom I was able to get the calculation done.
But I had to move it to the Button to make the Text('Your Volume is: $_volume'), working.
RaisedButton(
onPressed: () {
setState(() {
final length = double.parse(lenCon.text);
final width = double.parse(widCon.text);
final height = double.parse(higCon.text);
_volume = length * width * height;
print(_volume);
});
},
child: Text('Berechnen'),
),
Edit:
Nevermind, I just have to put the SetState into the calculation.

In Flutter check a checkbox -> disabled checkbox and text field should be enabled in checkbox check

I am new to Flutter.
If I click a check box action should be performed.
Eg: Click a checkbox, enable the other checkbox and enable a text field
disable or enable widget only for button click is available.
I don't know how to do it in flutter
The idea is to use ternary operator ( ? : ) this works same as if does. Most basic explanation about code below when the checkbox is triggered checkBox1 changes and widget rebuilds with checkBox1 equals true now so instead of empty Container currently we are building new CheckBox.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool checkBox1 = false;
bool checkBox2 = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: [
CheckboxListTile(
title: Text("title text"),
value: checkBox1,
onChanged: (newValue) {
setState(() {
checkBox1 = newValue;
});
},
),
checkBox1
? CheckboxListTile(
title: Text("title text2"),
value: checkBox2,
onChanged: (newValue) {
setState(() {
checkBox2 = newValue;
});
},
)
: Container(),
],
),
);
}
}
final TextEditingController _controllerTE =
TextEditingController();
bool cbFlag = false;
TextField(
readOnly: !cbFlag,
controller: _controllerTE,
decoration: InputDecoration(
labelText: 'TE disabled till CB checked',
prefixIcon: Checkbox(
value: cbFlag,
onChanged: (bool? value) {
setState(() {
cbFlag = value!;
});
},
),
),
),

DropdownButtonFormField assertion fails where DropdownButtonHideUnderline doesn't

This works with DropdownButtonHideUnderline, but does not work with DropdownButtonFormField. I want the inputDecoration that I get with DropdownButtonFormField, but this code fails at runtime when I change the project.
I either need to fix it to run with DropdownButtonFormField or I should find a way to get the inputDecoration added to the DropdownButtonHideUnderline;
At runtime the error that comes out is:
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
'package:flutter/src/material/dropdown.dart':
Failed assertion: line 827 pos 15: 'items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) {
return item.value == value;
}).length == 1'
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
Map data = {
'Project 1': ['Entrance', 'Main Hallway', 'Kitchen'],
'Project 2': ['Patio', 'Dining Room'],
};
class _MyHomePageState extends State<MyHomePage> {
String _project;
String _room;
List<String> _roomList = [];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: <Widget>[
DropdownButtonFormField(
decoration: InputDecoration(labelText: 'Project'),
value: _project,
onChanged: (value) {
setState(() {
_project = value;
_room = null;
_roomList = data[_project];
});
},
items: data.keys.map((item) {
return DropdownMenuItem(
child: Text(item),
value: item,
);
})?.toList() ??
[],
),
DropdownButtonFormField(
decoration: InputDecoration(labelText: 'Room'),
value: _room,
onChanged: (value) {
setState(() {
_room = value;
print(_project);
print(_room);
});
},
items: _roomList.map((item) {
return DropdownMenuItem(
child: Text(item),
value: item,
);
})?.toList() ??
[],
),
],
),
));
}
}
You can copy paste run full code below
When Dropdown list data is totally different will trigger this error
For Room DropdownButtonFormField You can use key: UniqueKey() and widget will recreate
code snippet
DropdownButtonFormField(
key: UniqueKey(),
decoration: InputDecoration(labelText: 'Room'),
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
Map data = {
'Project 1': ['Entrance', 'Main Hallway', 'Kitchen'],
'Project 2': ['Patio', 'Dining Room'],
};
class _MyHomePageState extends State<MyHomePage> {
String _project;
String _room;
List<String> _roomList = [];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: <Widget>[
DropdownButtonFormField(
decoration: InputDecoration(labelText: 'Project'),
value: _project,
onChanged: (value) {
setState(() {
_project = value;
_room = null;
_roomList = data[_project];
});
},
items: data.keys.map((item) {
return DropdownMenuItem(
child: Text(item),
value: item,
);
})?.toList() ??
[],
),
DropdownButtonFormField(
key: UniqueKey(),
decoration: InputDecoration(labelText: 'Room'),
value: _room,
onChanged: (value) {
setState(() {
_room = value;
print(_project);
print(_room);
});
},
items: _roomList.map((item) {
return DropdownMenuItem(
child: Text(item),
value: item,
);
})?.toList() ??
[],
),
],
),
));
}
}

How can I enable Tab in Textfield of flutter( web version)?

I m working on a web project using Flutter web, I understand currently Flutter web is only in beta version.
Basically I m implementing a web code editor using textfield, If the user press TAB key I want to make an indent as usual. However when I press tab it either move to next element (let say I have a button below the textarea) or it do not indent at all. Anyone know how to solve this?
example code below :
Widget build(BuildContext context) {
return Container(
color: Colors.black87,
child: Padding(
padding: EdgeInsets.all(8.0),
child:TextField(
controller: controller,
onEditingComplete: (){print('editing complete');},
onTap: (){},
onChanged: (text){print(text);},
style: TextStyle(fontStyle: FontStyle.italic,color: Colors.white),
maxLines: 20,
autofocus: true,
decoration: InputDecoration.collapsed(hintText: "write code for the formation"),
),
)
);
}
}
I've accomplished adding tabs with something like this:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.dark(),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class InsertTabIntent extends Intent {
const InsertTabIntent(this.numSpaces, this.textController);
final int numSpaces;
final TextEditingController textController;
}
class InsertTabAction extends Action {
#override
Object invoke(covariant Intent intent) {
if (intent is InsertTabIntent) {
final oldValue = intent.textController.value;
final newComposing = TextRange.collapsed(oldValue.composing.start);
final newSelection = TextSelection.collapsed(
offset: oldValue.selection.start + intent.numSpaces);
final newText = StringBuffer(oldValue.selection.isValid
? oldValue.selection.textBefore(oldValue.text)
: oldValue.text);
for (var i = 0; i < intent.numSpaces; i++) {
newText.write(' ');
}
newText.write(oldValue.selection.isValid
? oldValue.selection.textAfter(oldValue.text)
: '');
intent.textController.value = intent.textController.value.copyWith(
composing: newComposing,
text: newText.toString(),
selection: newSelection,
);
}
return '';
}
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController textController;
#override
void initState() {
super.initState();
textController = TextEditingController();
}
#override
void dispose() {
textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Actions(
actions: {InsertTabIntent: InsertTabAction()},
child: Shortcuts(
shortcuts: {
LogicalKeySet(LogicalKeyboardKey.tab):
InsertTabIntent(2, textController)
},
child: TextField(
controller: textController,
textInputAction: TextInputAction.newline,
maxLines: 30,
keyboardType: TextInputType.multiline,
),
),
),
],
),
),
);
}
}