From b214daccd82cfe26e587c20d194097b13a6e49db Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 Dec 2019 18:07:55 +0200 Subject: [PATCH] Settings --- lib/ui/app/forms/app_form.dart | 19 +- lib/ui/invoice/edit/invoice_edit_desktop.dart | 540 ++++++++++-------- .../edit/invoice_edit_items_desktop.dart | 32 +- .../invoice/edit/invoice_edit_items_vm.dart | 4 +- lib/ui/quote/edit/quote_edit_items_vm.dart | 4 +- lib/ui/settings/localization_settings.dart | 9 + lib/ui/settings/product_settings.dart | 14 + lib/ui/settings/tax_settings.dart | 14 + lib/ui/settings/user_details.dart | 10 +- lib/ui/tax_rate/edit/tax_rate_edit.dart | 2 + lib/ui/user/edit/user_edit.dart | 3 +- 11 files changed, 387 insertions(+), 264 deletions(-) diff --git a/lib/ui/app/forms/app_form.dart b/lib/ui/app/forms/app_form.dart index a5ce84c31..4e88cbed3 100644 --- a/lib/ui/app/forms/app_form.dart +++ b/lib/ui/app/forms/app_form.dart @@ -4,20 +4,27 @@ import 'package:invoiceninja_flutter/redux/app/app_state.dart'; class AppForm extends StatelessWidget { const AppForm({ - @required this.children, + this.children, + this.child, @required this.formKey, + @required this.focusNode, }); final GlobalKey formKey; final List children; + final Widget child; + final FocusScopeNode focusNode; @override Widget build(BuildContext context) { - return Form( - key: formKey, - child: ListView( - shrinkWrap: true, - children: children, + return FocusScope( + node: focusNode, + child: Form( + key: formKey, + child: child ?? ListView( + shrinkWrap: true, + children: children, + ), ), ); } diff --git a/lib/ui/invoice/edit/invoice_edit_desktop.dart b/lib/ui/invoice/edit/invoice_edit_desktop.dart index b90d5e50d..2d778911d 100644 --- a/lib/ui/invoice/edit/invoice_edit_desktop.dart +++ b/lib/ui/invoice/edit/invoice_edit_desktop.dart @@ -4,6 +4,7 @@ import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/data/models/entities.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_form.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_surcharges.dart'; @@ -38,6 +39,10 @@ class InvoiceEditDesktopState extends State with SingleTickerProviderStateMixin { TabController _tabController; + FocusNode _focusNode; + static final GlobalKey _formKey = + GlobalKey(debugLabel: '_invoicesEdit'); + final _invoiceNumberController = TextEditingController(); final _poNumberController = TextEditingController(); final _discountController = TextEditingController(); @@ -63,6 +68,7 @@ class InvoiceEditDesktopState extends State void initState() { super.initState(); + _focusNode = FocusScopeNode(); _tabController = TabController(vsync: this, length: 4); } @@ -111,7 +117,7 @@ class InvoiceEditDesktopState extends State _surcharge4Controller.text = formatNumber(invoice.customSurcharge4, context, formatNumberType: FormatNumberType.input); _designController.text = - invoice.designId != null ? kInvoiceDesigns[invoice.designId] : ''; + invoice.designId != null ? kInvoiceDesigns[invoice.designId] : ''; _publicNotesController.text = invoice.publicNotes; _privateNotesController.text = invoice.privateNotes; _termsController.text = invoice.terms; @@ -125,6 +131,7 @@ class InvoiceEditDesktopState extends State @override void dispose() { + _focusNode.dispose(); _tabController.dispose(); _controllers.forEach((controller) { controller.removeListener(_onChanged); @@ -136,7 +143,8 @@ class InvoiceEditDesktopState extends State void _onChanged() { _debouncer.run(() { - final invoice = widget.viewModel.invoice.rebuild((b) => b + final invoice = widget.viewModel.invoice.rebuild((b) => + b ..number = widget.viewModel.invoice.isNew ? '' : _invoiceNumberController.text.trim() @@ -168,273 +176,299 @@ class InvoiceEditDesktopState extends State final invoice = viewModel.invoice; final company = viewModel.company; - return ListView( - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - child: FormCard( - padding: const EdgeInsets.only( - top: kMobileDialogPadding, - right: kMobileDialogPadding / 2, - bottom: kMobileDialogPadding, - left: kMobileDialogPadding), - children: [ - if (invoice.isNew) - ClientPicker( - clientId: invoice.clientId, - clientState: viewModel.state.clientState, - onSelected: (client) => - viewModel.onClientChanged(invoice, client), - onAddPressed: (completer) => - viewModel.onAddClientPressed(context, completer), + return AppForm( + formKey: _formKey, + focusNode: _focusNode, + child: ListView( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: FormCard( + padding: const EdgeInsets.only( + top: kMobileDialogPadding, + right: kMobileDialogPadding / 2, + bottom: kMobileDialogPadding, + left: kMobileDialogPadding), + children: [ + if (invoice.isNew) + ClientPicker( + clientId: invoice.clientId, + clientState: viewModel.state.clientState, + 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( - userId: invoice.assignedUserId, - onChanged: (userId) => viewModel.onChanged( - invoice.rebuild((b) => b..assignedUserId = userId)), - ), - SizedBox( - height: 100, - child: InvoiceEditContactsScreen(), - ), - ], + SizedBox( + height: 100, + child: InvoiceEditContactsScreen(), + ), + ], + ), ), - ), - Expanded( - child: FormCard( - padding: const EdgeInsets.only( - top: kMobileDialogPadding, - right: kMobileDialogPadding / 2, - bottom: kMobileDialogPadding, - left: kMobileDialogPadding / 2), - children: [ - 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) + Expanded( + child: FormCard( + padding: const EdgeInsets.only( + top: kMobileDialogPadding, + right: kMobileDialogPadding / 2, + bottom: kMobileDialogPadding, + left: kMobileDialogPadding / 2), + children: [ DatePicker( - labelText: localization.partialDueDate, - selectedDate: invoice.partialDueDate, + 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..partialDueDate = date)); + viewModel + .onChanged(invoice.rebuild((b) => b..date = date)); }, ), - CustomField( - controller: _custom1Controller, - field: CustomFieldType.invoice1, - value: invoice.customValue1, - ), - CustomField( - controller: _custom2Controller, - field: CustomFieldType.invoice2, - value: invoice.customValue2, - ), - ], + 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( + 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( - child: FormCard( - padding: const EdgeInsets.only( - top: kMobileDialogPadding, - right: kMobileDialogPadding, - bottom: kMobileDialogPadding, - left: kMobileDialogPadding / 2), - children: [ - DecoratedFormField( - controller: _invoiceNumberController, - label: widget.isQuote - ? localization.quoteNumber - : localization.invoiceNumber, - validator: (String val) => val.trim().isEmpty && - invoice.isOld - ? AppLocalization.of(context).pleaseEnterAnInvoiceNumber - : null, - ), - DecoratedFormField( - label: localization.poNumber, - controller: _poNumberController, - ), - DiscountField( - controller: _discountController, - value: invoice.discount, - isAmountDiscount: invoice.isAmountDiscount, - onTypeChanged: (value) => viewModel.onChanged( - invoice.rebuild((b) => b..isAmountDiscount = value)), - ), - CustomField( - controller: _custom2Controller, - field: CustomFieldType.invoice2, - value: invoice.customValue2, - ), - CustomField( - controller: _custom4Controller, - field: CustomFieldType.invoice4, - value: invoice.customValue4, - ), - ], + Expanded( + child: FormCard( + padding: const EdgeInsets.only( + top: kMobileDialogPadding, + right: kMobileDialogPadding, + bottom: kMobileDialogPadding, + left: kMobileDialogPadding / 2), + children: [ + DecoratedFormField( + controller: _invoiceNumberController, + label: widget.isQuote + ? localization.quoteNumber + : localization.invoiceNumber, + validator: (String val) => + val + .trim() + .isEmpty && + invoice.isOld + ? AppLocalization + .of(context) + .pleaseEnterAnInvoiceNumber + : null, + ), + DecoratedFormField( + label: localization.poNumber, + controller: _poNumberController, + ), + DiscountField( + controller: _discountController, + value: invoice.discount, + isAmountDiscount: invoice.isAmountDiscount, + onTypeChanged: (value) => + viewModel.onChanged( + invoice.rebuild(( + b) => b..isAmountDiscount = value)), + ), + CustomField( + controller: _custom2Controller, + field: CustomFieldType.invoice2, + value: invoice.customValue2, + ), + CustomField( + controller: _custom4Controller, + field: CustomFieldType.invoice4, + value: invoice.customValue4, + ), + ], + ), ), - ), - ], - ), - widget.isQuote ? QuoteEditItemsScreen() : InvoiceEditItemsScreen(), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 2, - child: FormCard( - padding: const EdgeInsets.only( - top: kMobileDialogPadding, - right: kMobileDialogPadding / 2, - bottom: kMobileDialogPadding, - left: kMobileDialogPadding), - children: [ - 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( + ], + ), + widget.isQuote ? QuoteEditItemsScreen() : InvoiceEditItemsScreen(), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 2, + child: FormCard( + padding: const EdgeInsets.only( + top: kMobileDialogPadding, + right: kMobileDialogPadding / 2, + bottom: kMobileDialogPadding, + left: kMobileDialogPadding), + children: [ + TabBar( controller: _tabController, - children: [ - 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: '', - ), + tabs: [ + Tab(text: localization.publicNotes), + Tab(text: localization.privateNotes), + Tab(text: localization.terms), + Tab(text: localization.footer), ], ), - ), - ], + SizedBox( + height: 100, + child: TabBarView( + controller: _tabController, + children: [ + 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( - flex: 1, - child: FormCard( - padding: const EdgeInsets.only( - top: kMobileDialogPadding, - right: kMobileDialogPadding, - bottom: kMobileDialogPadding, - left: kMobileDialogPadding / 2), - children: [ - /* - DecoratedFormField( - controller: null, - enabled: false, - initialValue: '10', - 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, + Expanded( + flex: 1, + child: FormCard( + padding: const EdgeInsets.only( + top: kMobileDialogPadding, + right: kMobileDialogPadding, + bottom: kMobileDialogPadding, + left: kMobileDialogPadding / 2), + children: [ + /* + DecoratedFormField( + controller: null, + enabled: false, + initialValue: '10', + label: localization.subtotal, ), - if (company.settings.enableSecondInvoiceTaxRate) - TaxRateDropdown( - onSelected: (taxRate) => viewModel - .onChanged(invoice.applyTax(taxRate, isSecond: true)), - labelText: localization.tax, - initialTaxName: invoice.taxName2, - initialTaxRate: invoice.taxRate2, + */ + CustomSurcharges( + surcharge1Controller: _surcharge1Controller, + surcharge2Controller: _surcharge2Controller, + surcharge3Controller: _surcharge3Controller, + surcharge4Controller: _surcharge4Controller, ), - if (company.settings.enableThirdInvoiceTaxRate) - TaxRateDropdown( - onSelected: (taxRate) => viewModel - .onChanged(invoice.applyTax(taxRate, isThird: true)), - labelText: localization.tax, - initialTaxName: invoice.taxName3, - initialTaxRate: invoice.taxRate3, + 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( + 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( - surcharge1Controller: _surcharge1Controller, - surcharge2Controller: _surcharge2Controller, - surcharge3Controller: _surcharge3Controller, - surcharge4Controller: _surcharge4Controller, - isAfterTaxes: true, - ), - AppDropdownButton( - labelText: localization.design, - value: invoice.designId, - onChanged: (dynamic value) => viewModel - .onChanged(invoice.rebuild((b) => b..designId = value)), - items: company.invoiceDesignIds - .map((designId) => DropdownMenuItem( - value: designId, - child: Text(kInvoiceDesigns[designId]), - )) - .toList(), - ), - ], + AppDropdownButton( + labelText: localization.design, + value: invoice.designId, + onChanged: (dynamic value) => + viewModel + .onChanged( + invoice.rebuild((b) => b..designId = value)), + items: company.invoiceDesignIds + .map((designId) => + DropdownMenuItem( + value: designId, + child: Text(kInvoiceDesigns[designId]), + )) + .toList(), + ), + ], + ), ), - ), - ], - ), - ], + ], + ), + ], + ), ); } } diff --git a/lib/ui/invoice/edit/invoice_edit_items_desktop.dart b/lib/ui/invoice/edit/invoice_edit_items_desktop.dart index 03b4dddc7..0fdb31673 100644 --- a/lib/ui/invoice/edit/invoice_edit_items_desktop.dart +++ b/lib/ui/invoice/edit/invoice_edit_items_desktop.dart @@ -8,7 +8,9 @@ import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; class InvoiceEditItemsDesktop extends StatefulWidget { - const InvoiceEditItemsDesktop({this.viewModel}); + const InvoiceEditItemsDesktop({ + this.viewModel, + }); final EntityEditItemsVM viewModel; @@ -20,6 +22,32 @@ class InvoiceEditItemsDesktop extends StatefulWidget { class _InvoiceEditItemsDesktopState extends State { int _updatedAt; + /* + final Map _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 Widget build(BuildContext context) { final localization = AppLocalization.of(context); @@ -96,6 +124,8 @@ class _InvoiceEditItemsDesktopState extends State { lineItems[index].rebuild((b) => b..notes = value), index), minLines: 1, maxLines: 6, + //maxLines: _focusNodes[index].hasFocus ? 6 : 1, + //focusNode: _focusNodes[index], ), ), Padding( diff --git a/lib/ui/invoice/edit/invoice_edit_items_vm.dart b/lib/ui/invoice/edit/invoice_edit_items_vm.dart index 415924854..2959f8a4b 100644 --- a/lib/ui/invoice/edit/invoice_edit_items_vm.dart +++ b/lib/ui/invoice/edit/invoice_edit_items_vm.dart @@ -9,7 +9,9 @@ import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; class InvoiceEditItemsScreen extends StatelessWidget { - const InvoiceEditItemsScreen({Key key}) : super(key: key); + const InvoiceEditItemsScreen({ + Key key, + }) : super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/ui/quote/edit/quote_edit_items_vm.dart b/lib/ui/quote/edit/quote_edit_items_vm.dart index 27e3c28bd..45a400b72 100644 --- a/lib/ui/quote/edit/quote_edit_items_vm.dart +++ b/lib/ui/quote/edit/quote_edit_items_vm.dart @@ -10,7 +10,9 @@ import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; class QuoteEditItemsScreen extends StatelessWidget { - const QuoteEditItemsScreen({Key key}) : super(key: key); + const QuoteEditItemsScreen({ + Key key, + }) : super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/ui/settings/localization_settings.dart b/lib/ui/settings/localization_settings.dart index e85ad06c1..2c21f0f0e 100644 --- a/lib/ui/settings/localization_settings.dart +++ b/lib/ui/settings/localization_settings.dart @@ -33,11 +33,19 @@ class _LocalizationSettingsState extends State { bool autoValidate = false; final _firstNameController = TextEditingController(); + FocusScopeNode _focusNode; List _controllers = []; + @override + void initState() { + super.initState(); + _focusNode = FocusScopeNode(); + } + @override void dispose() { + _focusNode.dispose(); _controllers.forEach((dynamic controller) { controller.removeListener(_onChanged); controller.dispose(); @@ -78,6 +86,7 @@ class _LocalizationSettingsState extends State { onSavePressed: viewModel.onSavePressed, body: AppForm( formKey: _formKey, + focusNode: _focusNode, children: [ FormCard( children: [ diff --git a/lib/ui/settings/product_settings.dart b/lib/ui/settings/product_settings.dart index 17f10630e..6e42b13ab 100644 --- a/lib/ui/settings/product_settings.dart +++ b/lib/ui/settings/product_settings.dart @@ -21,6 +21,19 @@ class ProductSettings extends StatefulWidget { class _ProductSettingsState extends State { static final GlobalKey _formKey = GlobalKey(debugLabel: '_productSettings'); + FocusScopeNode _focusNode; + + @override + void initState() { + super.initState(); + _focusNode = FocusScopeNode(); + } + + @override + void dispose() { + _focusNode.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { @@ -33,6 +46,7 @@ class _ProductSettingsState extends State { onSavePressed: viewModel.onSavePressed, body: AppForm( formKey: _formKey, + focusNode: _focusNode, children: [ FormCard( children: [ diff --git a/lib/ui/settings/tax_settings.dart b/lib/ui/settings/tax_settings.dart index 4d7c44a77..cf6ed0cca 100644 --- a/lib/ui/settings/tax_settings.dart +++ b/lib/ui/settings/tax_settings.dart @@ -26,6 +26,19 @@ class TaxSettings extends StatefulWidget { class _TaxSettingsState extends State { static final GlobalKey _formKey = GlobalKey(debugLabel: '_taxSettings'); + FocusScopeNode _focusNode; + + @override + void initState() { + super.initState(); + _focusNode = FocusScopeNode(); + } + + @override + void dispose() { + _focusNode.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { @@ -39,6 +52,7 @@ class _TaxSettingsState extends State { onSavePressed: viewModel.onSavePressed, body: AppForm( formKey: _formKey, + focusNode: _focusNode, children: [ FormCard( children: [ diff --git a/lib/ui/settings/user_details.dart b/lib/ui/settings/user_details.dart index 5434a1d26..e354a139c 100644 --- a/lib/ui/settings/user_details.dart +++ b/lib/ui/settings/user_details.dart @@ -23,7 +23,7 @@ class UserDetails extends StatefulWidget { class _UserDetailsState extends State { static final GlobalKey _formKey = GlobalKey(debugLabel: '_userDetails'); - + FocusScopeNode _focusNode; bool autoValidate = false; final _firstNameController = TextEditingController(); @@ -34,8 +34,15 @@ class _UserDetailsState extends State { List _controllers = []; final _debouncer = Debouncer(); + @override + void initState() { + super.initState(); + _focusNode = FocusScopeNode(); + } + @override void dispose() { + _focusNode.dispose(); _controllers.forEach((dynamic controller) { controller.removeListener(_onChanged); controller.dispose(); @@ -89,6 +96,7 @@ class _UserDetailsState extends State { title: localization.userDetails, onSavePressed: viewModel.onSavePressed, body: AppForm( + focusNode: _focusNode, formKey: _formKey, children: [ FormCard( diff --git a/lib/ui/tax_rate/edit/tax_rate_edit.dart b/lib/ui/tax_rate/edit/tax_rate_edit.dart index 64943e766..d21f87480 100644 --- a/lib/ui/tax_rate/edit/tax_rate_edit.dart +++ b/lib/ui/tax_rate/edit/tax_rate_edit.dart @@ -25,6 +25,7 @@ class _TaxRateEditState extends State { static final GlobalKey _formKey = GlobalKey(debugLabel: '_taxRateEdit'); + FocusScopeNode _focusNode; bool autoValidate = false; final _nameController = TextEditingController(); @@ -85,6 +86,7 @@ class _TaxRateEditState extends State { onSavePressed: viewModel.onSavePressed, onCancelPressed: viewModel.onCancelPressed, body: AppForm( + focusNode: _focusNode, formKey: _formKey, children: [ FormCard( diff --git a/lib/ui/user/edit/user_edit.dart b/lib/ui/user/edit/user_edit.dart index a500a0f58..ba1704c15 100644 --- a/lib/ui/user/edit/user_edit.dart +++ b/lib/ui/user/edit/user_edit.dart @@ -29,7 +29,7 @@ class _UserEditState extends State { static final GlobalKey _formKey = GlobalKey(debugLabel: '_userEdit'); final _debouncer = Debouncer(); - + FocusScopeNode _focusNode; bool autoValidate = false; final _firstNameController = TextEditingController(); @@ -143,6 +143,7 @@ class _UserEditState extends State { ], ), body: AppForm( + focusNode: _focusNode, formKey: _formKey, children: [ FormCard(