import "dart:async"; import 'package:inventree/inventree/company.dart'; import 'package:inventree/inventree/purchase_order.dart'; import "package:inventree/widget/part_detail.dart"; import "package:inventree/widget/progress.dart"; import 'package:inventree/widget/refreshable_state.dart'; import "package:inventree/widget/snacks.dart"; import "package:inventree/widget/stock_detail.dart"; import "package:flutter/cupertino.dart"; import "package:flutter/material.dart"; import "package:font_awesome_flutter/font_awesome_flutter.dart"; import "package:inventree/l10.dart"; import "package:inventree/inventree/part.dart"; import "package:inventree/inventree/stock.dart"; // Widget for performing database-wide search class SearchWidget extends StatefulWidget { @override _SearchDisplayState createState() => _SearchDisplayState(); } class _SearchDisplayState extends RefreshableState { @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 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( search: term ).then((int n) { setState(() { nPartResults = n; }); }); // Search part categories InvenTreePartCategory().count( search: term, ).then((int n) { setState(() { nCategoryResults = n; }); }); // Search stock items InvenTreeStockItem().count( search: term ).then((int n) { setState(() { nStockResults = n; }); }); // Search stock locations InvenTreeStockLocation().count( search: term ).then((int n) { setState(() { nLocationResults = n; }); }); // Search suppliers InvenTreeCompany().count( search: term, filters: { "is_supplier": "true", }, ).then((int n) { setState(() { nSupplierResults = n; }); }); // Search purchase orders InvenTreePurchaseOrder().count( search: term, filters: { "outstanding": "true" } ).then((int n) { setState(() { nPurchaseOrderResults = n; }); }); } List _tiles(BuildContext context) { List tiles = []; // Search input tiles.add( InputDecorator( decoration: InputDecoration( ), child: ListTile( title: TextField( readOnly: false, controller: searchController, onChanged: (String text) { onSearchTextChanged(text); }, ), trailing: IconButton( icon: FaIcon(FontAwesomeIcons.backspace, color: Colors.red), onPressed: () { searchController.clear(); onSearchTextChanged("", immediate: true); }, ), ) ) ); List results = []; // Part Results if (nPartResults > 0) { results.add( ListTile( title: Text(L10().parts), leading: FaIcon(FontAwesomeIcons.shapes), trailing: Text("${nPartResults}"), onTap: () { // Show part results } ) ); } // Part Category Results if (nCategoryResults > 0) { results.add( ListTile( title: Text(L10().partCategories), leading: FaIcon(FontAwesomeIcons.sitemap), trailing: Text("${nCategoryResults}"), ) ); } // Stock Item Results if (nStockResults > 0) { results.add( ListTile( title: Text(L10().stockItems), leading: FaIcon(FontAwesomeIcons.boxes), trailing: Text("${nStockResults}"), ) ); } // Stock location results if (nLocationResults > 0) { results.add( ListTile( title: Text(L10().stockLocations), leading: FaIcon(FontAwesomeIcons.mapMarkerAlt), trailing: Text("${nLocationResults}"), ) ); } // Suppliers if (nSupplierResults > 0) { results.add( ListTile( title: Text(L10().suppliers), leading: FaIcon(FontAwesomeIcons.building), trailing: Text("${nSupplierResults}") ) ); } // Purchase orders if (nPurchaseOrderResults > 0) { results.add( ListTile( title: Text(L10().purchaseOrders), leading: FaIcon(FontAwesomeIcons.shoppingCart), trailing: Text("${nPurchaseOrderResults}"), ) ); } 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() ) ); } }