mirror of
https://github.com/inventree/inventree-app.git
synced 2025-05-14 13:03:11 +00:00
Display overlay screen for blocking operations (#186)
* Catch state error in homepage widget * Add flutter_overlay_loader lib - Displays an overlay screen to indicate blocking operation * Wrap blocking widget transitions in a loading overlay - Prevents user from doing other things while loading - Shows the user that something is happening * Linting fixes * Show overlay when uploading attachment file * Show overlay when downloading file also * Show overlay when loading or submitting API forms - Major improvements to usability "feel" * UI improvements for stock item test results widget * Fix API_FORM bug - onSuccess function was not being called
This commit is contained in:
parent
277193ecb0
commit
01dd046dd1
@ -651,12 +651,6 @@ class InvenTreeAPI {
|
||||
*/
|
||||
Future<void> downloadFile(String url, {bool openOnDownload = true}) async {
|
||||
|
||||
showSnackIcon(
|
||||
L10().downloading,
|
||||
icon: FontAwesomeIcons.download,
|
||||
success: true
|
||||
);
|
||||
|
||||
// Find the local downlods directory
|
||||
final Directory dir = await getTemporaryDirectory();
|
||||
|
||||
|
@ -18,6 +18,7 @@ import "package:inventree/widget/fields.dart";
|
||||
import "package:inventree/l10.dart";
|
||||
|
||||
import "package:flutter/material.dart";
|
||||
import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
|
||||
|
||||
@ -859,7 +860,9 @@ Future<void> launchApiForm(
|
||||
|
||||
if (url.isNotEmpty) {
|
||||
|
||||
showLoadingOverlay(context);
|
||||
var options = await InvenTreeAPI().options(url);
|
||||
hideLoadingOverlay();
|
||||
|
||||
// Invalid response from server
|
||||
if (!options.isValid()) {
|
||||
@ -902,7 +905,7 @@ Future<void> launchApiForm(
|
||||
field.definition = extractFieldDefinition(serverFields, field.lookupPath);
|
||||
|
||||
// Skip fields with empty definitions
|
||||
if (field.definition.isEmpty) {
|
||||
if (url.isNotEmpty && field.definition.isEmpty) {
|
||||
print("Warning: Empty field definition for field '${fieldName}'");
|
||||
}
|
||||
|
||||
@ -987,8 +990,6 @@ class _APIFormWidgetState extends State<APIFormWidget> {
|
||||
|
||||
List<String> nonFieldErrors = [];
|
||||
|
||||
Function(Map<String, dynamic>)? onSuccess;
|
||||
|
||||
bool spacerRequired = false;
|
||||
|
||||
List<Widget> _buildForm() {
|
||||
@ -1102,20 +1103,25 @@ class _APIFormWidgetState extends State<APIFormWidget> {
|
||||
}
|
||||
|
||||
if (widget.method == "POST") {
|
||||
|
||||
showLoadingOverlay(context);
|
||||
final response = await InvenTreeAPI().post(
|
||||
widget.url,
|
||||
body: data,
|
||||
expectedStatusCode: null
|
||||
);
|
||||
hideLoadingOverlay();
|
||||
|
||||
return response;
|
||||
|
||||
} else {
|
||||
showLoadingOverlay(context);
|
||||
final response = await InvenTreeAPI().patch(
|
||||
widget.url,
|
||||
body: data,
|
||||
expectedStatusCode: null
|
||||
);
|
||||
hideLoadingOverlay();
|
||||
|
||||
return response;
|
||||
}
|
||||
@ -1259,7 +1265,7 @@ class _APIFormWidgetState extends State<APIFormWidget> {
|
||||
}
|
||||
|
||||
// Run custom onSuccess function
|
||||
var successFunc = onSuccess;
|
||||
var successFunc = widget.onSuccess;
|
||||
|
||||
// An "empty" URL means we don't want to submit the form anywhere
|
||||
// Perhaps we just want to process the data?
|
||||
@ -1398,4 +1404,4 @@ class _APIFormWidgetState extends State<APIFormWidget> {
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -708,9 +708,7 @@ class InvenTreeAttachment extends InvenTreeModel {
|
||||
* Download this attachment file
|
||||
*/
|
||||
Future<void> downloadAttachment() async {
|
||||
|
||||
await InvenTreeAPI().downloadFile(attachment);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -333,6 +333,12 @@
|
||||
"filterTemplateDetail": "Show template parts",
|
||||
"@filterTemplateDetail": {},
|
||||
|
||||
"filterTrackable": "Trackable",
|
||||
"@filterTrackable": {},
|
||||
|
||||
"filterTrackableDetail": "Show trackable parts",
|
||||
"@filterTrackableDetail": {},
|
||||
|
||||
"filterVirtual": "Virtual",
|
||||
"@filterVirtual": {},
|
||||
|
||||
|
@ -6,6 +6,7 @@ import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
||||
import "package:inventree/app_colors.dart";
|
||||
import "package:inventree/inventree/model.dart";
|
||||
import "package:inventree/widget/fields.dart";
|
||||
import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:inventree/l10.dart";
|
||||
@ -51,8 +52,8 @@ class _AttachmentWidgetState extends RefreshableState<AttachmentWidget> {
|
||||
icon: FaIcon(FontAwesomeIcons.plusCircle),
|
||||
onPressed: () async {
|
||||
FilePickerDialog.pickFile(
|
||||
onPicked: (File file) {
|
||||
upload(file);
|
||||
onPicked: (File file) async {
|
||||
await upload(context, file);
|
||||
}
|
||||
);
|
||||
},
|
||||
@ -63,9 +64,11 @@ class _AttachmentWidgetState extends RefreshableState<AttachmentWidget> {
|
||||
return actions;
|
||||
}
|
||||
|
||||
Future<void> upload(File file) async {
|
||||
Future<void> upload(BuildContext context, File file) async {
|
||||
|
||||
showLoadingOverlay(context);
|
||||
final bool result = await widget.attachment.uploadAttachment(file, widget.referenceId);
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (result) {
|
||||
showSnackIcon(L10().uploadSuccess, success: true);
|
||||
@ -121,7 +124,9 @@ class _AttachmentWidgetState extends RefreshableState<AttachmentWidget> {
|
||||
subtitle: Text(attachment.comment),
|
||||
leading: FaIcon(attachment.icon, color: COLOR_CLICK),
|
||||
onTap: () async {
|
||||
showLoadingOverlay(context);
|
||||
await attachment.downloadAttachment();
|
||||
hideLoadingOverlay();
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import "package:inventree/inventree/part.dart";
|
||||
|
||||
import "package:inventree/widget/paginator.dart";
|
||||
import "package:inventree/widget/part_detail.dart";
|
||||
import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
|
||||
|
||||
@ -125,11 +126,14 @@ class _PaginatedBomListState extends PaginatedSearchState<PaginatedBomList> {
|
||||
height: 40,
|
||||
),
|
||||
onTap: subPart == null ? null : () async {
|
||||
InvenTreePart().get(bomItem.subPartId).then((var part) {
|
||||
if (part is InvenTreePart) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
}
|
||||
});
|
||||
|
||||
showLoadingOverlay(context);
|
||||
var part = await InvenTreePart().get(bomItem.subPartId);
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (part is InvenTreePart) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import "package:inventree/inventree/part.dart";
|
||||
|
||||
import "package:inventree/widget/category_list.dart";
|
||||
import "package:inventree/widget/part_list.dart";
|
||||
import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
import "package:inventree/widget/part_detail.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
@ -125,16 +126,21 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
FontAwesomeIcons.levelUpAlt,
|
||||
color: COLOR_CLICK,
|
||||
),
|
||||
onTap: () {
|
||||
if (category == null || ((category?.parentId ?? 0) < 0)) {
|
||||
onTap: () async {
|
||||
|
||||
int parentId = category?.parentId ?? -1;
|
||||
|
||||
if (parentId < 0) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)));
|
||||
} else {
|
||||
// TODO - Refactor this code into the InvenTreePart class
|
||||
InvenTreePartCategory().get(category?.parentId ?? -1).then((var cat) {
|
||||
if (cat is InvenTreePartCategory) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(cat)));
|
||||
}
|
||||
});
|
||||
|
||||
showLoadingOverlay(context);
|
||||
var cat = await InvenTreePartCategory().get(parentId);
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (cat is InvenTreePartCategory) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(cat)));
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -54,11 +54,13 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
||||
});
|
||||
|
||||
InvenTreeAPI().registerCallback(() {
|
||||
setState(() {
|
||||
// Reload the widget
|
||||
});
|
||||
});
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
// Reload the widget
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Index of bottom navigation bar
|
||||
@ -192,6 +194,11 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore if the widget is no longer active
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
final notifications = await InvenTreeNotification().list();
|
||||
|
||||
setState(() {
|
||||
|
@ -10,6 +10,7 @@ import "package:inventree/l10.dart";
|
||||
import "package:inventree/inventree/stock.dart";
|
||||
|
||||
import "package:inventree/widget/location_list.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";
|
||||
@ -218,19 +219,21 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
title: Text(L10().parentLocation),
|
||||
subtitle: Text("${location!.parentPathString}"),
|
||||
leading: FaIcon(FontAwesomeIcons.levelUpAlt, color: COLOR_CLICK),
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
|
||||
int parent = location?.parentId ?? -1;
|
||||
int parentId = location?.parentId ?? -1;
|
||||
|
||||
if (parent < 0) {
|
||||
if (parentId < 0) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)));
|
||||
} else {
|
||||
|
||||
InvenTreeStockLocation().get(parent).then((var loc) {
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc)));
|
||||
}
|
||||
});
|
||||
showLoadingOverlay(context);
|
||||
var loc = await InvenTreeStockLocation().get(parentId);
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc)));
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -384,8 +384,8 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta
|
||||
PagedSliverList.separated(
|
||||
pagingController: _pagingController,
|
||||
builderDelegate: PagedChildBuilderDelegate<InvenTreeModel>(
|
||||
itemBuilder: (context, item, index) {
|
||||
return buildItem(context, item);
|
||||
itemBuilder: (ctx, item, index) {
|
||||
return buildItem(ctx, item);
|
||||
},
|
||||
noItemsFoundIndicatorBuilder: (context) {
|
||||
return NoResultsWidget(noResultsText);
|
||||
@ -450,9 +450,11 @@ class NoResultsWidget extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return ListTile(
|
||||
title: Text(L10().noResults),
|
||||
subtitle: Text(description),
|
||||
leading: FaIcon(FontAwesomeIcons.exclamationCircle),
|
||||
title: Text(
|
||||
description,
|
||||
style: TextStyle(fontStyle: FontStyle.italic),
|
||||
),
|
||||
leading: FaIcon(FontAwesomeIcons.exclamationCircle, color: COLOR_WARNING),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -253,15 +253,17 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
title: Text(L10().partCategory),
|
||||
subtitle: Text("${part.categoryName}"),
|
||||
leading: FaIcon(FontAwesomeIcons.sitemap, color: COLOR_CLICK),
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
if (part.categoryId > 0) {
|
||||
InvenTreePartCategory().get(part.categoryId).then((var cat) {
|
||||
|
||||
if (cat is InvenTreePartCategory) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => CategoryDisplayWidget(cat)));
|
||||
}
|
||||
});
|
||||
showLoadingOverlay(context);
|
||||
var cat = await InvenTreePartCategory().get(part.categoryId);
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (cat is InvenTreePartCategory) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => CategoryDisplayWidget(cat)));
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -104,6 +104,10 @@ class _PaginatedPartListState extends PaginatedSearchState<PaginatedPartList> {
|
||||
"label": L10().filterTemplate,
|
||||
"help_text": L10().filterTemplateDetail
|
||||
},
|
||||
"trackable": {
|
||||
"label": L10().filterTrackable,
|
||||
"help_text": L10().filterTrackableDetail,
|
||||
},
|
||||
"virtual": {
|
||||
"label": L10().filterVirtual,
|
||||
"help_text": L10().filterVirtualDetail,
|
||||
@ -122,15 +126,6 @@ class _PaginatedPartListState extends PaginatedSearchState<PaginatedPartList> {
|
||||
return page;
|
||||
}
|
||||
|
||||
void _openPart(BuildContext context, int pk) {
|
||||
// Attempt to load the part information
|
||||
InvenTreePart().get(pk).then((var part) {
|
||||
if (part is InvenTreePart) {
|
||||
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildItem(BuildContext context, InvenTreeModel model) {
|
||||
@ -147,7 +142,7 @@ class _PaginatedPartListState extends PaginatedSearchState<PaginatedPartList> {
|
||||
height: 40,
|
||||
),
|
||||
onTap: () {
|
||||
_openPart(context, part.pk);
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
|
||||
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_overlay_loader/flutter_overlay_loader.dart";
|
||||
|
||||
/*
|
||||
* Construct a circular progress indicator
|
||||
@ -10,4 +11,17 @@ Widget progressIndicator() {
|
||||
return Center(
|
||||
child: CircularProgressIndicator()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void showLoadingOverlay(BuildContext context) {
|
||||
Loader.show(
|
||||
context,
|
||||
themeData: Theme.of(context).copyWith(colorScheme: ColorScheme.fromSwatch())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void hideLoadingOverlay() {
|
||||
Loader.hide();
|
||||
}
|
||||
|
@ -64,9 +64,12 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> with
|
||||
|
||||
// Update current tab selection
|
||||
void onTabSelectionChanged(int index) {
|
||||
setState(() {
|
||||
tabIndex = index;
|
||||
});
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
tabIndex = index;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@ -87,6 +90,10 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> with
|
||||
|
||||
Future<void> refresh(BuildContext context) async {
|
||||
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
loading = true;
|
||||
});
|
||||
|
@ -1,91 +0,0 @@
|
||||
import "package:inventree/inventree/part.dart";
|
||||
import "package:inventree/widget/part_detail.dart";
|
||||
import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:flutter/material.dart";
|
||||
|
||||
import "package:inventree/l10.dart";
|
||||
|
||||
import "package:inventree/api.dart";
|
||||
|
||||
|
||||
class StarredPartWidget extends StatefulWidget {
|
||||
|
||||
const StarredPartWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_StarredPartState createState() => _StarredPartState();
|
||||
}
|
||||
|
||||
|
||||
class _StarredPartState extends RefreshableState<StarredPartWidget> {
|
||||
|
||||
List<InvenTreePart> starredParts = [];
|
||||
|
||||
@override
|
||||
String getAppBarTitle(BuildContext context) => L10().partsStarred;
|
||||
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
|
||||
final parts = await InvenTreePart().list(filters: {"starred": "true"});
|
||||
|
||||
starredParts.clear();
|
||||
|
||||
for (int idx = 0; idx < parts.length; idx++) {
|
||||
if (parts[idx] is InvenTreePart) {
|
||||
starredParts.add(parts[idx] as InvenTreePart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Widget _partResult(BuildContext context, int index) {
|
||||
final part = starredParts[index];
|
||||
|
||||
return ListTile(
|
||||
title: Text(part.fullname),
|
||||
subtitle: Text(part.description),
|
||||
leading: InvenTreeAPI().getImage(
|
||||
part.thumbnail,
|
||||
width: 40,
|
||||
height: 40
|
||||
),
|
||||
onTap: () {
|
||||
InvenTreePart().get(part.pk).then((var prt) {
|
||||
if (prt is InvenTreePart) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => PartDetailWidget(prt))
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget getBody(BuildContext context) {
|
||||
|
||||
if (loading) {
|
||||
return progressIndicator();
|
||||
}
|
||||
|
||||
if (starredParts.isEmpty) {
|
||||
return ListView(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(L10().partsNone),
|
||||
subtitle: Text(L10().partsStarredNone)
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
itemCount: starredParts.length,
|
||||
itemBuilder: _partResult,
|
||||
separatorBuilder: (_, __) => const Divider(height: 3),
|
||||
physics: ClampingScrollPhysics(),
|
||||
);
|
||||
}
|
||||
}
|
@ -475,13 +475,16 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
color: item.statusColor
|
||||
)
|
||||
),
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
if (item.partId > 0) {
|
||||
InvenTreePart().get(item.partId).then((var part) {
|
||||
if (part is InvenTreePart) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
}
|
||||
});
|
||||
|
||||
showLoadingOverlay(context);
|
||||
var part = await InvenTreePart().get(item.partId);
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (part is InvenTreePart) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
}
|
||||
}
|
||||
},
|
||||
//trailing: Text(item.serialOrQuantityDisplay()),
|
||||
@ -533,15 +536,17 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
FontAwesomeIcons.mapMarkerAlt,
|
||||
color: COLOR_CLICK,
|
||||
),
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
if (item.locationId > 0) {
|
||||
InvenTreeStockLocation().get(item.locationId).then((var loc) {
|
||||
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => LocationDisplayWidget(loc)));
|
||||
}
|
||||
});
|
||||
showLoadingOverlay(context);
|
||||
var loc = await InvenTreeStockLocation().get(item.locationId);
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => LocationDisplayWidget(loc)));
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
|
@ -156,6 +156,7 @@ class _StockItemTestResultDisplayState extends RefreshableState<StockItemTestRes
|
||||
String _test = "";
|
||||
bool _result = false;
|
||||
String _value = "";
|
||||
String _notes = "";
|
||||
|
||||
FaIcon _icon = FaIcon(FontAwesomeIcons.questionCircle, color: COLOR_BLUE);
|
||||
bool _valueRequired = false;
|
||||
@ -168,11 +169,13 @@ class _StockItemTestResultDisplayState extends RefreshableState<StockItemTestRes
|
||||
_value = item.latestResult()?.value ?? "";
|
||||
_valueRequired = item.requiresValue;
|
||||
_attachmentRequired = item.requiresAttachment;
|
||||
_notes = item.latestResult()?.notes ?? "";
|
||||
} else if (item is InvenTreeStockItemTestResult) {
|
||||
_result = item.result;
|
||||
_test = item.testName;
|
||||
_required = false;
|
||||
_value = item.value;
|
||||
_notes = item.notes;
|
||||
}
|
||||
|
||||
if (_result == true) {
|
||||
@ -187,8 +190,9 @@ class _StockItemTestResultDisplayState extends RefreshableState<StockItemTestRes
|
||||
|
||||
tiles.add(ListTile(
|
||||
title: Text(_test, style: TextStyle(fontWeight: _required ? FontWeight.bold : FontWeight.normal)),
|
||||
subtitle: Text(_value),
|
||||
trailing: _icon,
|
||||
subtitle: Text(_notes),
|
||||
trailing: Text(_value),
|
||||
leading: _icon,
|
||||
onLongPress: () {
|
||||
addTestResult(
|
||||
context,
|
||||
|
@ -104,14 +104,6 @@ class _PaginatedStockItemListState extends PaginatedSearchState<PaginatedStockIt
|
||||
return page;
|
||||
}
|
||||
|
||||
void _openItem(BuildContext context, int pk) {
|
||||
InvenTreeStockItem().get(pk).then((var item) {
|
||||
if (item is InvenTreeStockItem) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => StockDetailWidget(item)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildItem(BuildContext context, InvenTreeModel model) {
|
||||
|
||||
@ -132,7 +124,7 @@ class _PaginatedStockItemListState extends PaginatedSearchState<PaginatedStockIt
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
_openItem(context, item.pk);
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => StockDetailWidget(item)));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -291,6 +291,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.9+1"
|
||||
flutter_overlay_loader:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_overlay_loader
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -22,12 +22,13 @@ dependencies:
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
flutter_markdown: ^0.6.9 # Rendering markdown
|
||||
flutter_overlay_loader: ^2.0.0 # Overlay screen support
|
||||
font_awesome_flutter: ^9.1.0 # FontAwesome icon set
|
||||
http: ^0.13.4
|
||||
image_picker: ^0.8.3 # Select or take photos
|
||||
infinite_scroll_pagination: ^3.1.0 # Let the server do all the work!
|
||||
intl: ^0.17.0
|
||||
one_context: ^1.1.0 # Dialogs without requiring context
|
||||
one_context: ^1.1.1 # Dialogs without requiring context
|
||||
open_file: ^3.2.1 # Open local files
|
||||
package_info_plus: ^1.0.4 # App information introspection
|
||||
path: ^1.8.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user