2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-04-28 21:46:46 +00:00
inventree-app/lib/widget/search.dart
2022-03-29 19:54:19 +11:00

347 lines
7.9 KiB
Dart

import "dart:async";
import "package:flutter/material.dart";
import "package:font_awesome_flutter/font_awesome_flutter.dart";
import "package:inventree/inventree/company.dart";
import "package:inventree/inventree/purchase_order.dart";
import "package:inventree/widget/part_list.dart";
import "package:inventree/widget/purchase_order_list.dart";
import "package:inventree/widget/refreshable_state.dart";
import "package:inventree/l10.dart";
import "package:inventree/inventree/part.dart";
import "package:inventree/inventree/stock.dart";
import "package:inventree/widget/stock_list.dart";
import "package:inventree/widget/category_list.dart";
import "package:inventree/widget/company_list.dart";
import "package:inventree/widget/location_list.dart";
// Widget for performing database-wide search
class SearchWidget extends StatefulWidget {
@override
_SearchDisplayState createState() => _SearchDisplayState();
}
class _SearchDisplayState extends RefreshableState<SearchWidget> {
@override
String getAppBarTitle(BuildContext context) => L10().search;
final TextEditingController searchController = TextEditingController();
Timer? debounceTimer;
int nPartResults = 0;
int nCategoryResults = 0;
int nStockResults = 0;
int nLocationResults = 0;
int nSupplierResults = 0;
int nPurchaseOrderResults = 0;
// Callback when the text is being edited
// Incorporates a debounce timer to restrict search frequency
void onSearchTextChanged(String text, {bool immediate = false}) {
if (debounceTimer?.isActive ?? false) {
debounceTimer!.cancel();
}
if (immediate) {
search(text);
} else {
debounceTimer = Timer(Duration(milliseconds: 250), () {
search(text);
});
}
}
Future<void> search(String term) async {
if (term.isEmpty) {
setState(() {
// Do not search on an empty string
nPartResults = 0;
nCategoryResults = 0;
nStockResults = 0;
nLocationResults = 0;
nSupplierResults = 0;
nPurchaseOrderResults = 0;
});
return;
}
// Search parts
InvenTreePart().count(
searchQuery: term
).then((int n) {
setState(() {
nPartResults = n;
});
});
// Search part categories
InvenTreePartCategory().count(
searchQuery: term,
).then((int n) {
setState(() {
nCategoryResults = n;
});
});
// Search stock items
InvenTreeStockItem().count(
searchQuery: term
).then((int n) {
setState(() {
nStockResults = n;
});
});
// Search stock locations
InvenTreeStockLocation().count(
searchQuery: term
).then((int n) {
setState(() {
nLocationResults = n;
});
});
// Search suppliers
InvenTreeCompany().count(
searchQuery: term,
filters: {
"is_supplier": "true",
},
).then((int n) {
setState(() {
nSupplierResults = n;
});
});
// Search purchase orders
InvenTreePurchaseOrder().count(
searchQuery: term,
filters: {
"outstanding": "true"
}
).then((int n) {
setState(() {
nPurchaseOrderResults = n;
});
});
}
List<Widget> _tiles(BuildContext context) {
List<Widget> tiles = [];
// Search input
tiles.add(
InputDecorator(
decoration: InputDecoration(
),
child: ListTile(
title: TextField(
readOnly: false,
controller: searchController,
onChanged: (String text) {
onSearchTextChanged(text);
},
),
leading: IconButton(
icon: FaIcon(FontAwesomeIcons.backspace, color: Colors.red),
onPressed: () {
searchController.clear();
onSearchTextChanged("", immediate: true);
},
),
)
)
);
String query = searchController.text;
List<Widget> results = [];
// Part Results
if (nPartResults > 0) {
results.add(
ListTile(
title: Text(L10().parts),
leading: FaIcon(FontAwesomeIcons.shapes),
trailing: Text("${nPartResults}"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PartList(
{
"original_search": query
}
)
)
);
}
)
);
}
// Part Category Results
if (nCategoryResults > 0) {
results.add(
ListTile(
title: Text(L10().partCategories),
leading: FaIcon(FontAwesomeIcons.sitemap),
trailing: Text("${nCategoryResults}"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PartCategoryList(
{
"original_search": query
}
)
)
);
},
)
);
}
// Stock Item Results
if (nStockResults > 0) {
results.add(
ListTile(
title: Text(L10().stockItems),
leading: FaIcon(FontAwesomeIcons.boxes),
trailing: Text("${nStockResults}"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => StockItemList(
{
"original_search": query,
}
)
)
);
},
)
);
}
// Stock location results
if (nLocationResults > 0) {
results.add(
ListTile(
title: Text(L10().stockLocations),
leading: FaIcon(FontAwesomeIcons.mapMarkerAlt),
trailing: Text("${nLocationResults}"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => StockLocationList(
{
"original_search": query
}
)
)
);
},
)
);
}
// Suppliers
if (nSupplierResults > 0) {
results.add(
ListTile(
title: Text(L10().suppliers),
leading: FaIcon(FontAwesomeIcons.building),
trailing: Text("${nSupplierResults}"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CompanyListWidget(
L10().suppliers,
{
"is_supplier": "true",
"original_search": query
}
)
)
);
},
)
);
}
// Purchase orders
if (nPurchaseOrderResults > 0) {
results.add(
ListTile(
title: Text(L10().purchaseOrders),
leading: FaIcon(FontAwesomeIcons.shoppingCart),
trailing: Text("${nPurchaseOrderResults}"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PurchaseOrderListWidget(
filters: {
"original_search": query
}
)
)
);
},
)
);
}
if (results.isEmpty) {
tiles.add(
ListTile(
title: Text(L10().queryNoResults),
leading: FaIcon(FontAwesomeIcons.search),
)
);
} else {
for (Widget result in results) {
tiles.add(result);
}
}
return tiles;
}
@override
Widget getBody(BuildContext context) {
return Center(
child: ListView(
children: ListTile.divideTiles(
context: context,
tiles: _tiles(context),
).toList()
)
);
}
}