From c8c056f96deb98fd8af79a94480669769ac4a60b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 10 Feb 2021 23:51:38 +1100 Subject: [PATCH 1/6] Add snackbar with icon - stock adjust - part edit - location edit --- lib/inventree/stock.dart | 9 +++++ lib/widget/dialogs.dart | 34 +++++++++++++++++- lib/widget/location_display.dart | 58 ++++++++++++++++++++++++++++--- lib/widget/part_detail.dart | 44 +++++++++-------------- lib/widget/refreshable_state.dart | 3 ++ lib/widget/snacks.dart | 33 ++++++++++++++++++ lib/widget/stock_detail.dart | 7 ++-- 7 files changed, 154 insertions(+), 34 deletions(-) create mode 100644 lib/widget/snacks.dart diff --git a/lib/inventree/stock.dart b/lib/inventree/stock.dart index 516672f4..022d0642 100644 --- a/lib/inventree/stock.dart +++ b/lib/inventree/stock.dart @@ -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; diff --git a/lib/widget/dialogs.dart b/lib/widget/dialogs.dart index 803476ab..3baa1c44 100644 --- a/lib/widget/dialogs.dart +++ b/lib/widget/dialogs.dart @@ -161,7 +161,39 @@ void hideProgressDialog(BuildContext context) { Navigator.pop(context); } -void showFormDialog(BuildContext context, String title, {GlobalKey key, List fields, List actions}) { +void showFormDialog(BuildContext context, String title, {GlobalKey key, List fields, List actions, Function callback}) { + + // Undefined actions = OK + Cancel + if (actions == null) { + actions = [ + 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) { diff --git a/lib/widget/location_display.dart b/lib/widget/location_display.dart index 5498793a..8d6b7b7c 100644 --- a/lib/widget/location_display.dart +++ b/lib/widget/location_display.dart @@ -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 { final InvenTreeStockLocation location; + final _editLocationKey = GlobalKey(); + @override String getAppBarTitle(BuildContext context) { return "Stock Location"; } @@ -33,13 +40,53 @@ class _LocationDisplayState extends RefreshableState { return [ IconButton( icon: FaIcon(FontAwesomeIcons.edit), - tooltip: "Edit", - // TODO - Edit stock location - onPressed: null, + tooltip: I18N.of(context).edit, + onPressed: _editLocationDialog, ) ]; } + void _editLocation(Map 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: [ + 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 _sublocations = List(); @@ -67,6 +114,9 @@ class _LocationDisplayState extends RefreshableState { 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(); diff --git a/lib/widget/part_detail.dart b/lib/widget/part_detail.dart index 64cedbe9..6c34fc53 100644 --- a/lib/widget/part_detail.dart +++ b/lib/widget/part_detail.dart @@ -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 { void _savePart(Map 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 { var _name; var _description; var _ipn; - var _revision; var _keywords; showFormDialog(context, I18N.of(context).editPart, key: _editPartKey, - actions: [ - 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: [ StringField( label: I18N.of(context).name, @@ -163,7 +153,7 @@ class _PartDisplayState extends RefreshableState { onTap: () { Navigator.push( context, - MaterialPageRoute(builder: (context) => FullScreenWidget(part.name, part.image)) + MaterialPageRoute(builder: (context) => FullScreenWidget(part.fullname, part.image)) ); }), ) diff --git a/lib/widget/refreshable_state.dart b/lib/widget/refreshable_state.dart index 7bf1bbde..53ed0a8e 100644 --- a/lib/widget/refreshable_state.dart +++ b/lib/widget/refreshable_state.dart @@ -8,6 +8,8 @@ import 'package:InvenTree/widget/drawer.dart'; abstract class RefreshableState extends State { + final refreshableKey = GlobalKey(); + // Storage for context once "Build" is called BuildContext context; @@ -80,6 +82,7 @@ abstract class RefreshableState extends State { this.context = context; return Scaffold( + key: refreshableKey, appBar: getAppBar(context), drawer: getDrawer(context), floatingActionButton: getFab(context), diff --git a/lib/widget/snacks.dart b/lib/widget/snacks.dart new file mode 100644 index 00000000..9b2f3cad --- /dev/null +++ b/lib/widget/snacks.dart @@ -0,0 +1,33 @@ + +/* + * Display a snackbar with: + * + * a) Text on the left + * b) Icon on the right + * + * | Text | + */ + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +void showSnackIcon(GlobalKey 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) + ] + ), + ) + ); +} \ No newline at end of file diff --git a/lib/widget/stock_detail.dart b/lib/widget/stock_detail.dart index dc4beddd..2acca4c7 100644 --- a/lib/widget/stock_detail.dart +++ b/lib/widget/stock_detail.dart @@ -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 { 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 { From 3757010599ddf1082f5545d184139921ab13f739 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 10 Feb 2021 23:54:00 +1100 Subject: [PATCH 2/6] Refactor stock adjustment actions - Handle timeout - Handle error --- lib/inventree/stock.dart | 91 ++++++++++++++++++++++++------------ lib/l10n | 2 +- lib/widget/stock_detail.dart | 36 +++++++------- 3 files changed, 81 insertions(+), 48 deletions(-) diff --git a/lib/inventree/stock.dart b/lib/inventree/stock.dart index 022d0642..0d3aaab6 100644 --- a/lib/inventree/stock.dart +++ b/lib/inventree/stock.dart @@ -1,4 +1,5 @@ import 'package:InvenTree/inventree/part.dart'; +import 'package:InvenTree/widget/dialogs.dart'; import 'package:flutter/cupertino.dart'; import 'package:http/http.dart' as http; import 'model.dart'; @@ -391,51 +392,81 @@ class InvenTreeStockItem extends InvenTreeModel { return item; } - Future countStock(double quan, {String notes}) async { + /* + * Perform stocktake action: + * + * - Add + * - Remove + * - Count + */ + Future adjustStock(BuildContext context, String endpoint, double q, {String notes}) async { - // Cannot 'count' a serialized StockItem + // Serialized stock cannot be adjusted if (isSerialized()) { - return null; + return false; } - // Cannot count negative stock - if (quan < 0) { - return null; + // Cannot handle negative stock + if (q < 0) { + return false; } - return api.post("/stock/count/", body: { - "item": { - "pk": "${pk}", - "quantity": "${quan}", - }, - "notes": notes ?? '', + var response = await api.post( + endpoint, + body: { + "item": { + "pk": "${pk}", + "quantity": "${q}", + }, + "notes": notes ?? '', + }).timeout(Duration(seconds: 10)).catchError((error) { + if (error is TimeoutException) { + showTimeoutError(context); + } else if (error is SocketException) { + showServerError( + context, + I18N.of(context).connectionRefused, + error.toString() + ); + } else { + // Re-throw the error, let sentry handle it! + throw error; + } + + // Null response if error + return null; }); + + if (response == null) return false; + + if (response.statusCode != 200) { + showStatusCodeError(context, response.statusCode); + return false; + } + + // Stock adjustment succeeded! + return true; } - Future addStock(double quan, {String notes}) async { + Future countStock(BuildContext context, double q, {String notes}) async { - if (isSerialized() || quan <= 0) return null; + final bool result = await adjustStock(context, "/stock/count", q, notes: notes); - return api.post("/stock/add/", body: { - "item": { - "pk": "${pk}", - "quantity": "${quan}", - }, - "notes": notes ?? '', - }); + return result; } - Future removeStock(double quan, {String notes}) async { + Future addStock(BuildContext context, double q, {String notes}) async { - if (isSerialized() || quan <= 0) return null; + final bool result = await adjustStock(context, "/stock/add/", q, notes: notes); - return api.post("/stock/remove/", body: { - "item": { - "pk": "${pk}", - "quantity": "${quan}", - }, - "notes": notes ?? '', - }); + return result; + } + + Future removeStock(BuildContext context, double q, {String notes}) async { + + final bool result = await adjustStock(context, "/stock/remove/", q, notes: notes); + + return result; } Future transferStock(int location, {double quantity, String notes}) async { diff --git a/lib/l10n b/lib/l10n index 249e4964..ed3bd59b 160000 --- a/lib/l10n +++ b/lib/l10n @@ -1 +1 @@ -Subproject commit 249e4964a08b79e53df7e1ea18b051de0d307905 +Subproject commit ed3bd59b15b2c69b9a21649a0e0507efd811c1a1 diff --git a/lib/widget/stock_detail.dart b/lib/widget/stock_detail.dart index 2acca4c7..7b173410 100644 --- a/lib/widget/stock_detail.dart +++ b/lib/widget/stock_detail.dart @@ -70,13 +70,11 @@ class _StockItemDisplayState extends RefreshableState { double quantity = double.parse(_quantityController.text); _quantityController.clear(); - // Await response to prevent the button from being pressed multiple times - var response = await item.addStock(quantity, notes: _notesController.text); + final bool result = await item.addStock(context, quantity, notes: _notesController.text); _notesController.clear(); - _stockUpdateMessage(response); + _stockUpdateMessage(result); - // TODO - Handle error cases refresh(); } @@ -111,20 +109,27 @@ class _StockItemDisplayState extends RefreshableState { ); } + void _stockUpdateMessage(bool result) { + + showSnackIcon( + refreshableKey, + result ? "Stock item updated" : "Stock item updated failed", + success: result + ); + } + void _removeStock() async { Navigator.of(context).pop(); double quantity = double.parse(_quantityController.text); _quantityController.clear(); - var response = await item.removeStock(quantity, notes: _notesController.text); - _notesController.clear(); + final bool result = await item.removeStock(context, quantity, notes: _notesController.text); - // TODO - Handle error cases + _stockUpdateMessage(result); refresh(); - // TODO - Display a snackbar here indicating the action was successful (or otherwise) } void _removeStockDialog() { @@ -166,19 +171,16 @@ class _StockItemDisplayState extends RefreshableState { double quantity = double.parse(_quantityController.text); _quantityController.clear(); - var response = await item.countStock(quantity, notes: _notesController.text); - _notesController.clear(); + final bool result = await item.countStock(context, quantity, notes: _notesController.text); - // TODO - Handle error cases, timeout, etc + _stockUpdateMessage(result); refresh(); - - // TODO - Display a snackbar here indicating the action was successful (or otherwise) } void _countStockDialog() async { - _quantityController.text = item.quantity.toString(); + _quantityController.text = item.quantityString; _notesController.clear(); showFormDialog(context, I18N.of(context).countStock, @@ -194,7 +196,7 @@ class _StockItemDisplayState extends RefreshableState { fields: [ QuantityField( label: I18N.of(context).countStock, - hint: "${item.quantity}", + hint: "${item.quantityString}", controller: _quantityController, ), TextFormField( @@ -233,7 +235,7 @@ class _StockItemDisplayState extends RefreshableState { InvenTreeStockLocation selectedLocation; - _quantityController.text = "${item.quantity}"; + _quantityController.text = "${item.quantityString}"; showFormDialog(context, I18N.of(context).transferStock, key: _moveStockKey, @@ -385,7 +387,7 @@ class _StockItemDisplayState extends RefreshableState { ListTile( title: Text(I18N.of(context).quantity), leading: FaIcon(FontAwesomeIcons.cubes), - trailing: Text("${item.quantity}"), + trailing: Text("${item.quantityString}"), ) ); } From a62b038faf0bf904c258c3176171e27b4c4f183e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 11 Feb 2021 00:02:00 +1100 Subject: [PATCH 3/6] Edit part category --- lib/widget/category_display.dart | 53 +++++++++++++++++++++++++++++++- lib/widget/snacks.dart | 3 ++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/lib/widget/category_display.dart b/lib/widget/category_display.dart index 2a6c6722..45db9435 100644 --- a/lib/widget/category_display.dart +++ b/lib/widget/category_display.dart @@ -5,6 +5,9 @@ import 'package:InvenTree/preferences.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:InvenTree/widget/fields.dart'; +import 'package:InvenTree/widget/dialogs.dart'; +import 'package:InvenTree/widget/snacks.dart'; import 'package:InvenTree/widget/part_detail.dart'; import 'package:InvenTree/widget/drawer.dart'; import 'package:InvenTree/widget/refreshable_state.dart'; @@ -28,6 +31,8 @@ class CategoryDisplayWidget extends StatefulWidget { class _CategoryDisplayState extends RefreshableState { + final _editCategoryKey = GlobalKey(); + @override String getAppBarTitle(BuildContext context) => I18N.of(context).partCategory; @@ -37,11 +42,54 @@ class _CategoryDisplayState extends RefreshableState { IconButton( icon: FaIcon(FontAwesomeIcons.edit), tooltip: I18N.of(context).edit, - onPressed: null, + onPressed: _editCategoryDialog, ) ]; } + void _editCategory(Map values) async { + + final bool result = await category.update(context, values: values); + + showSnackIcon( + refreshableKey, + result ? "Category edited" : "Category editing failed", + success: result + ); + + refresh(); + } + + void _editCategoryDialog() { + + var _name; + var _description; + + showFormDialog( + context, + I18N.of(context).editCategory, + key: _editCategoryKey, + callback: () { + _editCategory({ + "name": _name, + "description": _description + }); + }, + fields: [ + StringField( + label: I18N.of(context).name, + initial: category.name, + onSaved: (value) => _name = value + ), + StringField( + label: I18N.of(context).description, + initial: category.description, + onSaved: (value) => _description = value + ) + ] + ); + } + _CategoryDisplayState(this.category) {} // The local InvenTreePartCategory object @@ -61,6 +109,9 @@ class _CategoryDisplayState extends RefreshableState { int pk = category?.pk ?? -1; + // Update the category + await category.reload(context); + // Request a list of sub-categories under this one await InvenTreePartCategory().list(context, filters: {"parent": "$pk"}).then((var cats) { _subcategories.clear(); diff --git a/lib/widget/snacks.dart b/lib/widget/snacks.dart index 9b2f3cad..b6aeda23 100644 --- a/lib/widget/snacks.dart +++ b/lib/widget/snacks.dart @@ -14,6 +14,9 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; void showSnackIcon(GlobalKey key, String text, {IconData icon, bool success}) { + // Hide the current snackbar + key.currentState.hideCurrentSnackBar(); + // If icon not specified, use the success status if (icon == null) { icon = (success == true) ? FontAwesomeIcons.checkCircle : FontAwesomeIcons.timesCircle; From 4339f70d64ee8a044e106e962bbc8a35fdbcd053 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 11 Feb 2021 00:11:47 +1100 Subject: [PATCH 4/6] null fix --- lib/widget/category_display.dart | 6 ++++-- lib/widget/location_display.dart | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/widget/category_display.dart b/lib/widget/category_display.dart index 45db9435..64b8da88 100644 --- a/lib/widget/category_display.dart +++ b/lib/widget/category_display.dart @@ -110,7 +110,9 @@ class _CategoryDisplayState extends RefreshableState { int pk = category?.pk ?? -1; // Update the category - await category.reload(context); + if (category != null) { + await category.reload(context); + } // Request a list of sub-categories under this one await InvenTreePartCategory().list(context, filters: {"parent": "$pk"}).then((var cats) { @@ -317,7 +319,7 @@ class PartList extends StatelessWidget { } return ListTile( - title: Text("${part.name}"), + title: Text(part.fullname), subtitle: Text("${part.description}"), trailing: Text("${part.inStockString}"), leading: InvenTreeAPI().getImage( diff --git a/lib/widget/location_display.dart b/lib/widget/location_display.dart index 8d6b7b7c..ebaef9b5 100644 --- a/lib/widget/location_display.dart +++ b/lib/widget/location_display.dart @@ -115,7 +115,9 @@ class _LocationDisplayState extends RefreshableState { int pk = location?.pk ?? -1; // Reload location information - await location.reload(context); + if (location != null) { + await location.reload(context); + } // Request a list of sub-locations under this one await InvenTreeStockLocation().list(context, filters: {"parent": "$pk"}).then((var locs) { From e1a8ecc5f01952adebd7c6111d109a942e2320e2 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 11 Feb 2021 00:11:58 +1100 Subject: [PATCH 5/6] Refactor upload of test result --- lib/widget/part_stock_detail.dart | 2 +- lib/widget/stock_item_test_results.dart | 48 ++++++++----------------- 2 files changed, 16 insertions(+), 34 deletions(-) diff --git a/lib/widget/part_stock_detail.dart b/lib/widget/part_stock_detail.dart index 521d7025..4d39f58a 100644 --- a/lib/widget/part_stock_detail.dart +++ b/lib/widget/part_stock_detail.dart @@ -58,7 +58,7 @@ class _PartStockDisplayState extends RefreshableState { title: Text(part.fullname), subtitle: Text(part.description), leading: InvenTreeAPI().getImage(part.thumbnail), - trailing: Text('${part.inStock}'), + trailing: Text(part.inStockString), ) ), PartStockList(part.stockItems), diff --git a/lib/widget/stock_item_test_results.dart b/lib/widget/stock_item_test_results.dart index 1ff75209..c903a2ce 100644 --- a/lib/widget/stock_item_test_results.dart +++ b/lib/widget/stock_item_test_results.dart @@ -4,6 +4,7 @@ import 'package:InvenTree/inventree/model.dart'; import 'package:InvenTree/api.dart'; import 'package:InvenTree/widget/dialogs.dart'; import 'package:InvenTree/widget/fields.dart'; +import 'package:InvenTree/widget/snacks.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -46,24 +47,20 @@ class _StockItemTestResultDisplayState extends RefreshableState[ - FlatButton( - child: Text(I18N.of(context).cancel), - onPressed: () { - Navigator.pop(context); - }, - ), - FlatButton( - child: Text(I18N.of(context).save), - onPressed: () { - if (_addResultKey.currentState.validate()) { - _addResultKey.currentState.save(); - Navigator.pop(context); - uploadTestResult(_name, _result, _value, _notes, _attachment); - } - }, - ) - ], + callback: () { + uploadTestResult(_name, _result, _value, _notes, _attachment); + }, fields: [ StringField( label: "Test Name", From 1e4fbc36979003795b2c457a98436680226bfd27 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 11 Feb 2021 00:25:41 +1100 Subject: [PATCH 6/6] Default to checkCircle if icon not specified --- lib/widget/snacks.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/widget/snacks.dart b/lib/widget/snacks.dart index b6aeda23..c01e3363 100644 --- a/lib/widget/snacks.dart +++ b/lib/widget/snacks.dart @@ -19,7 +19,7 @@ void showSnackIcon(GlobalKey key, String text, {IconData icon, bo // If icon not specified, use the success status if (icon == null) { - icon = (success == true) ? FontAwesomeIcons.checkCircle : FontAwesomeIcons.timesCircle; + icon = (success == false) ? FontAwesomeIcons.timesCircle : FontAwesomeIcons.checkCircle; } key.currentState.showSnackBar(