Handle quick changes in line items
This commit is contained in:
parent
bf569242c6
commit
3d34cfa06a
|
|
@ -264,308 +264,370 @@ class _InvoiceEditItemsDesktopState extends State<InvoiceEditItemsDesktop> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
*/
|
*/
|
||||||
Padding(
|
Focus(
|
||||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
onFocusChange: (hasFocus) => Debouncer.complete(),
|
||||||
child: TypeAheadFormField<String>(
|
skipTraversal: true,
|
||||||
key: ValueKey('__line_item_${index}_name__'),
|
child: Padding(
|
||||||
initialValue: lineItems[index].productKey,
|
padding:
|
||||||
noItemsFoundBuilder: (context) => SizedBox(),
|
const EdgeInsets.only(right: kTableColumnGap),
|
||||||
suggestionsCallback: (pattern) {
|
child: TypeAheadFormField<String>(
|
||||||
return productIds
|
key: ValueKey('__line_item_${index}_name__'),
|
||||||
.where((productId) => productState
|
initialValue: lineItems[index].productKey,
|
||||||
.map[productId].productKey
|
noItemsFoundBuilder: (context) => SizedBox(),
|
||||||
.toLowerCase()
|
suggestionsCallback: (pattern) {
|
||||||
.contains(pattern.toLowerCase()))
|
return productIds
|
||||||
.toList();
|
.where((productId) => productState
|
||||||
/*
|
.map[productId].productKey
|
||||||
return productIds
|
.toLowerCase()
|
||||||
.where((productId) => productState.map[productId]
|
.contains(pattern.toLowerCase()))
|
||||||
.matchesFilter(pattern))
|
.toList();
|
||||||
.toList();
|
/*
|
||||||
*/
|
return productIds
|
||||||
},
|
.where((productId) => productState.map[productId]
|
||||||
itemBuilder: (context, productId) {
|
.matchesFilter(pattern))
|
||||||
// TODO fix this
|
.toList();
|
||||||
/*
|
*/
|
||||||
return ListTile(
|
},
|
||||||
title: Text(productState.map[suggestion].productKey),
|
itemBuilder: (context, productId) {
|
||||||
);
|
// TODO fix this
|
||||||
*/
|
/*
|
||||||
return Listener(
|
return ListTile(
|
||||||
child: Container(
|
title: Text(productState.map[suggestion].productKey),
|
||||||
color: Theme.of(context).cardColor,
|
);
|
||||||
child: ListTile(
|
*/
|
||||||
title: Text(
|
return Listener(
|
||||||
productState.map[productId].productKey),
|
child: Container(
|
||||||
|
color: Theme.of(context).cardColor,
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(
|
||||||
|
productState.map[productId].productKey),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
onPointerDown: (_) {
|
||||||
onPointerDown: (_) {
|
if (!kIsWeb) {
|
||||||
if (!kIsWeb) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
final item = lineItems[index];
|
final item = lineItems[index];
|
||||||
final product = productState.map[productId];
|
final product = productState.map[productId];
|
||||||
final client =
|
final client =
|
||||||
state.clientState.get(invoice.clientId);
|
state.clientState.get(invoice.clientId);
|
||||||
final currency = state
|
final currency = state.staticState
|
||||||
.staticState.currencyMap[client.currencyId];
|
.currencyMap[client.currencyId];
|
||||||
|
|
||||||
double cost = product.price;
|
double cost = product.price;
|
||||||
if (company.convertProductExchangeRate &&
|
if (company.convertProductExchangeRate &&
|
||||||
invoice.clientId != null &&
|
invoice.clientId != null &&
|
||||||
client.currencyId != company.currencyId) {
|
client.currencyId != company.currencyId) {
|
||||||
cost = round(cost * invoice.exchangeRate,
|
cost = round(cost * invoice.exchangeRate,
|
||||||
currency.precision);
|
currency.precision);
|
||||||
}
|
}
|
||||||
|
|
||||||
final updatedItem = item.rebuild((b) => b
|
final updatedItem = item.rebuild((b) => b
|
||||||
..productKey = product.productKey
|
..productKey = product.productKey
|
||||||
..notes =
|
..notes =
|
||||||
item.isTask ? item.notes : product.notes
|
item.isTask ? item.notes : product.notes
|
||||||
..cost = item.isTask && item.cost != 0
|
..cost = item.isTask && item.cost != 0
|
||||||
? item.cost
|
? item.cost
|
||||||
: cost
|
: cost
|
||||||
..quantity = item.isTask || item.quantity != 0
|
..quantity =
|
||||||
? item.quantity
|
item.isTask || item.quantity != 0
|
||||||
: viewModel.state.company.defaultQuantity
|
? item.quantity
|
||||||
? 1
|
: viewModel.state.company
|
||||||
: product.quantity
|
.defaultQuantity
|
||||||
..customValue1 = product.customValue1
|
? 1
|
||||||
..customValue2 = product.customValue2
|
: product.quantity
|
||||||
..customValue3 = product.customValue3
|
..customValue1 = product.customValue1
|
||||||
..customValue4 = product.customValue4
|
..customValue2 = product.customValue2
|
||||||
..taxRate1 = product.taxRate1
|
..customValue3 = product.customValue3
|
||||||
..taxName1 = product.taxName1
|
..customValue4 = product.customValue4
|
||||||
..taxRate2 = product.taxRate2
|
..taxRate1 = product.taxRate1
|
||||||
..taxName2 = product.taxName2
|
..taxName1 = product.taxName1
|
||||||
..taxRate3 = product.taxRate3
|
..taxRate2 = product.taxRate2
|
||||||
..taxName3 = product.taxName3);
|
..taxName2 = product.taxName2
|
||||||
_onChanged(updatedItem, index, debounce: false);
|
..taxRate3 = product.taxRate3
|
||||||
_updateTable();
|
..taxName3 = product.taxName3);
|
||||||
},
|
_onChanged(updatedItem, index,
|
||||||
);
|
debounce: false);
|
||||||
},
|
_updateTable();
|
||||||
onSuggestionSelected: (suggestion) {
|
},
|
||||||
if (kIsWeb) {
|
);
|
||||||
return;
|
},
|
||||||
}
|
onSuggestionSelected: (suggestion) {
|
||||||
|
if (kIsWeb) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final item = lineItems[index];
|
final item = lineItems[index];
|
||||||
final product = productState.map[suggestion];
|
final product = productState.map[suggestion];
|
||||||
final client =
|
final client =
|
||||||
state.clientState.get(invoice.clientId);
|
state.clientState.get(invoice.clientId);
|
||||||
|
|
||||||
double cost = product.price;
|
double cost = product.price;
|
||||||
if (company.convertProductExchangeRate &&
|
if (company.convertProductExchangeRate &&
|
||||||
invoice.clientId != null &&
|
invoice.clientId != null &&
|
||||||
client.currencyId != company.currencyId) {
|
client.currencyId != company.currencyId) {
|
||||||
cost = round(
|
cost = round(
|
||||||
cost * invoice.exchangeRate,
|
cost * invoice.exchangeRate,
|
||||||
state
|
state
|
||||||
.staticState
|
.staticState
|
||||||
.currencyMap[client?.currencyId ??
|
.currencyMap[client?.currencyId ??
|
||||||
company.currencyId]
|
company.currencyId]
|
||||||
.precision);
|
.precision);
|
||||||
}
|
}
|
||||||
final updatedItem = item.rebuild((b) => b
|
final updatedItem = item.rebuild((b) => b
|
||||||
..productKey = product.productKey
|
..productKey = product.productKey
|
||||||
..notes = item.isTask ? item.notes : product.notes
|
..notes =
|
||||||
..cost = item.isTask && item.cost != 0
|
item.isTask ? item.notes : product.notes
|
||||||
? item.cost
|
..cost = item.isTask && item.cost != 0
|
||||||
: cost
|
? item.cost
|
||||||
..quantity = item.isTask || item.quantity != 0
|
: cost
|
||||||
? item.quantity
|
..quantity = item.isTask || item.quantity != 0
|
||||||
: viewModel.state.company.defaultQuantity
|
? item.quantity
|
||||||
? 1
|
: viewModel.state.company.defaultQuantity
|
||||||
: product.quantity
|
? 1
|
||||||
..customValue1 = product.customValue1
|
: product.quantity
|
||||||
..customValue2 = product.customValue2
|
..customValue1 = product.customValue1
|
||||||
..customValue3 = product.customValue3
|
..customValue2 = product.customValue2
|
||||||
..customValue4 = product.customValue4
|
..customValue3 = product.customValue3
|
||||||
..taxRate1 = product.taxRate1
|
..customValue4 = product.customValue4
|
||||||
..taxName1 = product.taxName1
|
..taxRate1 = product.taxRate1
|
||||||
..taxRate2 = product.taxRate2
|
..taxName1 = product.taxName1
|
||||||
..taxName2 = product.taxName2
|
..taxRate2 = product.taxRate2
|
||||||
..taxRate3 = product.taxRate3
|
..taxName2 = product.taxName2
|
||||||
..taxName3 = product.taxName3);
|
..taxRate3 = product.taxRate3
|
||||||
_onChanged(updatedItem, index, debounce: false);
|
..taxName3 = product.taxName3);
|
||||||
_updateTable();
|
_onChanged(updatedItem, index, debounce: false);
|
||||||
},
|
_updateTable();
|
||||||
textFieldConfiguration:
|
},
|
||||||
TextFieldConfiguration(onChanged: (value) {
|
textFieldConfiguration:
|
||||||
_onChanged(
|
TextFieldConfiguration(onChanged: (value) {
|
||||||
lineItems[index]
|
_onChanged(
|
||||||
.rebuild((b) => b..productKey = value),
|
lineItems[index]
|
||||||
index);
|
.rebuild((b) => b..productKey = value),
|
||||||
}),
|
index);
|
||||||
autoFlipDirection: true,
|
}),
|
||||||
animationStart: 1,
|
autoFlipDirection: true,
|
||||||
debounceDuration: Duration(seconds: 0),
|
animationStart: 1,
|
||||||
)),
|
debounceDuration: Duration(seconds: 0),
|
||||||
Padding(
|
)),
|
||||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
),
|
||||||
child: GrowableFormField(
|
Focus(
|
||||||
key: ValueKey('__line_item_${index}_description__'),
|
onFocusChange: (hasFocus) => Debouncer.complete(),
|
||||||
initialValue: lineItems[index].notes,
|
skipTraversal: true,
|
||||||
onChanged: (value) => _onChanged(
|
child: Padding(
|
||||||
lineItems[index].rebuild((b) => b..notes = value),
|
padding: const EdgeInsets.only(right: kTableColumnGap),
|
||||||
index),
|
child: GrowableFormField(
|
||||||
keyboardType: TextInputType.multiline,
|
key: ValueKey('__line_item_${index}_description__'),
|
||||||
|
initialValue: lineItems[index].notes,
|
||||||
|
onChanged: (value) => _onChanged(
|
||||||
|
lineItems[index].rebuild((b) => b..notes = value),
|
||||||
|
index),
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (company.hasCustomField(customField1))
|
if (company.hasCustomField(customField1))
|
||||||
Padding(
|
Focus(
|
||||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
onFocusChange: (hasFocus) => Debouncer.complete(),
|
||||||
child: CustomField(
|
skipTraversal: true,
|
||||||
field: customField1,
|
child: Padding(
|
||||||
value: lineItems[index].customValue1,
|
padding:
|
||||||
hideFieldLabel: true,
|
const EdgeInsets.only(right: kTableColumnGap),
|
||||||
onChanged: (value) => _onChanged(
|
child: CustomField(
|
||||||
lineItems[index]
|
field: customField1,
|
||||||
.rebuild((b) => b..customValue1 = value),
|
value: lineItems[index].customValue1,
|
||||||
index),
|
hideFieldLabel: true,
|
||||||
onSavePressed: widget.entityViewModel.onSavePressed,
|
onChanged: (value) => _onChanged(
|
||||||
|
lineItems[index]
|
||||||
|
.rebuild((b) => b..customValue1 = value),
|
||||||
|
index),
|
||||||
|
onSavePressed: widget.entityViewModel.onSavePressed,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (company.hasCustomField(customField2))
|
if (company.hasCustomField(customField2))
|
||||||
Padding(
|
Focus(
|
||||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
onFocusChange: (hasFocus) => Debouncer.complete(),
|
||||||
child: CustomField(
|
skipTraversal: true,
|
||||||
field: customField2,
|
child: Padding(
|
||||||
value: lineItems[index].customValue2,
|
padding:
|
||||||
hideFieldLabel: true,
|
const EdgeInsets.only(right: kTableColumnGap),
|
||||||
onChanged: (value) => _onChanged(
|
child: CustomField(
|
||||||
lineItems[index]
|
field: customField2,
|
||||||
.rebuild((b) => b..customValue2 = value),
|
value: lineItems[index].customValue2,
|
||||||
index),
|
hideFieldLabel: true,
|
||||||
onSavePressed: widget.entityViewModel.onSavePressed,
|
onChanged: (value) => _onChanged(
|
||||||
|
lineItems[index]
|
||||||
|
.rebuild((b) => b..customValue2 = value),
|
||||||
|
index),
|
||||||
|
onSavePressed: widget.entityViewModel.onSavePressed,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (company.hasCustomField(CustomFieldType.product3))
|
if (company.hasCustomField(CustomFieldType.product3))
|
||||||
Padding(
|
Focus(
|
||||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
onFocusChange: (hasFocus) => Debouncer.complete(),
|
||||||
child: CustomField(
|
skipTraversal: true,
|
||||||
field: CustomFieldType.product3,
|
child: Padding(
|
||||||
value: lineItems[index].customValue3,
|
padding:
|
||||||
hideFieldLabel: true,
|
const EdgeInsets.only(right: kTableColumnGap),
|
||||||
onChanged: (value) => _onChanged(
|
child: CustomField(
|
||||||
lineItems[index]
|
field: CustomFieldType.product3,
|
||||||
.rebuild((b) => b..customValue3 = value),
|
value: lineItems[index].customValue3,
|
||||||
index),
|
hideFieldLabel: true,
|
||||||
onSavePressed: widget.entityViewModel.onSavePressed,
|
onChanged: (value) => _onChanged(
|
||||||
|
lineItems[index]
|
||||||
|
.rebuild((b) => b..customValue3 = value),
|
||||||
|
index),
|
||||||
|
onSavePressed: widget.entityViewModel.onSavePressed,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (company.hasCustomField(customField4))
|
if (company.hasCustomField(customField4))
|
||||||
Padding(
|
Focus(
|
||||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
onFocusChange: (hasFocus) => Debouncer.complete(),
|
||||||
child: CustomField(
|
skipTraversal: true,
|
||||||
field: customField4,
|
child: Padding(
|
||||||
value: lineItems[index].customValue4,
|
padding:
|
||||||
hideFieldLabel: true,
|
const EdgeInsets.only(right: kTableColumnGap),
|
||||||
onChanged: (value) => _onChanged(
|
child: CustomField(
|
||||||
lineItems[index]
|
field: customField4,
|
||||||
.rebuild((b) => b..customValue4 = value),
|
value: lineItems[index].customValue4,
|
||||||
index),
|
hideFieldLabel: true,
|
||||||
onSavePressed: widget.entityViewModel.onSavePressed,
|
onChanged: (value) => _onChanged(
|
||||||
|
lineItems[index]
|
||||||
|
.rebuild((b) => b..customValue4 = value),
|
||||||
|
index),
|
||||||
|
onSavePressed: widget.entityViewModel.onSavePressed,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (hasTax1)
|
if (hasTax1)
|
||||||
Padding(
|
Focus(
|
||||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
onFocusChange: (hasFocus) => Debouncer.complete(),
|
||||||
child: TaxRateDropdown(
|
skipTraversal: true,
|
||||||
onSelected: (taxRate) => _onChanged(
|
child: Padding(
|
||||||
lineItems[index].rebuild((b) => b
|
padding:
|
||||||
..taxName1 = taxRate.name
|
const EdgeInsets.only(right: kTableColumnGap),
|
||||||
..taxRate1 = taxRate.rate),
|
child: TaxRateDropdown(
|
||||||
index),
|
onSelected: (taxRate) => _onChanged(
|
||||||
labelText: null,
|
lineItems[index].rebuild((b) => b
|
||||||
initialTaxName: lineItems[index].taxName1,
|
..taxName1 = taxRate.name
|
||||||
initialTaxRate: lineItems[index].taxRate1,
|
..taxRate1 = taxRate.rate),
|
||||||
|
index),
|
||||||
|
labelText: null,
|
||||||
|
initialTaxName: lineItems[index].taxName1,
|
||||||
|
initialTaxRate: lineItems[index].taxRate1,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (hasTax2)
|
if (hasTax2)
|
||||||
Padding(
|
Focus(
|
||||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
onFocusChange: (hasFocus) => Debouncer.complete(),
|
||||||
child: TaxRateDropdown(
|
skipTraversal: true,
|
||||||
onSelected: (taxRate) => _onChanged(
|
child: Padding(
|
||||||
lineItems[index].rebuild((b) => b
|
padding:
|
||||||
..taxName2 = taxRate.name
|
const EdgeInsets.only(right: kTableColumnGap),
|
||||||
..taxRate2 = taxRate.rate),
|
child: TaxRateDropdown(
|
||||||
index),
|
onSelected: (taxRate) => _onChanged(
|
||||||
labelText: null,
|
lineItems[index].rebuild((b) => b
|
||||||
initialTaxName: lineItems[index].taxName2,
|
..taxName2 = taxRate.name
|
||||||
initialTaxRate: lineItems[index].taxRate2,
|
..taxRate2 = taxRate.rate),
|
||||||
|
index),
|
||||||
|
labelText: null,
|
||||||
|
initialTaxName: lineItems[index].taxName2,
|
||||||
|
initialTaxRate: lineItems[index].taxRate2,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (hasTax3)
|
if (hasTax3)
|
||||||
Padding(
|
Focus(
|
||||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
onFocusChange: (hasFocus) => Debouncer.complete(),
|
||||||
child: TaxRateDropdown(
|
skipTraversal: true,
|
||||||
onSelected: (taxRate) => _onChanged(
|
child: Padding(
|
||||||
lineItems[index].rebuild((b) => b
|
padding:
|
||||||
..taxName3 = taxRate.name
|
const EdgeInsets.only(right: kTableColumnGap),
|
||||||
..taxRate3 = taxRate.rate),
|
child: TaxRateDropdown(
|
||||||
index),
|
onSelected: (taxRate) => _onChanged(
|
||||||
labelText: null,
|
lineItems[index].rebuild((b) => b
|
||||||
initialTaxName: lineItems[index].taxName3,
|
..taxName3 = taxRate.name
|
||||||
initialTaxRate: lineItems[index].taxRate3,
|
..taxRate3 = taxRate.rate),
|
||||||
|
index),
|
||||||
|
labelText: null,
|
||||||
|
initialTaxName: lineItems[index].taxName3,
|
||||||
|
initialTaxRate: lineItems[index].taxRate3,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Focus(
|
||||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
onFocusChange: (hasFocus) => Debouncer.complete(),
|
||||||
child: DecoratedFormField(
|
skipTraversal: true,
|
||||||
key: ValueKey('__line_item_${index}_cost__'),
|
child: Padding(
|
||||||
textAlign: TextAlign.right,
|
padding: const EdgeInsets.only(right: kTableColumnGap),
|
||||||
initialValue: formatNumber(
|
child: DecoratedFormField(
|
||||||
lineItems[index].cost, context,
|
key: ValueKey('__line_item_${index}_cost__'),
|
||||||
formatNumberType: FormatNumberType.inputMoney,
|
textAlign: TextAlign.right,
|
||||||
clientId: invoice.clientId),
|
initialValue: formatNumber(
|
||||||
onChanged: (value) => _onChanged(
|
lineItems[index].cost, context,
|
||||||
lineItems[index]
|
formatNumberType: FormatNumberType.inputMoney,
|
||||||
.rebuild((b) => b..cost = parseDouble(value)),
|
clientId: invoice.clientId),
|
||||||
index),
|
onChanged: (value) => _onChanged(
|
||||||
keyboardType: TextInputType.numberWithOptions(
|
lineItems[index]
|
||||||
decimal: true, signed: true),
|
.rebuild((b) => b..cost = parseDouble(value)),
|
||||||
onSavePressed: widget.entityViewModel.onSavePressed,
|
index),
|
||||||
|
keyboardType: TextInputType.numberWithOptions(
|
||||||
|
decimal: true, signed: true),
|
||||||
|
onSavePressed: widget.entityViewModel.onSavePressed,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (company.enableProductQuantity || widget.isTasks)
|
if (company.enableProductQuantity || widget.isTasks)
|
||||||
Padding(
|
Focus(
|
||||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
onFocusChange: (hasFocus) => Debouncer.complete(),
|
||||||
child: DecoratedFormField(
|
skipTraversal: true,
|
||||||
key: ValueKey('__line_item_${index}_quantity__'),
|
child: Padding(
|
||||||
textAlign: TextAlign.right,
|
padding:
|
||||||
initialValue: formatNumber(
|
const EdgeInsets.only(right: kTableColumnGap),
|
||||||
lineItems[index].quantity, context,
|
child: DecoratedFormField(
|
||||||
formatNumberType: FormatNumberType.inputAmount,
|
key: ValueKey('__line_item_${index}_quantity__'),
|
||||||
clientId: invoice.clientId),
|
textAlign: TextAlign.right,
|
||||||
onChanged: (value) => _onChanged(
|
initialValue: formatNumber(
|
||||||
lineItems[index].rebuild(
|
lineItems[index].quantity, context,
|
||||||
(b) => b..quantity = parseDouble(value)),
|
formatNumberType: FormatNumberType.inputAmount,
|
||||||
index),
|
clientId: invoice.clientId),
|
||||||
keyboardType: TextInputType.numberWithOptions(
|
onChanged: (value) => _onChanged(
|
||||||
decimal: true, signed: true),
|
lineItems[index].rebuild(
|
||||||
onSavePressed: widget.entityViewModel.onSavePressed,
|
(b) => b..quantity = parseDouble(value)),
|
||||||
|
index),
|
||||||
|
keyboardType: TextInputType.numberWithOptions(
|
||||||
|
decimal: true, signed: true),
|
||||||
|
onSavePressed: widget.entityViewModel.onSavePressed,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (company.enableProductDiscount)
|
if (company.enableProductDiscount)
|
||||||
Padding(
|
Focus(
|
||||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
onFocusChange: (hasFocus) => Debouncer.complete(),
|
||||||
child: DecoratedFormField(
|
skipTraversal: true,
|
||||||
key: ValueKey('__line_item_${index}_discount__'),
|
child: Padding(
|
||||||
textAlign: TextAlign.right,
|
padding:
|
||||||
initialValue: formatNumber(
|
const EdgeInsets.only(right: kTableColumnGap),
|
||||||
lineItems[index].discount, context,
|
child: DecoratedFormField(
|
||||||
formatNumberType: FormatNumberType.inputAmount,
|
key: ValueKey('__line_item_${index}_discount__'),
|
||||||
clientId: invoice.clientId),
|
textAlign: TextAlign.right,
|
||||||
onChanged: (value) => _onChanged(
|
initialValue: formatNumber(
|
||||||
lineItems[index].rebuild(
|
lineItems[index].discount, context,
|
||||||
(b) => b..discount = parseDouble(value)),
|
formatNumberType: FormatNumberType.inputAmount,
|
||||||
index),
|
clientId: invoice.clientId),
|
||||||
keyboardType: TextInputType.numberWithOptions(
|
onChanged: (value) => _onChanged(
|
||||||
decimal: true, signed: true),
|
lineItems[index].rebuild(
|
||||||
onSavePressed: widget.entityViewModel.onSavePressed,
|
(b) => b..discount = parseDouble(value)),
|
||||||
|
index),
|
||||||
|
keyboardType: TextInputType.numberWithOptions(
|
||||||
|
decimal: true, signed: true),
|
||||||
|
onSavePressed: widget.entityViewModel.onSavePressed,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
|
|
|
||||||
|
|
@ -62,9 +62,14 @@ Completer<Null> errorCompleter(BuildContext context) {
|
||||||
|
|
||||||
// https://stackoverflow.com/a/55119208/497368
|
// https://stackoverflow.com/a/55119208/497368
|
||||||
class Debouncer {
|
class Debouncer {
|
||||||
Debouncer({this.milliseconds = kMillisecondsToDebounceUpdate});
|
Debouncer({
|
||||||
|
this.milliseconds = kMillisecondsToDebounceUpdate,
|
||||||
|
this.sendFirstAction = false,
|
||||||
|
});
|
||||||
|
|
||||||
final int milliseconds;
|
final int milliseconds;
|
||||||
|
final bool sendFirstAction;
|
||||||
|
|
||||||
static VoidCallback action;
|
static VoidCallback action;
|
||||||
static Timer timer;
|
static Timer timer;
|
||||||
|
|
||||||
|
|
@ -75,7 +80,11 @@ class Debouncer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timer == null) {
|
if (timer == null) {
|
||||||
action();
|
if (sendFirstAction) {
|
||||||
|
action();
|
||||||
|
} else {
|
||||||
|
Debouncer.action = action;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
Debouncer.action = action;
|
Debouncer.action = action;
|
||||||
|
|
@ -93,6 +102,7 @@ class Debouncer {
|
||||||
static void complete() {
|
static void complete() {
|
||||||
if (action != null) {
|
if (action != null) {
|
||||||
action();
|
action();
|
||||||
|
action = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue