import 'package:diameter/components/repeat_on_hold_button.dart'; import 'package:diameter/utils/utils.dart'; import 'package:flutter/material.dart'; class NumberFormField extends StatefulWidget { final TextEditingController controller; final double min; final double? max; final double step; final String label; final String? suffix; final void Function(double?) onChanged; final bool readOnly; final bool showSteppers; final bool autoRoundToMultipleOfStep; final String? Function(String?)? validator; const NumberFormField({ Key? key, required this.controller, required this.label, required this.onChanged, this.suffix, this.min = 0, this.max, this.step = 1, this.readOnly = false, this.showSteppers = true, this.autoRoundToMultipleOfStep = false, this.validator, }) : super(key: key); @override _NumberFormFieldState createState() => _NumberFormFieldState(); } class _NumberFormFieldState extends State { int precision = 1; @override void initState() { super.initState(); precision = Utils.getFractionDigitsLength(widget.step) + 1; } bool onIncrease() { double? currentValue = double.tryParse(widget.controller.text); if (currentValue != null && (widget.max == null || currentValue + widget.step <= widget.max!)) { widget.onChanged( Utils.addDoublesWithPrecision(currentValue, widget.step, precision)); setState(() {}); return true; } return false; } bool onDecrease() { double? currentValue = double.tryParse(widget.controller.text); if (currentValue != null && (currentValue - widget.step >= widget.min)) { widget.onChanged( Utils.addDoublesWithPrecision(currentValue, -widget.step, precision)); setState(() {}); return true; } return false; } @override Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.min, children: [ widget.showSteppers ? RepeatOnHoldButton( onTap: onDecrease, child: IconButton( onPressed: double.tryParse(widget.controller.text) != null && (double.parse(widget.controller.text) - widget.step >= widget.min) ? onDecrease : null, icon: const Icon(Icons.remove), ), ) : Container(), Flexible( child: TextFormField( readOnly: widget.readOnly, controller: widget.controller, decoration: InputDecoration( labelText: widget.label, suffixText: widget.suffix, ), keyboardType: TextInputType.numberWithOptions( decimal: widget.step > 0 && widget.step < 1, signed: widget.min.isNegative), onChanged: (input) async { await Future.delayed(const Duration(seconds: 1)); double? value = double.tryParse(input); if (widget.autoRoundToMultipleOfStep) { value = value != null ? Utils.roundToMultipleOfBase(value, widget.step) : null; } widget.onChanged(value); }, validator: widget.validator, ), ), widget.showSteppers ? RepeatOnHoldButton( onTap: onIncrease, child: IconButton( onPressed: double.tryParse(widget.controller.text) != null && (widget.max == null || double.parse(widget.controller.text) + widget.step <= widget.max!) ? onIncrease : null, icon: const Icon(Icons.add), ), ) : Container(), ], ); } }