Settings
This commit is contained in:
parent
e0a1d69bf6
commit
b214daccd8
|
|
@ -4,20 +4,27 @@ import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||||
|
|
||||||
class AppForm extends StatelessWidget {
|
class AppForm extends StatelessWidget {
|
||||||
const AppForm({
|
const AppForm({
|
||||||
@required this.children,
|
this.children,
|
||||||
|
this.child,
|
||||||
@required this.formKey,
|
@required this.formKey,
|
||||||
|
@required this.focusNode,
|
||||||
});
|
});
|
||||||
|
|
||||||
final GlobalKey<FormState> formKey;
|
final GlobalKey<FormState> formKey;
|
||||||
final List<Widget> children;
|
final List<Widget> children;
|
||||||
|
final Widget child;
|
||||||
|
final FocusScopeNode focusNode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Form(
|
return FocusScope(
|
||||||
key: formKey,
|
node: focusNode,
|
||||||
child: ListView(
|
child: Form(
|
||||||
shrinkWrap: true,
|
key: formKey,
|
||||||
children: children,
|
child: child ?? ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
children: children,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import 'package:invoiceninja_flutter/constants.dart';
|
||||||
import 'package:invoiceninja_flutter/data/models/entities.dart';
|
import 'package:invoiceninja_flutter/data/models/entities.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/form_card.dart';
|
import 'package:invoiceninja_flutter/ui/app/form_card.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/forms/app_dropdown_button.dart';
|
import 'package:invoiceninja_flutter/ui/app/forms/app_dropdown_button.dart';
|
||||||
|
import 'package:invoiceninja_flutter/ui/app/forms/app_form.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/forms/client_picker.dart';
|
import 'package:invoiceninja_flutter/ui/app/forms/client_picker.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/forms/custom_field.dart';
|
import 'package:invoiceninja_flutter/ui/app/forms/custom_field.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/forms/custom_surcharges.dart';
|
import 'package:invoiceninja_flutter/ui/app/forms/custom_surcharges.dart';
|
||||||
|
|
@ -38,6 +39,10 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
TabController _tabController;
|
TabController _tabController;
|
||||||
|
|
||||||
|
FocusNode _focusNode;
|
||||||
|
static final GlobalKey<FormState> _formKey =
|
||||||
|
GlobalKey<FormState>(debugLabel: '_invoicesEdit');
|
||||||
|
|
||||||
final _invoiceNumberController = TextEditingController();
|
final _invoiceNumberController = TextEditingController();
|
||||||
final _poNumberController = TextEditingController();
|
final _poNumberController = TextEditingController();
|
||||||
final _discountController = TextEditingController();
|
final _discountController = TextEditingController();
|
||||||
|
|
@ -63,6 +68,7 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
|
_focusNode = FocusScopeNode();
|
||||||
_tabController = TabController(vsync: this, length: 4);
|
_tabController = TabController(vsync: this, length: 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,7 +117,7 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
||||||
_surcharge4Controller.text = formatNumber(invoice.customSurcharge4, context,
|
_surcharge4Controller.text = formatNumber(invoice.customSurcharge4, context,
|
||||||
formatNumberType: FormatNumberType.input);
|
formatNumberType: FormatNumberType.input);
|
||||||
_designController.text =
|
_designController.text =
|
||||||
invoice.designId != null ? kInvoiceDesigns[invoice.designId] : '';
|
invoice.designId != null ? kInvoiceDesigns[invoice.designId] : '';
|
||||||
_publicNotesController.text = invoice.publicNotes;
|
_publicNotesController.text = invoice.publicNotes;
|
||||||
_privateNotesController.text = invoice.privateNotes;
|
_privateNotesController.text = invoice.privateNotes;
|
||||||
_termsController.text = invoice.terms;
|
_termsController.text = invoice.terms;
|
||||||
|
|
@ -125,6 +131,7 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_focusNode.dispose();
|
||||||
_tabController.dispose();
|
_tabController.dispose();
|
||||||
_controllers.forEach((controller) {
|
_controllers.forEach((controller) {
|
||||||
controller.removeListener(_onChanged);
|
controller.removeListener(_onChanged);
|
||||||
|
|
@ -136,7 +143,8 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
||||||
|
|
||||||
void _onChanged() {
|
void _onChanged() {
|
||||||
_debouncer.run(() {
|
_debouncer.run(() {
|
||||||
final invoice = widget.viewModel.invoice.rebuild((b) => b
|
final invoice = widget.viewModel.invoice.rebuild((b) =>
|
||||||
|
b
|
||||||
..number = widget.viewModel.invoice.isNew
|
..number = widget.viewModel.invoice.isNew
|
||||||
? ''
|
? ''
|
||||||
: _invoiceNumberController.text.trim()
|
: _invoiceNumberController.text.trim()
|
||||||
|
|
@ -168,273 +176,299 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
||||||
final invoice = viewModel.invoice;
|
final invoice = viewModel.invoice;
|
||||||
final company = viewModel.company;
|
final company = viewModel.company;
|
||||||
|
|
||||||
return ListView(
|
return AppForm(
|
||||||
children: <Widget>[
|
formKey: _formKey,
|
||||||
Row(
|
focusNode: _focusNode,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: ListView(
|
||||||
mainAxisSize: MainAxisSize.max,
|
children: <Widget>[
|
||||||
children: <Widget>[
|
Row(
|
||||||
Expanded(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
child: FormCard(
|
mainAxisSize: MainAxisSize.max,
|
||||||
padding: const EdgeInsets.only(
|
children: <Widget>[
|
||||||
top: kMobileDialogPadding,
|
Expanded(
|
||||||
right: kMobileDialogPadding / 2,
|
child: FormCard(
|
||||||
bottom: kMobileDialogPadding,
|
padding: const EdgeInsets.only(
|
||||||
left: kMobileDialogPadding),
|
top: kMobileDialogPadding,
|
||||||
children: <Widget>[
|
right: kMobileDialogPadding / 2,
|
||||||
if (invoice.isNew)
|
bottom: kMobileDialogPadding,
|
||||||
ClientPicker(
|
left: kMobileDialogPadding),
|
||||||
clientId: invoice.clientId,
|
children: <Widget>[
|
||||||
clientState: viewModel.state.clientState,
|
if (invoice.isNew)
|
||||||
onSelected: (client) =>
|
ClientPicker(
|
||||||
viewModel.onClientChanged(invoice, client),
|
clientId: invoice.clientId,
|
||||||
onAddPressed: (completer) =>
|
clientState: viewModel.state.clientState,
|
||||||
viewModel.onAddClientPressed(context, completer),
|
onSelected: (client) =>
|
||||||
|
viewModel.onClientChanged(invoice, client),
|
||||||
|
onAddPressed: (completer) =>
|
||||||
|
viewModel.onAddClientPressed(context, completer),
|
||||||
|
),
|
||||||
|
UserPicker(
|
||||||
|
userId: invoice.assignedUserId,
|
||||||
|
onChanged: (userId) =>
|
||||||
|
viewModel.onChanged(
|
||||||
|
invoice.rebuild((
|
||||||
|
b) => b..assignedUserId = userId)),
|
||||||
),
|
),
|
||||||
UserPicker(
|
SizedBox(
|
||||||
userId: invoice.assignedUserId,
|
height: 100,
|
||||||
onChanged: (userId) => viewModel.onChanged(
|
child: InvoiceEditContactsScreen(),
|
||||||
invoice.rebuild((b) => b..assignedUserId = userId)),
|
),
|
||||||
),
|
],
|
||||||
SizedBox(
|
),
|
||||||
height: 100,
|
|
||||||
child: InvoiceEditContactsScreen(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
Expanded(
|
child: FormCard(
|
||||||
child: FormCard(
|
padding: const EdgeInsets.only(
|
||||||
padding: const EdgeInsets.only(
|
top: kMobileDialogPadding,
|
||||||
top: kMobileDialogPadding,
|
right: kMobileDialogPadding / 2,
|
||||||
right: kMobileDialogPadding / 2,
|
bottom: kMobileDialogPadding,
|
||||||
bottom: kMobileDialogPadding,
|
left: kMobileDialogPadding / 2),
|
||||||
left: kMobileDialogPadding / 2),
|
children: <Widget>[
|
||||||
children: <Widget>[
|
|
||||||
DatePicker(
|
|
||||||
validator: (String val) => val.trim().isEmpty
|
|
||||||
? AppLocalization.of(context).pleaseSelectADate
|
|
||||||
: null,
|
|
||||||
labelText: widget.isQuote
|
|
||||||
? localization.quoteDate
|
|
||||||
: localization.invoiceDate,
|
|
||||||
selectedDate: invoice.date,
|
|
||||||
onSelected: (date) {
|
|
||||||
viewModel
|
|
||||||
.onChanged(invoice.rebuild((b) => b..date = date));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
DatePicker(
|
|
||||||
labelText: widget.isQuote
|
|
||||||
? localization.validUntil
|
|
||||||
: localization.dueDate,
|
|
||||||
selectedDate: invoice.dueDate,
|
|
||||||
onSelected: (date) {
|
|
||||||
viewModel
|
|
||||||
.onChanged(invoice.rebuild((b) => b..dueDate = date));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
DecoratedFormField(
|
|
||||||
label: localization.partialDeposit,
|
|
||||||
controller: _partialController,
|
|
||||||
keyboardType:
|
|
||||||
TextInputType.numberWithOptions(decimal: true),
|
|
||||||
),
|
|
||||||
if (invoice.partial != null && invoice.partial > 0)
|
|
||||||
DatePicker(
|
DatePicker(
|
||||||
labelText: localization.partialDueDate,
|
validator: (String val) =>
|
||||||
selectedDate: invoice.partialDueDate,
|
val
|
||||||
|
.trim()
|
||||||
|
.isEmpty
|
||||||
|
? AppLocalization
|
||||||
|
.of(context)
|
||||||
|
.pleaseSelectADate
|
||||||
|
: null,
|
||||||
|
labelText: widget.isQuote
|
||||||
|
? localization.quoteDate
|
||||||
|
: localization.invoiceDate,
|
||||||
|
selectedDate: invoice.date,
|
||||||
onSelected: (date) {
|
onSelected: (date) {
|
||||||
viewModel.onChanged(
|
viewModel
|
||||||
invoice.rebuild((b) => b..partialDueDate = date));
|
.onChanged(invoice.rebuild((b) => b..date = date));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
CustomField(
|
DatePicker(
|
||||||
controller: _custom1Controller,
|
labelText: widget.isQuote
|
||||||
field: CustomFieldType.invoice1,
|
? localization.validUntil
|
||||||
value: invoice.customValue1,
|
: localization.dueDate,
|
||||||
),
|
selectedDate: invoice.dueDate,
|
||||||
CustomField(
|
onSelected: (date) {
|
||||||
controller: _custom2Controller,
|
viewModel
|
||||||
field: CustomFieldType.invoice2,
|
.onChanged(
|
||||||
value: invoice.customValue2,
|
invoice.rebuild((b) => b..dueDate = date));
|
||||||
),
|
},
|
||||||
],
|
),
|
||||||
|
DecoratedFormField(
|
||||||
|
label: localization.partialDeposit,
|
||||||
|
controller: _partialController,
|
||||||
|
keyboardType:
|
||||||
|
TextInputType.numberWithOptions(decimal: true),
|
||||||
|
),
|
||||||
|
if (invoice.partial != null && invoice.partial > 0)
|
||||||
|
DatePicker(
|
||||||
|
labelText: localization.partialDueDate,
|
||||||
|
selectedDate: invoice.partialDueDate,
|
||||||
|
onSelected: (date) {
|
||||||
|
viewModel.onChanged(
|
||||||
|
invoice.rebuild((b) => b..partialDueDate = date));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
CustomField(
|
||||||
|
controller: _custom1Controller,
|
||||||
|
field: CustomFieldType.invoice1,
|
||||||
|
value: invoice.customValue1,
|
||||||
|
),
|
||||||
|
CustomField(
|
||||||
|
controller: _custom2Controller,
|
||||||
|
field: CustomFieldType.invoice2,
|
||||||
|
value: invoice.customValue2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
Expanded(
|
child: FormCard(
|
||||||
child: FormCard(
|
padding: const EdgeInsets.only(
|
||||||
padding: const EdgeInsets.only(
|
top: kMobileDialogPadding,
|
||||||
top: kMobileDialogPadding,
|
right: kMobileDialogPadding,
|
||||||
right: kMobileDialogPadding,
|
bottom: kMobileDialogPadding,
|
||||||
bottom: kMobileDialogPadding,
|
left: kMobileDialogPadding / 2),
|
||||||
left: kMobileDialogPadding / 2),
|
children: <Widget>[
|
||||||
children: <Widget>[
|
DecoratedFormField(
|
||||||
DecoratedFormField(
|
controller: _invoiceNumberController,
|
||||||
controller: _invoiceNumberController,
|
label: widget.isQuote
|
||||||
label: widget.isQuote
|
? localization.quoteNumber
|
||||||
? localization.quoteNumber
|
: localization.invoiceNumber,
|
||||||
: localization.invoiceNumber,
|
validator: (String val) =>
|
||||||
validator: (String val) => val.trim().isEmpty &&
|
val
|
||||||
invoice.isOld
|
.trim()
|
||||||
? AppLocalization.of(context).pleaseEnterAnInvoiceNumber
|
.isEmpty &&
|
||||||
: null,
|
invoice.isOld
|
||||||
),
|
? AppLocalization
|
||||||
DecoratedFormField(
|
.of(context)
|
||||||
label: localization.poNumber,
|
.pleaseEnterAnInvoiceNumber
|
||||||
controller: _poNumberController,
|
: null,
|
||||||
),
|
),
|
||||||
DiscountField(
|
DecoratedFormField(
|
||||||
controller: _discountController,
|
label: localization.poNumber,
|
||||||
value: invoice.discount,
|
controller: _poNumberController,
|
||||||
isAmountDiscount: invoice.isAmountDiscount,
|
),
|
||||||
onTypeChanged: (value) => viewModel.onChanged(
|
DiscountField(
|
||||||
invoice.rebuild((b) => b..isAmountDiscount = value)),
|
controller: _discountController,
|
||||||
),
|
value: invoice.discount,
|
||||||
CustomField(
|
isAmountDiscount: invoice.isAmountDiscount,
|
||||||
controller: _custom2Controller,
|
onTypeChanged: (value) =>
|
||||||
field: CustomFieldType.invoice2,
|
viewModel.onChanged(
|
||||||
value: invoice.customValue2,
|
invoice.rebuild((
|
||||||
),
|
b) => b..isAmountDiscount = value)),
|
||||||
CustomField(
|
),
|
||||||
controller: _custom4Controller,
|
CustomField(
|
||||||
field: CustomFieldType.invoice4,
|
controller: _custom2Controller,
|
||||||
value: invoice.customValue4,
|
field: CustomFieldType.invoice2,
|
||||||
),
|
value: invoice.customValue2,
|
||||||
],
|
),
|
||||||
|
CustomField(
|
||||||
|
controller: _custom4Controller,
|
||||||
|
field: CustomFieldType.invoice4,
|
||||||
|
value: invoice.customValue4,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
widget.isQuote ? QuoteEditItemsScreen() : InvoiceEditItemsScreen(),
|
||||||
widget.isQuote ? QuoteEditItemsScreen() : InvoiceEditItemsScreen(),
|
Row(
|
||||||
Row(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: <Widget>[
|
||||||
children: <Widget>[
|
Expanded(
|
||||||
Expanded(
|
flex: 2,
|
||||||
flex: 2,
|
child: FormCard(
|
||||||
child: FormCard(
|
padding: const EdgeInsets.only(
|
||||||
padding: const EdgeInsets.only(
|
top: kMobileDialogPadding,
|
||||||
top: kMobileDialogPadding,
|
right: kMobileDialogPadding / 2,
|
||||||
right: kMobileDialogPadding / 2,
|
bottom: kMobileDialogPadding,
|
||||||
bottom: kMobileDialogPadding,
|
left: kMobileDialogPadding),
|
||||||
left: kMobileDialogPadding),
|
children: <Widget>[
|
||||||
children: <Widget>[
|
TabBar(
|
||||||
TabBar(
|
|
||||||
controller: _tabController,
|
|
||||||
tabs: [
|
|
||||||
Tab(text: localization.publicNotes),
|
|
||||||
Tab(text: localization.privateNotes),
|
|
||||||
Tab(text: localization.terms),
|
|
||||||
Tab(text: localization.footer),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: 100,
|
|
||||||
child: TabBarView(
|
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
children: <Widget>[
|
tabs: [
|
||||||
DecoratedFormField(
|
Tab(text: localization.publicNotes),
|
||||||
maxLines: 4,
|
Tab(text: localization.privateNotes),
|
||||||
controller: _publicNotesController,
|
Tab(text: localization.terms),
|
||||||
keyboardType: TextInputType.multiline,
|
Tab(text: localization.footer),
|
||||||
label: '',
|
|
||||||
),
|
|
||||||
DecoratedFormField(
|
|
||||||
maxLines: 4,
|
|
||||||
controller: _privateNotesController,
|
|
||||||
keyboardType: TextInputType.multiline,
|
|
||||||
label: '',
|
|
||||||
),
|
|
||||||
DecoratedFormField(
|
|
||||||
maxLines: 4,
|
|
||||||
controller: _termsController,
|
|
||||||
keyboardType: TextInputType.multiline,
|
|
||||||
label: '',
|
|
||||||
),
|
|
||||||
DecoratedFormField(
|
|
||||||
maxLines: 4,
|
|
||||||
controller: _footerController,
|
|
||||||
keyboardType: TextInputType.multiline,
|
|
||||||
label: '',
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
SizedBox(
|
||||||
],
|
height: 100,
|
||||||
|
child: TabBarView(
|
||||||
|
controller: _tabController,
|
||||||
|
children: <Widget>[
|
||||||
|
DecoratedFormField(
|
||||||
|
maxLines: 4,
|
||||||
|
controller: _publicNotesController,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
label: '',
|
||||||
|
),
|
||||||
|
DecoratedFormField(
|
||||||
|
maxLines: 4,
|
||||||
|
controller: _privateNotesController,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
label: '',
|
||||||
|
),
|
||||||
|
DecoratedFormField(
|
||||||
|
maxLines: 4,
|
||||||
|
controller: _termsController,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
label: '',
|
||||||
|
),
|
||||||
|
DecoratedFormField(
|
||||||
|
maxLines: 4,
|
||||||
|
controller: _footerController,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
label: '',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
Expanded(
|
flex: 1,
|
||||||
flex: 1,
|
child: FormCard(
|
||||||
child: FormCard(
|
padding: const EdgeInsets.only(
|
||||||
padding: const EdgeInsets.only(
|
top: kMobileDialogPadding,
|
||||||
top: kMobileDialogPadding,
|
right: kMobileDialogPadding,
|
||||||
right: kMobileDialogPadding,
|
bottom: kMobileDialogPadding,
|
||||||
bottom: kMobileDialogPadding,
|
left: kMobileDialogPadding / 2),
|
||||||
left: kMobileDialogPadding / 2),
|
children: <Widget>[
|
||||||
children: <Widget>[
|
/*
|
||||||
/*
|
DecoratedFormField(
|
||||||
DecoratedFormField(
|
controller: null,
|
||||||
controller: null,
|
enabled: false,
|
||||||
enabled: false,
|
initialValue: '10',
|
||||||
initialValue: '10',
|
label: localization.subtotal,
|
||||||
label: localization.subtotal,
|
|
||||||
),
|
|
||||||
*/
|
|
||||||
CustomSurcharges(
|
|
||||||
surcharge1Controller: _surcharge1Controller,
|
|
||||||
surcharge2Controller: _surcharge2Controller,
|
|
||||||
surcharge3Controller: _surcharge3Controller,
|
|
||||||
surcharge4Controller: _surcharge4Controller,
|
|
||||||
),
|
|
||||||
if (company.settings.enableFirstInvoiceTaxRate)
|
|
||||||
TaxRateDropdown(
|
|
||||||
onSelected: (taxRate) =>
|
|
||||||
viewModel.onChanged(invoice.applyTax(taxRate)),
|
|
||||||
labelText: localization.tax,
|
|
||||||
initialTaxName: invoice.taxName1,
|
|
||||||
initialTaxRate: invoice.taxRate1,
|
|
||||||
),
|
),
|
||||||
if (company.settings.enableSecondInvoiceTaxRate)
|
*/
|
||||||
TaxRateDropdown(
|
CustomSurcharges(
|
||||||
onSelected: (taxRate) => viewModel
|
surcharge1Controller: _surcharge1Controller,
|
||||||
.onChanged(invoice.applyTax(taxRate, isSecond: true)),
|
surcharge2Controller: _surcharge2Controller,
|
||||||
labelText: localization.tax,
|
surcharge3Controller: _surcharge3Controller,
|
||||||
initialTaxName: invoice.taxName2,
|
surcharge4Controller: _surcharge4Controller,
|
||||||
initialTaxRate: invoice.taxRate2,
|
|
||||||
),
|
),
|
||||||
if (company.settings.enableThirdInvoiceTaxRate)
|
if (company.settings.enableFirstInvoiceTaxRate)
|
||||||
TaxRateDropdown(
|
TaxRateDropdown(
|
||||||
onSelected: (taxRate) => viewModel
|
onSelected: (taxRate) =>
|
||||||
.onChanged(invoice.applyTax(taxRate, isThird: true)),
|
viewModel.onChanged(invoice.applyTax(taxRate)),
|
||||||
labelText: localization.tax,
|
labelText: localization.tax,
|
||||||
initialTaxName: invoice.taxName3,
|
initialTaxName: invoice.taxName1,
|
||||||
initialTaxRate: invoice.taxRate3,
|
initialTaxRate: invoice.taxRate1,
|
||||||
|
),
|
||||||
|
if (company.settings.enableSecondInvoiceTaxRate)
|
||||||
|
TaxRateDropdown(
|
||||||
|
onSelected: (taxRate) =>
|
||||||
|
viewModel
|
||||||
|
.onChanged(
|
||||||
|
invoice.applyTax(taxRate, isSecond: true)),
|
||||||
|
labelText: localization.tax,
|
||||||
|
initialTaxName: invoice.taxName2,
|
||||||
|
initialTaxRate: invoice.taxRate2,
|
||||||
|
),
|
||||||
|
if (company.settings.enableThirdInvoiceTaxRate)
|
||||||
|
TaxRateDropdown(
|
||||||
|
onSelected: (taxRate) =>
|
||||||
|
viewModel
|
||||||
|
.onChanged(
|
||||||
|
invoice.applyTax(taxRate, isThird: true)),
|
||||||
|
labelText: localization.tax,
|
||||||
|
initialTaxName: invoice.taxName3,
|
||||||
|
initialTaxRate: invoice.taxRate3,
|
||||||
|
),
|
||||||
|
CustomSurcharges(
|
||||||
|
surcharge1Controller: _surcharge1Controller,
|
||||||
|
surcharge2Controller: _surcharge2Controller,
|
||||||
|
surcharge3Controller: _surcharge3Controller,
|
||||||
|
surcharge4Controller: _surcharge4Controller,
|
||||||
|
isAfterTaxes: true,
|
||||||
),
|
),
|
||||||
CustomSurcharges(
|
AppDropdownButton(
|
||||||
surcharge1Controller: _surcharge1Controller,
|
labelText: localization.design,
|
||||||
surcharge2Controller: _surcharge2Controller,
|
value: invoice.designId,
|
||||||
surcharge3Controller: _surcharge3Controller,
|
onChanged: (dynamic value) =>
|
||||||
surcharge4Controller: _surcharge4Controller,
|
viewModel
|
||||||
isAfterTaxes: true,
|
.onChanged(
|
||||||
),
|
invoice.rebuild((b) => b..designId = value)),
|
||||||
AppDropdownButton(
|
items: company.invoiceDesignIds
|
||||||
labelText: localization.design,
|
.map((designId) =>
|
||||||
value: invoice.designId,
|
DropdownMenuItem<String>(
|
||||||
onChanged: (dynamic value) => viewModel
|
value: designId,
|
||||||
.onChanged(invoice.rebuild((b) => b..designId = value)),
|
child: Text(kInvoiceDesigns[designId]),
|
||||||
items: company.invoiceDesignIds
|
))
|
||||||
.map((designId) => DropdownMenuItem<String>(
|
.toList(),
|
||||||
value: designId,
|
),
|
||||||
child: Text(kInvoiceDesigns[designId]),
|
],
|
||||||
))
|
),
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||||
|
|
||||||
class InvoiceEditItemsDesktop extends StatefulWidget {
|
class InvoiceEditItemsDesktop extends StatefulWidget {
|
||||||
const InvoiceEditItemsDesktop({this.viewModel});
|
const InvoiceEditItemsDesktop({
|
||||||
|
this.viewModel,
|
||||||
|
});
|
||||||
|
|
||||||
final EntityEditItemsVM viewModel;
|
final EntityEditItemsVM viewModel;
|
||||||
|
|
||||||
|
|
@ -20,6 +22,32 @@ class InvoiceEditItemsDesktop extends StatefulWidget {
|
||||||
class _InvoiceEditItemsDesktopState extends State<InvoiceEditItemsDesktop> {
|
class _InvoiceEditItemsDesktopState extends State<InvoiceEditItemsDesktop> {
|
||||||
int _updatedAt;
|
int _updatedAt;
|
||||||
|
|
||||||
|
/*
|
||||||
|
final Map<int, FocusNode> _focusNodes = {};
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
_focusNodes.values.forEach((node) => node.dispose());
|
||||||
|
|
||||||
|
final lineItems = widget.viewModel.invoice.lineItems;
|
||||||
|
for (var index = 0; index < lineItems.length; index++) {
|
||||||
|
_focusNodes[index] = FocusNode()
|
||||||
|
..addListener(() => _onFocusChange(index));
|
||||||
|
}
|
||||||
|
super.didChangeDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_focusNodes.values.forEach((node) => node.dispose());
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onFocusChange(int index) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final localization = AppLocalization.of(context);
|
final localization = AppLocalization.of(context);
|
||||||
|
|
@ -96,6 +124,8 @@ class _InvoiceEditItemsDesktopState extends State<InvoiceEditItemsDesktop> {
|
||||||
lineItems[index].rebuild((b) => b..notes = value), index),
|
lineItems[index].rebuild((b) => b..notes = value), index),
|
||||||
minLines: 1,
|
minLines: 1,
|
||||||
maxLines: 6,
|
maxLines: 6,
|
||||||
|
//maxLines: _focusNodes[index].hasFocus ? 6 : 1,
|
||||||
|
//focusNode: _focusNodes[index],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||||
|
|
||||||
class InvoiceEditItemsScreen extends StatelessWidget {
|
class InvoiceEditItemsScreen extends StatelessWidget {
|
||||||
const InvoiceEditItemsScreen({Key key}) : super(key: key);
|
const InvoiceEditItemsScreen({
|
||||||
|
Key key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,9 @@ import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||||
|
|
||||||
class QuoteEditItemsScreen extends StatelessWidget {
|
class QuoteEditItemsScreen extends StatelessWidget {
|
||||||
const QuoteEditItemsScreen({Key key}) : super(key: key);
|
const QuoteEditItemsScreen({
|
||||||
|
Key key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,19 @@ class _LocalizationSettingsState extends State<LocalizationSettings> {
|
||||||
bool autoValidate = false;
|
bool autoValidate = false;
|
||||||
|
|
||||||
final _firstNameController = TextEditingController();
|
final _firstNameController = TextEditingController();
|
||||||
|
FocusScopeNode _focusNode;
|
||||||
|
|
||||||
List<TextEditingController> _controllers = [];
|
List<TextEditingController> _controllers = [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_focusNode = FocusScopeNode();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_focusNode.dispose();
|
||||||
_controllers.forEach((dynamic controller) {
|
_controllers.forEach((dynamic controller) {
|
||||||
controller.removeListener(_onChanged);
|
controller.removeListener(_onChanged);
|
||||||
controller.dispose();
|
controller.dispose();
|
||||||
|
|
@ -78,6 +86,7 @@ class _LocalizationSettingsState extends State<LocalizationSettings> {
|
||||||
onSavePressed: viewModel.onSavePressed,
|
onSavePressed: viewModel.onSavePressed,
|
||||||
body: AppForm(
|
body: AppForm(
|
||||||
formKey: _formKey,
|
formKey: _formKey,
|
||||||
|
focusNode: _focusNode,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FormCard(
|
FormCard(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,19 @@ class ProductSettings extends StatefulWidget {
|
||||||
class _ProductSettingsState extends State<ProductSettings> {
|
class _ProductSettingsState extends State<ProductSettings> {
|
||||||
static final GlobalKey<FormState> _formKey =
|
static final GlobalKey<FormState> _formKey =
|
||||||
GlobalKey<FormState>(debugLabel: '_productSettings');
|
GlobalKey<FormState>(debugLabel: '_productSettings');
|
||||||
|
FocusScopeNode _focusNode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_focusNode = FocusScopeNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_focusNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -33,6 +46,7 @@ class _ProductSettingsState extends State<ProductSettings> {
|
||||||
onSavePressed: viewModel.onSavePressed,
|
onSavePressed: viewModel.onSavePressed,
|
||||||
body: AppForm(
|
body: AppForm(
|
||||||
formKey: _formKey,
|
formKey: _formKey,
|
||||||
|
focusNode: _focusNode,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FormCard(
|
FormCard(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,19 @@ class TaxSettings extends StatefulWidget {
|
||||||
class _TaxSettingsState extends State<TaxSettings> {
|
class _TaxSettingsState extends State<TaxSettings> {
|
||||||
static final GlobalKey<FormState> _formKey =
|
static final GlobalKey<FormState> _formKey =
|
||||||
GlobalKey<FormState>(debugLabel: '_taxSettings');
|
GlobalKey<FormState>(debugLabel: '_taxSettings');
|
||||||
|
FocusScopeNode _focusNode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_focusNode = FocusScopeNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_focusNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -39,6 +52,7 @@ class _TaxSettingsState extends State<TaxSettings> {
|
||||||
onSavePressed: viewModel.onSavePressed,
|
onSavePressed: viewModel.onSavePressed,
|
||||||
body: AppForm(
|
body: AppForm(
|
||||||
formKey: _formKey,
|
formKey: _formKey,
|
||||||
|
focusNode: _focusNode,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FormCard(
|
FormCard(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ class UserDetails extends StatefulWidget {
|
||||||
class _UserDetailsState extends State<UserDetails> {
|
class _UserDetailsState extends State<UserDetails> {
|
||||||
static final GlobalKey<FormState> _formKey =
|
static final GlobalKey<FormState> _formKey =
|
||||||
GlobalKey<FormState>(debugLabel: '_userDetails');
|
GlobalKey<FormState>(debugLabel: '_userDetails');
|
||||||
|
FocusScopeNode _focusNode;
|
||||||
bool autoValidate = false;
|
bool autoValidate = false;
|
||||||
|
|
||||||
final _firstNameController = TextEditingController();
|
final _firstNameController = TextEditingController();
|
||||||
|
|
@ -34,8 +34,15 @@ class _UserDetailsState extends State<UserDetails> {
|
||||||
List<TextEditingController> _controllers = [];
|
List<TextEditingController> _controllers = [];
|
||||||
final _debouncer = Debouncer();
|
final _debouncer = Debouncer();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_focusNode = FocusScopeNode();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_focusNode.dispose();
|
||||||
_controllers.forEach((dynamic controller) {
|
_controllers.forEach((dynamic controller) {
|
||||||
controller.removeListener(_onChanged);
|
controller.removeListener(_onChanged);
|
||||||
controller.dispose();
|
controller.dispose();
|
||||||
|
|
@ -89,6 +96,7 @@ class _UserDetailsState extends State<UserDetails> {
|
||||||
title: localization.userDetails,
|
title: localization.userDetails,
|
||||||
onSavePressed: viewModel.onSavePressed,
|
onSavePressed: viewModel.onSavePressed,
|
||||||
body: AppForm(
|
body: AppForm(
|
||||||
|
focusNode: _focusNode,
|
||||||
formKey: _formKey,
|
formKey: _formKey,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FormCard(
|
FormCard(
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ class _TaxRateEditState extends State<TaxRateEdit> {
|
||||||
static final GlobalKey<FormState> _formKey =
|
static final GlobalKey<FormState> _formKey =
|
||||||
GlobalKey<FormState>(debugLabel: '_taxRateEdit');
|
GlobalKey<FormState>(debugLabel: '_taxRateEdit');
|
||||||
|
|
||||||
|
FocusScopeNode _focusNode;
|
||||||
bool autoValidate = false;
|
bool autoValidate = false;
|
||||||
|
|
||||||
final _nameController = TextEditingController();
|
final _nameController = TextEditingController();
|
||||||
|
|
@ -85,6 +86,7 @@ class _TaxRateEditState extends State<TaxRateEdit> {
|
||||||
onSavePressed: viewModel.onSavePressed,
|
onSavePressed: viewModel.onSavePressed,
|
||||||
onCancelPressed: viewModel.onCancelPressed,
|
onCancelPressed: viewModel.onCancelPressed,
|
||||||
body: AppForm(
|
body: AppForm(
|
||||||
|
focusNode: _focusNode,
|
||||||
formKey: _formKey,
|
formKey: _formKey,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FormCard(
|
FormCard(
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ class _UserEditState extends State<UserEdit> {
|
||||||
static final GlobalKey<FormState> _formKey =
|
static final GlobalKey<FormState> _formKey =
|
||||||
GlobalKey<FormState>(debugLabel: '_userEdit');
|
GlobalKey<FormState>(debugLabel: '_userEdit');
|
||||||
final _debouncer = Debouncer();
|
final _debouncer = Debouncer();
|
||||||
|
FocusScopeNode _focusNode;
|
||||||
bool autoValidate = false;
|
bool autoValidate = false;
|
||||||
|
|
||||||
final _firstNameController = TextEditingController();
|
final _firstNameController = TextEditingController();
|
||||||
|
|
@ -143,6 +143,7 @@ class _UserEditState extends State<UserEdit> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: AppForm(
|
body: AppForm(
|
||||||
|
focusNode: _focusNode,
|
||||||
formKey: _formKey,
|
formKey: _formKey,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FormCard(
|
FormCard(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue