130 lines
3.9 KiB
Dart
130 lines
3.9 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(),
|
|
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(),
|
|
],
|
|
);
|
|
}
|
|
}
|