title: Flutter TextFormField display value with NumberFormat
body: <p>I'd like to be able to display the value entered in TextFormField with <code>NumberFormat('###,##0.00', 'en_US')</code>.</p>
  <p>There are no issues in the conversion, but I'm having issues displaying the value of <code>formattedPrice</code> in the TextFormField. I'd also like to set the default value of TextFormField as <code>'0.00'</code>. In my sample code below, the TextFormField restricts input to only have a single decimal symbol/separator and two decimal values.</p>
  <p>I'm fine with <code>.00</code> 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.</p>
  <div class="s-table-container">
  <table class="s-table">
  <thead>
  <tr>
  <th>TextFormField value displayed when typing</th>
  <th>TextFormField value displayed when entered</th>
  </tr>
  </thead>
  <tbody>
  <tr>
  <td>123</td>
  <td>123.00</td>
  </tr>
  <tr>
  <td>123.5</td>
  <td>123.50</td>
  </tr>
  <tr>
  <td>123.54</td>
  <td>123.54</td>
  </tr>
  <tr>
  <td>12,345</td>
  <td>12,345.00</td>
  </tr>
  <tr>
  <td>12,345.6</td>
  <td>12,345.60</td>
  </tr>
  </tbody>
  </table>
  </div><h3>Minimal Repro</h3>
  <pre class="lang-dart prettyprint-override"><code>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() =&gt; _MyHomePageState();
  }
  
  class _MyHomePageState extends State&lt;MyHomePage&gt; {
    final _subscriptionFormKey = GlobalKey&lt;FormState&gt;();
    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),
        ),
      );
    }
  }
  </code></pre>
  <h2>Update:</h2>
  <p>Thanks to @Andrej for suggesting to use <code>onFieldSubmitted()</code> instead of <code>onChange()</code>. I've updated my code with the fix included. I hope this will help other developers blocked with similar issue.</p>
  <h3>Code with the fix:</h3>
  <pre class="lang-dart prettyprint-override"><code>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() =&gt; _MyHomePageState();
  }
  
  class _MyHomePageState extends State&lt;MyHomePage&gt; {
    final _subscriptionFormKey = GlobalKey&lt;FormState&gt;();
    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),
        ),
      );
    }
  }
  </code></pre>
  <p><a href="https://i.stack.imgur.com/8dprG.gif" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/8dprG.gif" alt="Demo" /></a></p>