138 lines
4.2 KiB
Dart
138 lines
4.2 KiB
Dart
|
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<NumberFormField> {
|
||
|
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(),
|
||
|
Expanded(
|
||
|
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 (value != null &&
|
||
|
widget.autoRoundToMultipleOfStep &&
|
||
|
(value % widget.step != 0)) {
|
||
|
double remainder = value % widget.step;
|
||
|
value =
|
||
|
Utils.addDoublesWithPrecision(value, -remainder, precision);
|
||
|
if (remainder > widget.step / 2) {
|
||
|
value = Utils.addDoublesWithPrecision(
|
||
|
value, widget.step, precision);
|
||
|
}
|
||
|
}
|
||
|
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(),
|
||
|
],
|
||
|
);
|
||
|
}
|
||
|
}
|