2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-04-28 13:36:50 +00:00

Keyboard fix (#306)

* Remove focusNode in search widget

- Was causing some issues in iOS in particular

* Improve search UX by cancelling previous query

- Prevent successive search queries from being displayed

* Update release notes

* Add "type" for cancelable operation
This commit is contained in:
Oliver 2023-04-10 17:07:06 +10:00 committed by GitHub
parent 020cc4497c
commit efb7ff4170
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 50 deletions

View File

@ -1,6 +1,13 @@
## InvenTree App Release Notes ## InvenTree App Release Notes
--- ---
### 0.11.1 - April 2023
---
- Fixes keyboard bug in search widget
- Adds ability to create new purchase orders directly from the app
- Adds support for the "contact" field to purchase orders
### 0.11.0 - April 2023 ### 0.11.0 - April 2023
--- ---

View File

@ -1,5 +1,5 @@
import "dart:async"; import "dart:async";
import "package:async/async.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:font_awesome_flutter/font_awesome_flutter.dart"; import "package:font_awesome_flutter/font_awesome_flutter.dart";
@ -40,16 +40,10 @@ class _SearchDisplayState extends RefreshableState<SearchWidget> {
final bool hasAppBar; final bool hasAppBar;
@override CancelableOperation<void>? _search_query;
void initState() {
super.initState();
_focusNode = FocusNode();
}
@override @override
void dispose() { void dispose() {
_focusNode.dispose();
super.dispose(); super.dispose();
} }
@ -99,8 +93,6 @@ class _SearchDisplayState extends RefreshableState<SearchWidget> {
int nSupplierResults = 0; int nSupplierResults = 0;
int nPurchaseOrderResults = 0; int nPurchaseOrderResults = 0;
late FocusNode _focusNode;
// Callback when the text is being edited // Callback when the text is being edited
// Incorporates a debounce timer to restrict search frequency // Incorporates a debounce timer to restrict search frequency
void onSearchTextChanged(String text, {bool immediate = false}) { void onSearchTextChanged(String text, {bool immediate = false}) {
@ -114,9 +106,6 @@ class _SearchDisplayState extends RefreshableState<SearchWidget> {
} else { } else {
debounceTimer = Timer(Duration(milliseconds: 250), () { debounceTimer = Timer(Duration(milliseconds: 250), () {
search(text); search(text);
if (!_focusNode.hasFocus) {
_focusNode.requestFocus();
}
}); });
} }
} }
@ -147,12 +136,36 @@ class _SearchDisplayState extends RefreshableState<SearchWidget> {
return count; return count;
} }
// Actually perform the search query
Future<void> _perform_search(Map<String, dynamic> body) async {
InvenTreeAPI().post(
"search/",
body: body,
expectedStatusCode: 200).then((APIResponse response) {
decrementPendingSearches();
Map<String, dynamic> results = {};
if (response.data is Map<String, dynamic>) {
results = response.data as Map<String, dynamic>;
}
if (mounted) {
setState(() {
nPartResults = getSearchResultCount(results, "part");
nCategoryResults = getSearchResultCount(results, "partcategory");
nStockResults = getSearchResultCount(results, "stockitem");
nLocationResults = getSearchResultCount(results, "stocklocation");
nSupplierResults = 0; //getSearchResultCount(results, "")
nPurchaseOrderResults = getSearchResultCount(results, "purchaseorder");
});
}
});
}
/* /*
* Initiate multiple search requests to the server. * Callback when the search input is changed
* Each request returns at *some point* in the future,
* by which time the search input may have changed, giving unexpected results.
*
* So, each request only causes an update *if* the search term is still the same when it completes
*/ */
Future<void> search(String term) async { Future<void> search(String term) async {
var api = InvenTreeAPI(); var api = InvenTreeAPI();
@ -173,6 +186,15 @@ class _SearchDisplayState extends RefreshableState<SearchWidget> {
nPendingSearches = 0; nPendingSearches = 0;
}); });
// Cancel the previous search query (if in progress)
if (_search_query != null) {
if (!_search_query!.isCanceled) {
_search_query!.cancel();
}
}
_search_query = null;
if (term.isEmpty) { if (term.isEmpty) {
return; return;
} }
@ -216,29 +238,9 @@ class _SearchDisplayState extends RefreshableState<SearchWidget> {
if (body.isNotEmpty) { if (body.isNotEmpty) {
nPendingSearches++; nPendingSearches++;
api.post( _search_query = CancelableOperation.fromFuture(
"search/", _perform_search(body),
body: body, );
expectedStatusCode: 200).then((APIResponse response) {
decrementPendingSearches();
Map<String, dynamic> results = {};
if (response.data is Map<String, dynamic>) {
results = response.data as Map<String, dynamic>;
}
if (mounted) {
setState(() {
nPartResults = getSearchResultCount(results, "part");
nCategoryResults = getSearchResultCount(results, "partcategory");
nStockResults = getSearchResultCount(results, "stockitem");
nLocationResults = getSearchResultCount(results, "stocklocation");
nSupplierResults = 0; //getSearchResultCount(results, "")
nPurchaseOrderResults = getSearchResultCount(results, "purchaseorder");
});
}
});
} }
} else { } else {
legacySearch(term); legacySearch(term);
@ -344,16 +346,13 @@ class _SearchDisplayState extends RefreshableState<SearchWidget> {
), ),
key: _formKey, key: _formKey,
readOnly: false, readOnly: false,
autofocus: false, autofocus: true,
autocorrect: false, autocorrect: false,
focusNode: _focusNode,
controller: searchController, controller: searchController,
onChanged: (String text) { onChanged: (String text) {
onSearchTextChanged(text); onSearchTextChanged(text);
_focusNode.requestFocus();
}, },
onFieldSubmitted: (String text) { onFieldSubmitted: (String text) {
_focusNode.requestFocus();
}, },
), ),
trailing: GestureDetector( trailing: GestureDetector(
@ -363,7 +362,6 @@ class _SearchDisplayState extends RefreshableState<SearchWidget> {
), ),
onTap: () { onTap: () {
searchController.clear(); searchController.clear();
_focusNode.requestFocus();
onSearchTextChanged("", immediate: true); onSearchTextChanged("", immediate: true);
}, },
), ),
@ -541,10 +539,6 @@ class _SearchDisplayState extends RefreshableState<SearchWidget> {
} }
} }
if (!_focusNode.hasFocus) {
_focusNode.requestFocus();
}
return tiles; return tiles;
} }