Below is the code I have tried but it completely fails. If I Remove the WhitelistingTextInputFormatter, I get the Number keyboard and I can insert numbers and periods. but the number of period I can use is more than one, which I need to limit at just one. how to do this?

            TextField(
                  controller: _weightCtr,
                  keyboardType: TextInputType.numberWithOptions(decimal: true),
                  inputFormatters: [
                    BlacklistingTextInputFormatter(new RegExp('[\\-|\\ ]')),
                    WhitelistingTextInputFormatter(new RegExp('^\d+[\.\,]\d+\$')),
                  ],
                  decoration: InputDecoration(
                    hintText: "Please enter a valid weight for this trip",
                  ),
                  style: Theme.of(context).textTheme.title.copyWith(
                    fontWeight: FontWeight.w300,
                    fontSize: 14,
                  ),
                ),


Solution 1: chunhunghan

You can copy paste run full code below
You can extend TextInputFormatter and remove extra dot
In working demo you can see when extra dot will not show on screen

code snippet

class NumberRemoveExtraDotFormatter extends TextInputFormatter {
  NumberRemoveExtraDotFormatter({this.decimalRange = 3})

if (nValue.split('.').length > 2) {
              List<String> split = nValue.split('.');
              nValue = split[0] + '.' + split[1];
            }
...         
TextField(
          controller: _weightCtr,
          keyboardType: TextInputType.numberWithOptions(decimal: true),
          inputFormatters: [
            NumberRemoveExtraDotFormatter()
            //BlacklistingTextInputFormatter(new RegExp('[\\-|\\ ]')),
            //WhitelistingTextInputFormatter(new RegExp('^\d+[\.\,]\d+\$')),
          ],
          decoration: InputDecoration(
            hintText: "Please enter a valid weight for this trip",
          ),
          style: Theme.of(context).textTheme.title.copyWith(
                fontWeight: FontWeight.w300,
                fontSize: 14,
              ),
        ),          

working demo

enter image description here

full code

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:math' as math;

class NumberRemoveExtraDotFormatter extends TextInputFormatter {
  NumberRemoveExtraDotFormatter({this.decimalRange = 3})
      : assert(decimalRange == null || decimalRange > 0);

  final int decimalRange;

  @override
  TextEditingValue formatEditUpdate(
      TextEditingValue oldValue, TextEditingValue newValue) {
    String nValue = newValue.text;
    TextSelection 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) {
          List<String> 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: nValue, selection: nSelection, composing: TextRange.empty);
  }
}

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, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  TextEditingController _weightCtr = TextEditingController();

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            TextField(
              controller: _weightCtr,
              keyboardType: TextInputType.numberWithOptions(decimal: true),
              inputFormatters: [
                NumberRemoveExtraDotFormatter()
                //BlacklistingTextInputFormatter(new RegExp('[\\-|\\ ]')),
                //WhitelistingTextInputFormatter(new RegExp('^\d+[\.\,]\d+\$')),
              ],
              decoration: InputDecoration(
                hintText: "Please enter a valid weight for this trip",
              ),
              style: Theme.of(context).textTheme.title.copyWith(
                    fontWeight: FontWeight.w300,
                    fontSize: 14,
                  ),
            ),
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}