mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-28 13:36:50 +00:00
commit
1750f93720
@ -230,6 +230,9 @@ class InvenTreeAPI {
|
|||||||
|
|
||||||
int get apiVersion => _apiVersion;
|
int get apiVersion => _apiVersion;
|
||||||
|
|
||||||
|
// Notification support requires API v25 or newer
|
||||||
|
bool get supportsNotifications => isConnected() && apiVersion >= 25;
|
||||||
|
|
||||||
// Are plugins enabled on the server?
|
// Are plugins enabled on the server?
|
||||||
bool _pluginsEnabled = false;
|
bool _pluginsEnabled = false;
|
||||||
|
|
||||||
@ -428,6 +431,7 @@ class InvenTreeAPI {
|
|||||||
|
|
||||||
// Return the received token
|
// Return the received token
|
||||||
_token = (data["token"] ?? "") as String;
|
_token = (data["token"] ?? "") as String;
|
||||||
|
|
||||||
print("Received token - $_token");
|
print("Received token - $_token");
|
||||||
|
|
||||||
// Request user role information (async)
|
// Request user role information (async)
|
||||||
|
51
lib/inventree/notification.dart
Normal file
51
lib/inventree/notification.dart
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import "package:inventree/inventree/model.dart";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class representing a "notification"
|
||||||
|
*/
|
||||||
|
|
||||||
|
class InvenTreeNotification extends InvenTreeModel {
|
||||||
|
|
||||||
|
InvenTreeNotification() : super();
|
||||||
|
|
||||||
|
InvenTreeNotification.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
InvenTreeNotification createFromJson(Map<String, dynamic> json) {
|
||||||
|
return InvenTreeNotification.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get URL => "notifications/";
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, String> defaultListFilters() {
|
||||||
|
|
||||||
|
// By default, only return 'unread' notifications
|
||||||
|
return {
|
||||||
|
"read": "false",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
String get message => (jsondata["message"] ?? "") as String;
|
||||||
|
|
||||||
|
DateTime? get creationDate {
|
||||||
|
if (jsondata.containsKey("creation")) {
|
||||||
|
return DateTime.tryParse((jsondata["creation"] ?? "") as String);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dismiss this notification (mark as read)
|
||||||
|
*/
|
||||||
|
Future<void> dismiss() async {
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
"${url}read/",
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -533,7 +533,7 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
Map<String, dynamic> data = {};
|
Map<String, dynamic> data = {};
|
||||||
|
|
||||||
// Note: Format of adjustment API was updated in API v14
|
// Note: Format of adjustment API was updated in API v14
|
||||||
if (InvenTreeAPI().supportModernStockTransactions()) {
|
if (api.supportModernStockTransactions()) {
|
||||||
// Modern (> 14) API
|
// Modern (> 14) API
|
||||||
data = {
|
data = {
|
||||||
"items": [
|
"items": [
|
||||||
@ -560,7 +560,7 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Expected API return code depends on server API version
|
// Expected API return code depends on server API version
|
||||||
final int expected_response = InvenTreeAPI().supportModernStockTransactions() ? 201 : 200;
|
final int expected_response = api.supportModernStockTransactions() ? 201 : 200;
|
||||||
|
|
||||||
var response = await api.post(
|
var response = await api.post(
|
||||||
endpoint,
|
endpoint,
|
||||||
|
@ -308,6 +308,9 @@
|
|||||||
"description": "history"
|
"description": "history"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"home": "Home",
|
||||||
|
"@homeScreen": {},
|
||||||
|
|
||||||
"homeScreen": "Home Screen",
|
"homeScreen": "Home Screen",
|
||||||
"@homeScreen": {},
|
"@homeScreen": {},
|
||||||
|
|
||||||
@ -461,6 +464,12 @@
|
|||||||
"description": "Notes"
|
"description": "Notes"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"notifications": "Notifications",
|
||||||
|
"@notifications": {},
|
||||||
|
|
||||||
|
"notificationsEmpty": "No unread notifications",
|
||||||
|
"@notificationsEmpty": {},
|
||||||
|
|
||||||
"noResponse": "No Response from Server",
|
"noResponse": "No Response from Server",
|
||||||
"@noResponse": {},
|
"@noResponse": {},
|
||||||
|
|
||||||
@ -631,6 +640,9 @@
|
|||||||
"quantityPositive": "Quantity must be positive",
|
"quantityPositive": "Quantity must be positive",
|
||||||
"@quantityPositive": {},
|
"@quantityPositive": {},
|
||||||
|
|
||||||
|
"queryEmpty": "Enter search query",
|
||||||
|
"@queryEmpty": {},
|
||||||
|
|
||||||
"queryNoResults": "No results for query",
|
"queryNoResults": "No results for query",
|
||||||
"@queryNoResults": {},
|
"@queryNoResults": {},
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ Widget backButton(BuildContext context, GlobalKey<ScaffoldState> key) {
|
|||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
// Display the menu
|
// Display the menu
|
||||||
key.currentState!.openDrawer();
|
key.currentState!.openDrawer();
|
||||||
print("hello?");
|
|
||||||
},
|
},
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
icon: BackButtonIcon(),
|
icon: BackButtonIcon(),
|
||||||
|
@ -174,7 +174,6 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
|||||||
icon: FaIcon(FontAwesomeIcons.shapes),
|
icon: FaIcon(FontAwesomeIcons.shapes),
|
||||||
label: L10().parts,
|
label: L10().parts,
|
||||||
),
|
),
|
||||||
// TODO - Add the "actions" item back in
|
|
||||||
BottomNavigationBarItem(
|
BottomNavigationBarItem(
|
||||||
icon: FaIcon(FontAwesomeIcons.wrench),
|
icon: FaIcon(FontAwesomeIcons.wrench),
|
||||||
label: L10().actions
|
label: L10().actions
|
||||||
|
@ -41,7 +41,7 @@ class InvenTreeDrawer extends StatelessWidget {
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => SearchWidget()
|
builder: (context) => SearchWidget(true)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,29 @@
|
|||||||
|
import "dart:async";
|
||||||
|
|
||||||
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";
|
||||||
|
|
||||||
|
import "package:inventree/api.dart";
|
||||||
import "package:inventree/app_colors.dart";
|
import "package:inventree/app_colors.dart";
|
||||||
|
import "package:inventree/app_settings.dart";
|
||||||
|
import "package:inventree/barcode.dart";
|
||||||
|
import "package:inventree/l10.dart";
|
||||||
|
import "package:inventree/settings/login.dart";
|
||||||
import "package:inventree/settings/settings.dart";
|
import "package:inventree/settings/settings.dart";
|
||||||
import "package:inventree/user_profile.dart";
|
import "package:inventree/user_profile.dart";
|
||||||
import "package:inventree/l10.dart";
|
|
||||||
import "package:inventree/barcode.dart";
|
import "package:inventree/inventree/notification.dart";
|
||||||
import "package:inventree/api.dart";
|
|
||||||
import "package:inventree/settings/login.dart";
|
|
||||||
import "package:inventree/widget/category_display.dart";
|
import "package:inventree/widget/category_display.dart";
|
||||||
import "package:inventree/widget/company_list.dart";
|
import "package:inventree/widget/company_list.dart";
|
||||||
|
import "package:inventree/widget/drawer.dart";
|
||||||
import "package:inventree/widget/location_display.dart";
|
import "package:inventree/widget/location_display.dart";
|
||||||
|
import "package:inventree/widget/notifications.dart";
|
||||||
import "package:inventree/widget/part_list.dart";
|
import "package:inventree/widget/part_list.dart";
|
||||||
import "package:inventree/widget/purchase_order_list.dart";
|
import "package:inventree/widget/purchase_order_list.dart";
|
||||||
import "package:inventree/widget/search.dart";
|
import "package:inventree/widget/search.dart";
|
||||||
import "package:inventree/widget/snacks.dart";
|
import "package:inventree/widget/snacks.dart";
|
||||||
import "package:inventree/widget/drawer.dart";
|
|
||||||
|
|
||||||
import "package:inventree/app_settings.dart";
|
|
||||||
|
|
||||||
|
|
||||||
class InvenTreeHomePage extends StatefulWidget {
|
class InvenTreeHomePage extends StatefulWidget {
|
||||||
@ -32,15 +37,29 @@ class InvenTreeHomePage extends StatefulWidget {
|
|||||||
class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
||||||
|
|
||||||
_InvenTreeHomePageState() : super() {
|
_InvenTreeHomePageState() : super() {
|
||||||
|
|
||||||
// Load display settings
|
// Load display settings
|
||||||
_loadSettings();
|
_loadSettings();
|
||||||
|
|
||||||
// Initially load the profile and attempt server connection
|
// Initially load the profile and attempt server connection
|
||||||
_loadProfile();
|
_loadProfile();
|
||||||
|
|
||||||
|
_refreshNotifications();
|
||||||
|
|
||||||
|
// Refresh notifications every ~30 seconds
|
||||||
|
Timer.periodic(
|
||||||
|
Duration(
|
||||||
|
milliseconds: 30000,
|
||||||
|
), (timer) {
|
||||||
|
_refreshNotifications();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Index of bottom navigation bar
|
||||||
|
int _tabIndex = 0;
|
||||||
|
|
||||||
|
// Number of outstanding notifications
|
||||||
|
int _notificationCounter = 0;
|
||||||
|
|
||||||
bool homeShowPo = false;
|
bool homeShowPo = false;
|
||||||
bool homeShowSubscribed = false;
|
bool homeShowSubscribed = false;
|
||||||
bool homeShowManufacturers = false;
|
bool homeShowManufacturers = false;
|
||||||
@ -52,18 +71,6 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
|||||||
// Selected user profile
|
// Selected user profile
|
||||||
UserProfile? _profile;
|
UserProfile? _profile;
|
||||||
|
|
||||||
void _search(BuildContext context) {
|
|
||||||
if (!InvenTreeAPI().checkConnection(context)) return;
|
|
||||||
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => SearchWidget()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void _scan(BuildContext context) {
|
void _scan(BuildContext context) {
|
||||||
if (!InvenTreeAPI().checkConnection(context)) return;
|
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||||
|
|
||||||
@ -168,6 +175,18 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Refresh the number of active notifications for this user
|
||||||
|
*/
|
||||||
|
Future<void> _refreshNotifications() async {
|
||||||
|
|
||||||
|
final notifications = await InvenTreeNotification().list();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_notificationCounter = notifications.length;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Widget _listTile(BuildContext context, String label, IconData icon, {Function()? callback, String role = "", String permission = ""}) {
|
Widget _listTile(BuildContext context, String label, IconData icon, {Function()? callback, String role = "", String permission = ""}) {
|
||||||
|
|
||||||
@ -224,16 +243,6 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
// Search widget
|
|
||||||
tiles.add(_listTile(
|
|
||||||
context,
|
|
||||||
L10().search,
|
|
||||||
FontAwesomeIcons.search,
|
|
||||||
callback: () {
|
|
||||||
_search(context);
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
// Parts
|
// Parts
|
||||||
tiles.add(_listTile(
|
tiles.add(_listTile(
|
||||||
context,
|
context,
|
||||||
@ -327,6 +336,79 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
|||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the main body widget for display.
|
||||||
|
* This depends on the current value of _tabIndex
|
||||||
|
*/
|
||||||
|
Widget getBody(BuildContext context) {
|
||||||
|
switch (_tabIndex) {
|
||||||
|
case 1: // Search widget
|
||||||
|
return SearchWidget(false);
|
||||||
|
case 2: // Notification widget
|
||||||
|
return NotificationWidget();
|
||||||
|
case 0: // Home widget
|
||||||
|
default:
|
||||||
|
return ListView(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
children: getListTiles(context),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Construct the bottom navigation bar
|
||||||
|
*/
|
||||||
|
List<BottomNavigationBarItem> getNavBarItems(BuildContext context) {
|
||||||
|
|
||||||
|
List<BottomNavigationBarItem> items = <BottomNavigationBarItem>[
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: FaIcon(FontAwesomeIcons.home),
|
||||||
|
label: L10().home,
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: FaIcon(FontAwesomeIcons.search),
|
||||||
|
label: L10().search,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (InvenTreeAPI().supportsNotifications) {
|
||||||
|
items.add(
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: _notificationCounter == 0 ? FaIcon(FontAwesomeIcons.bell) : Stack(
|
||||||
|
children: <Widget>[
|
||||||
|
FaIcon(FontAwesomeIcons.bell),
|
||||||
|
Positioned(
|
||||||
|
right: 0,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minWidth: 12,
|
||||||
|
minHeight: 12,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
"${_notificationCounter}",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 9,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
label: L10().notifications,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
@ -345,10 +427,18 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
drawer: InvenTreeDrawer(context),
|
drawer: InvenTreeDrawer(context),
|
||||||
body: ListView(
|
body: getBody(context),
|
||||||
scrollDirection: Axis.vertical,
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
children: getListTiles(context),
|
currentIndex: _tabIndex,
|
||||||
)
|
onTap: (int index) {
|
||||||
|
setState(() {
|
||||||
|
_tabIndex = index;
|
||||||
|
});
|
||||||
|
|
||||||
|
_refreshNotifications();
|
||||||
|
},
|
||||||
|
items: getNavBarItems(context),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
100
lib/widget/notifications.dart
Normal file
100
lib/widget/notifications.dart
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
|
||||||
|
import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
||||||
|
|
||||||
|
import "package:inventree/l10.dart";
|
||||||
|
import "package:inventree/inventree/model.dart";
|
||||||
|
import "package:inventree/inventree/notification.dart";
|
||||||
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
@override
|
||||||
|
_NotificationState createState() => _NotificationState();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class _NotificationState extends RefreshableState<NotificationWidget> {
|
||||||
|
|
||||||
|
_NotificationState() : super();
|
||||||
|
|
||||||
|
List<InvenTreeNotification> notifications = [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
AppBar? buildAppBar(BuildContext context) {
|
||||||
|
// No app bar for the notification widget
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> request (BuildContext context) async {
|
||||||
|
|
||||||
|
final results = await InvenTreeNotification().list();
|
||||||
|
|
||||||
|
notifications.clear();
|
||||||
|
|
||||||
|
for (InvenTreeModel n in results) {
|
||||||
|
if (n is InvenTreeNotification) {
|
||||||
|
notifications.add(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> dismissNotification(BuildContext context, InvenTreeNotification notification) async {
|
||||||
|
|
||||||
|
await notification.dismiss();
|
||||||
|
|
||||||
|
refresh(context);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> renderNotifications(BuildContext context) {
|
||||||
|
|
||||||
|
List<Widget> tiles = [];
|
||||||
|
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text(
|
||||||
|
L10().notifications,
|
||||||
|
),
|
||||||
|
subtitle: notifications.isEmpty ? Text(L10().notificationsEmpty) : null,
|
||||||
|
leading: notifications.isEmpty ? FaIcon(FontAwesomeIcons.bellSlash) : FaIcon(FontAwesomeIcons.bell),
|
||||||
|
trailing: Text("${notifications.length}"),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (var notification in notifications) {
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text(notification.name),
|
||||||
|
subtitle: Text(notification.message),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: FaIcon(FontAwesomeIcons.bookmark),
|
||||||
|
onPressed: () async {
|
||||||
|
dismissNotification(context, notification);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tiles;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget getBody(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: ListView(
|
||||||
|
children: ListTile.divideTiles(
|
||||||
|
context: context,
|
||||||
|
tiles: renderNotifications(context),
|
||||||
|
).toList()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -80,6 +80,14 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppBar? buildAppBar(BuildContext context) {
|
||||||
|
return AppBar(
|
||||||
|
title: Text(getAppBarTitle(context)),
|
||||||
|
actions: getAppBarActions(context),
|
||||||
|
leading: backButton(context, refreshableKey),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
@ -88,11 +96,7 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
key: refreshableKey,
|
key: refreshableKey,
|
||||||
appBar: AppBar(
|
appBar: buildAppBar(context),
|
||||||
title: Text(getAppBarTitle(context)),
|
|
||||||
actions: getAppBarActions(context),
|
|
||||||
leading: backButton(context, refreshableKey),
|
|
||||||
),
|
|
||||||
drawer: getDrawer(context),
|
drawer: getDrawer(context),
|
||||||
floatingActionButton: getFab(context),
|
floatingActionButton: getFab(context),
|
||||||
body: Builder(
|
body: Builder(
|
||||||
|
@ -21,16 +21,33 @@ import "package:inventree/widget/location_list.dart";
|
|||||||
// Widget for performing database-wide search
|
// Widget for performing database-wide search
|
||||||
class SearchWidget extends StatefulWidget {
|
class SearchWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
const SearchWidget(this.hasAppbar);
|
||||||
|
|
||||||
|
final bool hasAppbar;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_SearchDisplayState createState() => _SearchDisplayState();
|
_SearchDisplayState createState() => _SearchDisplayState(hasAppbar);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SearchDisplayState extends RefreshableState<SearchWidget> {
|
class _SearchDisplayState extends RefreshableState<SearchWidget> {
|
||||||
|
|
||||||
|
_SearchDisplayState(this.hasAppBar) : super();
|
||||||
|
|
||||||
|
final bool hasAppBar;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getAppBarTitle(BuildContext context) => L10().search;
|
String getAppBarTitle(BuildContext context) => L10().search;
|
||||||
|
|
||||||
|
@override
|
||||||
|
AppBar? buildAppBar(BuildContext context) {
|
||||||
|
if (hasAppBar) {
|
||||||
|
return super.buildAppBar(context);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final TextEditingController searchController = TextEditingController();
|
final TextEditingController searchController = TextEditingController();
|
||||||
|
|
||||||
Timer? debounceTimer;
|
Timer? debounceTimer;
|
||||||
@ -155,12 +172,15 @@ class _SearchDisplayState extends RefreshableState<SearchWidget> {
|
|||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: TextField(
|
title: TextField(
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
helperText: L10().queryEmpty,
|
||||||
|
),
|
||||||
controller: searchController,
|
controller: searchController,
|
||||||
onChanged: (String text) {
|
onChanged: (String text) {
|
||||||
onSearchTextChanged(text);
|
onSearchTextChanged(text);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
leading: IconButton(
|
trailing: IconButton(
|
||||||
icon: FaIcon(FontAwesomeIcons.backspace, color: Colors.red),
|
icon: FaIcon(FontAwesomeIcons.backspace, color: Colors.red),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
searchController.clear();
|
searchController.clear();
|
||||||
@ -315,7 +335,7 @@ class _SearchDisplayState extends RefreshableState<SearchWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (results.isEmpty) {
|
if (results.isEmpty && searchController.text.isNotEmpty) {
|
||||||
tiles.add(
|
tiles.add(
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10().queryNoResults),
|
title: Text(L10().queryNoResults),
|
||||||
|
@ -28,7 +28,7 @@ class _StockListState extends RefreshableState<StockItemList> {
|
|||||||
final Map<String, String> filters;
|
final Map<String, String> filters;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getAppBarTitle(BuildContext context) => L10().purchaseOrders;
|
String getAppBarTitle(BuildContext context) => L10().stockItems;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget getBody(BuildContext context) {
|
Widget getBody(BuildContext context) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user