| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- // ignore_for_file: library_private_types_in_public_api, depend_on_referenced_packages
- import 'package:intl/intl.dart';
- import '../icon_button_widget/icon_button_widget.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- /// quantity input type
- /// [int] default mask type, to format value as int
- /// [double] to format value as double
- enum QuantityInputType {
- int,
- double,
- }
- class QuantityInput extends StatefulWidget {
- /// Has to be an int or double depending on QuantityInputType variable
- final dynamic value;
- /// Detects changes to the input and sends value through param
- final Function(String) onChanged;
- /// The value that is incremented or decremented each time the user presses a button
- final dynamic step;
- /// The number of decimal places that can be displayed for double input
- final int decimalDigits;
- /// Set min value to be displayed in input
- final dynamic minValue;
- /// Set max value to be displayed in input
- final dynamic maxValue;
- /// The width of the textfield input
- final double inputWidth;
- /// Sets color for increment and decrement buttons
- final Color? buttonColor;
- /// Sets color for icons inside increment and decrement buttons
- final Color? iconColor;
- /// Sets label for input
- final String label;
- /// Determines if the input will be readOnly
- final bool readOnly;
- /// If set to true, the input can accept the value 0
- final bool acceptsZero;
- /// If set to true, the input can accept negative values
- final bool acceptsNegatives;
- /// Determines if the input will manage values as int or double
- final QuantityInputType? type;
- /// Sets custom InputDecoration to the widget TextFormField
- final InputDecoration? decoration;
- /// Sets elevation to increment and decrement buttons
- final double elevation;
- /// Creates a widget that can be used to manage number inputs
- ///
- /// Widget can manage integer or double values
- const QuantityInput(
- {super.key,
- required this.value,
- required this.onChanged,
- this.step = 1,
- this.decimalDigits = 1,
- this.buttonColor,
- this.iconColor,
- this.label = '',
- this.readOnly = false,
- this.acceptsNegatives = false,
- this.acceptsZero = false,
- this.minValue = 1,
- this.maxValue = 100000,
- this.type = QuantityInputType.int,
- this.inputWidth = 80,
- this.decoration,
- this.elevation = 5})
- : assert(decimalDigits > 0,
- 'Decimal digits cannot be set to zero or negative value'),
- assert(!(acceptsZero && acceptsNegatives),
- 'acceptsZero and acceptsNegatives cannot be simultaneously true');
- @override
- _QuantityInputState createState() => _QuantityInputState();
- }
- class _QuantityInputState extends State<QuantityInput> {
- final TextEditingController _controller = TextEditingController();
- @override
- void initState() {
- super.initState();
- _controller.text = valueFormatter(widget.value);
- }
- String valueFormatter(dynamic value) {
- String extraZeros = '';
- if (widget.decimalDigits > 0 && widget.type == QuantityInputType.double) {
- extraZeros = '.';
- extraZeros = extraZeros.padRight(widget.decimalDigits + 1, '0');
- }
- NumberFormat formatter = NumberFormat(
- '#,###,###,###,###,###,###,###,###,##0$extraZeros', 'en_US');
- return formatter.format(value);
- }
- String parseNewDouble(String value) {
- String fullPartOfDouble =
- value.substring(0, value.length - widget.decimalDigits);
- String decimalPartOfDouble =
- value.substring(value.length - widget.decimalDigits);
- String newFullValue = '$fullPartOfDouble.$decimalPartOfDouble';
- return newFullValue;
- }
- @override
- Widget build(BuildContext context) {
- return Padding(
- padding: const EdgeInsets.symmetric(vertical: 5),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- mainAxisAlignment: MainAxisAlignment.start,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Visibility(
- visible: widget.label.isNotEmpty,
- child: Padding(
- padding: const EdgeInsets.only(bottom: 5),
- child: Text(widget.label,
- textAlign: TextAlign.start,
- style: TextStyle(
- color: Colors.grey[500],
- fontSize: 13,
- fontWeight: FontWeight.w500)))),
- Row(
- mainAxisSize: MainAxisSize.min,
- mainAxisAlignment: MainAxisAlignment.start,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- IconButtonWidget(
- icon: Icons.remove,
- iconColor: widget.iconColor,
- buttonColor: widget.buttonColor,
- elevation: widget.elevation,
- onTap: () {
- dynamic currentValue = 0;
- if (widget.acceptsNegatives ||
- ((widget.value - widget.step) > 0 &&
- widget.type == QuantityInputType.double) ||
- ((widget.value - widget.step) >= 1 &&
- widget.type == QuantityInputType.int) ||
- (widget.acceptsZero &&
- (widget.value - widget.step) == 0)) {
- currentValue = widget.value - widget.step;
- } else {
- currentValue = widget.value;
- }
- String formattedValue = valueFormatter(currentValue);
- _controller.value = TextEditingValue(
- text: formattedValue,
- selection: TextSelection.collapsed(
- offset: formattedValue.length));
- widget.onChanged(formattedValue);
- }),
- SizedBox(
- height: 38,
- width: widget.inputWidth,
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 5),
- child: TextFormField(
- controller: _controller,
- textAlign: TextAlign.center,
- decoration: widget.decoration,
- readOnly: widget.readOnly,
- keyboardType: TextInputType.number,
- inputFormatters: [
- TextInputFormatter.withFunction(
- (oldValue, newValue) {
- String simpleValue = '';
- String formattedValue = '';
- if (newValue.text
- .contains(RegExp(r'[a-zA-Z]'))) {
- return oldValue;
- }
- if (widget.type ==
- QuantityInputType.double) {
- simpleValue = newValue.text
- .replaceAll(',', '')
- .replaceAll('.', '');
- if (simpleValue.length == 1) {
- simpleValue =
- simpleValue.padLeft(1, '0');
- }
- formattedValue = valueFormatter(
- double.parse(
- parseNewDouble(simpleValue)));
- } else {
- if (newValue.text.isEmpty) {
- formattedValue = '0';
- } else {
- simpleValue =
- newValue.text.replaceAll(',', '');
- formattedValue = valueFormatter(
- int.parse(simpleValue));
- }
- }
- return TextEditingValue(
- text: formattedValue,
- selection: TextSelection.collapsed(
- offset: formattedValue.length));
- })
- ],
- onChanged: (value) =>
- widget.onChanged(value)))),
- IconButtonWidget(
- icon: Icons.add,
- iconColor: widget.iconColor,
- buttonColor: widget.buttonColor,
- elevation: widget.elevation,
- onTap: () {
- dynamic currentValue = 0;
- if (widget.maxValue >= widget.value &&
- widget.maxValue >= widget.value + widget.step) {
- currentValue = widget.value + widget.step;
- } else {
- currentValue = widget.value;
- }
- String formattedValue = valueFormatter(currentValue);
- _controller.value = TextEditingValue(
- text: formattedValue,
- selection: TextSelection.collapsed(
- offset: formattedValue.length));
- widget.onChanged(formattedValue);
- })
- ])
- ]));
- }
- }
|