2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-04-28 05:26:47 +00:00

Add snackbar with icon

- stock adjust
- part edit
- location edit
This commit is contained in:
Oliver Walters 2021-02-10 23:51:38 +11:00
parent ce2a866384
commit c8c056f96d
7 changed files with 154 additions and 34 deletions

View File

@ -323,6 +323,15 @@ class InvenTreeStockItem extends InvenTreeModel {
double get quantity => double.tryParse(jsondata['quantity'].toString() ?? '0');
String get quantityString {
if (quantity.toInt() == quantity) {
return quantity.toInt().toString();
} else {
return quantity.toString();
}
}
int get locationId => jsondata['location'] as int ?? -1;
bool isSerialized() => serialNumber != null && quantity.toInt() == 1;

View File

@ -161,7 +161,39 @@ void hideProgressDialog(BuildContext context) {
Navigator.pop(context);
}
void showFormDialog(BuildContext context, String title, {GlobalKey<FormState> key, List<Widget> fields, List<Widget> actions}) {
void showFormDialog(BuildContext context, String title, {GlobalKey<FormState> key, List<Widget> fields, List<Widget> actions, Function callback}) {
// Undefined actions = OK + Cancel
if (actions == null) {
actions = <Widget>[
FlatButton(
child: Text(I18N.of(context).cancel),
onPressed: () {
// Close the form
Navigator.pop(context);
}
),
FlatButton(
child: Text(I18N.of(context).save),
onPressed: () {
if (key.currentState.validate()) {
key.currentState.save();
// Close the dialog
Navigator.pop(context);
// Callback
if (callback != null) {
callback();
}
}
}
)
];
}
showDialog(
context: context,
builder: (BuildContext context) {

View File

@ -1,13 +1,18 @@
import 'package:InvenTree/api.dart';
import 'package:InvenTree/inventree/stock.dart';
import 'package:InvenTree/preferences.dart';
import 'package:InvenTree/widget/refreshable_state.dart';
import 'package:InvenTree/widget/fields.dart';
import 'package:InvenTree/widget/dialogs.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:flutter/foundation.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:InvenTree/widget/refreshable_state.dart';
class LocationDisplayWidget extends StatefulWidget {
@ -25,6 +30,8 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
final InvenTreeStockLocation location;
final _editLocationKey = GlobalKey<FormState>();
@override
String getAppBarTitle(BuildContext context) { return "Stock Location"; }
@ -33,13 +40,53 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
return <Widget>[
IconButton(
icon: FaIcon(FontAwesomeIcons.edit),
tooltip: "Edit",
// TODO - Edit stock location
onPressed: null,
tooltip: I18N.of(context).edit,
onPressed: _editLocationDialog,
)
];
}
void _editLocation(Map<String, String> values) async {
final bool result = await location.update(context, values: values);
showSnackIcon(
refreshableKey,
result ? "Location edited" : "Location editing failed",
success: result
);
refresh();
}
void _editLocationDialog() {
// Values which an be edited
var _name;
var _description;
showFormDialog(context, I18N.of(context).editLocation,
key: _editLocationKey,
callback: () {
_editLocation({
"name": _name,
"description": _description
});
},
fields: <Widget> [
StringField(
label: I18N.of(context).name,
initial: location.name,
onSaved: (value) => _name = value,
),
StringField(
label: I18N.of(context).description,
initial: location.description,
onSaved: (value) => _description = value,
)
]
);
}
_LocationDisplayState(this.location) {}
List<InvenTreeStockLocation> _sublocations = List<InvenTreeStockLocation>();
@ -67,6 +114,9 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
int pk = location?.pk ?? -1;
// Reload location information
await location.reload(context);
// Request a list of sub-locations under this one
await InvenTreeStockLocation().list(context, filters: {"parent": "$pk"}).then((var locs) {
_sublocations.clear();

View File

@ -1,4 +1,6 @@
import 'dart:io';
import 'package:InvenTree/widget/snacks.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -74,9 +76,13 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
void _savePart(Map<String, String> values) async {
Navigator.of(context).pop();
final bool result = await part.update(context, values: values);
var response = await part.update(context, values: values);
showSnackIcon(
refreshableKey,
result ? "Part edited" : "Part editing failed",
success: result
);
refresh();
}
@ -96,34 +102,18 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
var _name;
var _description;
var _ipn;
var _revision;
var _keywords;
showFormDialog(context, I18N.of(context).editPart,
key: _editPartKey,
actions: <Widget>[
FlatButton(
child: Text(I18N.of(context).cancel),
onPressed: () {
Navigator.pop(context);
},
),
FlatButton(
child: Text(I18N.of(context).save),
onPressed: () {
if (_editPartKey.currentState.validate()) {
_editPartKey.currentState.save();
_savePart({
"name": _name,
"description": _description,
"IPN": _ipn,
"keywords": _keywords,
});
}
},
),
],
callback: () {
_savePart({
"name": _name,
"description": _description,
"IPN": _ipn,
"keywords": _keywords
});
},
fields: <Widget>[
StringField(
label: I18N.of(context).name,
@ -163,7 +153,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => FullScreenWidget(part.name, part.image))
MaterialPageRoute(builder: (context) => FullScreenWidget(part.fullname, part.image))
);
}),
)

View File

@ -8,6 +8,8 @@ import 'package:InvenTree/widget/drawer.dart';
abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
final refreshableKey = GlobalKey<ScaffoldState>();
// Storage for context once "Build" is called
BuildContext context;
@ -80,6 +82,7 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
this.context = context;
return Scaffold(
key: refreshableKey,
appBar: getAppBar(context),
drawer: getDrawer(context),
floatingActionButton: getFab(context),

33
lib/widget/snacks.dart Normal file
View File

@ -0,0 +1,33 @@
/*
* Display a snackbar with:
*
* a) Text on the left
* b) Icon on the right
*
* | Text <icon> |
*/
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void showSnackIcon(GlobalKey<ScaffoldState> key, String text, {IconData icon, bool success}) {
// If icon not specified, use the success status
if (icon == null) {
icon = (success == true) ? FontAwesomeIcons.checkCircle : FontAwesomeIcons.timesCircle;
}
key.currentState.showSnackBar(
SnackBar(
content: Row(
children: [
Text(text),
Spacer(),
FaIcon(icon)
]
),
)
);
}

View File

@ -1,4 +1,5 @@
import 'dart:io';
import 'package:InvenTree/barcode.dart';
import 'package:InvenTree/inventree/stock.dart';
@ -8,6 +9,7 @@ import 'package:InvenTree/widget/fields.dart';
import 'package:InvenTree/widget/location_display.dart';
import 'package:InvenTree/widget/part_detail.dart';
import 'package:InvenTree/widget/refreshable_state.dart';
import 'package:InvenTree/widget/snacks.dart';
import 'package:InvenTree/widget/stock_item_test_results.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@ -22,6 +24,7 @@ import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:http/http.dart';
class StockDetailWidget extends StatefulWidget {
@ -71,10 +74,10 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
var response = await item.addStock(quantity, notes: _notesController.text);
_notesController.clear();
_stockUpdateMessage(response);
// TODO - Handle error cases
refresh();
// TODO - Display a snackbar here indicating the action was successful (or otherwise)
}
void _addStockDialog() async {