mirror of
https://github.com/inventree/inventree-app.git
synced 2025-06-12 18:25:26 +00:00
Many many changes for null-safety support
This commit is contained in:
@ -23,9 +23,9 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
|
||||
class CategoryDisplayWidget extends StatefulWidget {
|
||||
|
||||
CategoryDisplayWidget(this.category, {Key key}) : super(key: key);
|
||||
CategoryDisplayWidget(this.category, {Key? key}) : super(key: key);
|
||||
|
||||
final InvenTreePartCategory category;
|
||||
final InvenTreePartCategory? category;
|
||||
|
||||
@override
|
||||
_CategoryDisplayState createState() => _CategoryDisplayState(category);
|
||||
@ -81,7 +81,7 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
|
||||
void _editCategory(Map<String, String> values) async {
|
||||
|
||||
final bool result = await category.update(context, values: values);
|
||||
final bool result = await category!.update(values: values);
|
||||
|
||||
showSnackIcon(
|
||||
result ? "Category edited" : "Category editing failed",
|
||||
@ -93,6 +93,11 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
|
||||
void _editCategoryDialog() {
|
||||
|
||||
// Cannot edit top-level category
|
||||
if (category == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var _name;
|
||||
var _description;
|
||||
|
||||
@ -108,12 +113,12 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
fields: <Widget>[
|
||||
StringField(
|
||||
label: L10().name,
|
||||
initial: category.name,
|
||||
initial: category?.name,
|
||||
onSaved: (value) => _name = value
|
||||
),
|
||||
StringField(
|
||||
label: L10().description,
|
||||
initial: category.description,
|
||||
initial: category?.description,
|
||||
onSaved: (value) => _description = value
|
||||
)
|
||||
]
|
||||
@ -123,9 +128,9 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
_CategoryDisplayState(this.category) {}
|
||||
|
||||
// The local InvenTreePartCategory object
|
||||
final InvenTreePartCategory category;
|
||||
final InvenTreePartCategory? category;
|
||||
|
||||
List<InvenTreePartCategory> _subcategories = List<InvenTreePartCategory>();
|
||||
List<InvenTreePartCategory> _subcategories = List<InvenTreePartCategory>.empty();
|
||||
|
||||
@override
|
||||
Future<void> onBuild(BuildContext context) async {
|
||||
@ -133,17 +138,17 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
Future<void> request() async {
|
||||
|
||||
int pk = category?.pk ?? -1;
|
||||
|
||||
// Update the category
|
||||
if (category != null) {
|
||||
await category.reload(context);
|
||||
await category!.reload();
|
||||
}
|
||||
|
||||
// Request a list of sub-categories under this one
|
||||
await InvenTreePartCategory().list(context, filters: {"parent": "$pk"}).then((var cats) {
|
||||
await InvenTreePartCategory().list(filters: {"parent": "$pk"}).then((var cats) {
|
||||
_subcategories.clear();
|
||||
|
||||
for (var cat in cats) {
|
||||
@ -168,10 +173,10 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
|
||||
List<Widget> children = [
|
||||
ListTile(
|
||||
title: Text("${category.name}",
|
||||
title: Text("${category?.name}",
|
||||
style: TextStyle(fontWeight: FontWeight.bold)
|
||||
),
|
||||
subtitle: Text("${category.description}"),
|
||||
subtitle: Text("${category?.description}"),
|
||||
),
|
||||
];
|
||||
|
||||
@ -179,14 +184,14 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
children.add(
|
||||
ListTile(
|
||||
title: Text(L10().parentCategory),
|
||||
subtitle: Text("${category.parentpathstring}"),
|
||||
subtitle: Text("${category?.parentpathstring}"),
|
||||
leading: FaIcon(FontAwesomeIcons.levelUpAlt),
|
||||
onTap: () {
|
||||
if (category.parentId < 0) {
|
||||
if (category == null || ((category?.parentId ?? 0) < 0)) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)));
|
||||
} else {
|
||||
// TODO - Refactor this code into the InvenTreePart class
|
||||
InvenTreePartCategory().get(context, category.parentId).then((var cat) {
|
||||
InvenTreePartCategory().get(category?.parentId ?? -1).then((var cat) {
|
||||
if (cat is InvenTreePartCategory) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(cat)));
|
||||
}
|
||||
@ -290,6 +295,8 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
return ListView(
|
||||
children: actionTiles()
|
||||
);
|
||||
default:
|
||||
return ListView();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -306,7 +313,7 @@ class SubcategoryList extends StatelessWidget {
|
||||
void _openCategory(BuildContext context, int pk) {
|
||||
|
||||
// Attempt to load the sub-category.
|
||||
InvenTreePartCategory().get(context, pk).then((var cat) {
|
||||
InvenTreePartCategory().get(pk).then((var cat) {
|
||||
if (cat is InvenTreePartCategory) {
|
||||
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(cat)));
|
||||
@ -348,7 +355,7 @@ class PaginatedPartList extends StatefulWidget {
|
||||
|
||||
final Map<String, String> filters;
|
||||
|
||||
Function onTotalChanged;
|
||||
Function(int)? onTotalChanged;
|
||||
|
||||
PaginatedPartList(this.filters, {this.onTotalChanged});
|
||||
|
||||
@ -363,7 +370,7 @@ class _PaginatedPartListState extends State<PaginatedPartList> {
|
||||
|
||||
String _searchTerm = "";
|
||||
|
||||
Function onTotalChanged;
|
||||
Function(int)? onTotalChanged;
|
||||
|
||||
final Map<String, String> filters;
|
||||
|
||||
@ -393,21 +400,21 @@ class _PaginatedPartListState extends State<PaginatedPartList> {
|
||||
|
||||
Map<String, String> params = filters;
|
||||
|
||||
params["search"] = _searchTerm ?? "";
|
||||
params["search"] = _searchTerm;
|
||||
|
||||
final bool cascade = await InvenTreeSettingsManager().getValue("partSubcategory", false);
|
||||
params["cascade"] = "${cascade}";
|
||||
|
||||
final page = await InvenTreePart().listPaginated(_pageSize, pageKey, filters: params);
|
||||
int pageLength = page.length ?? 0;
|
||||
int pageCount = page.count ?? 0;
|
||||
int pageLength = page?.length ?? 0;
|
||||
int pageCount = page?.count ?? 0;
|
||||
|
||||
final isLastPage = pageLength < _pageSize;
|
||||
|
||||
// Construct a list of part objects
|
||||
List<InvenTreePart> parts = [];
|
||||
|
||||
if (page == null) {
|
||||
if (page != null) {
|
||||
for (var result in page.results) {
|
||||
if (result is InvenTreePart) {
|
||||
parts.add(result);
|
||||
@ -423,7 +430,7 @@ class _PaginatedPartListState extends State<PaginatedPartList> {
|
||||
}
|
||||
|
||||
if (onTotalChanged != null) {
|
||||
onTotalChanged(pageCount);
|
||||
onTotalChanged!(pageCount);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
@ -438,7 +445,7 @@ class _PaginatedPartListState extends State<PaginatedPartList> {
|
||||
|
||||
void _openPart(BuildContext context, int pk) {
|
||||
// Attempt to load the part information
|
||||
InvenTreePart().get(context, pk).then((var part) {
|
||||
InvenTreePart().get(pk).then((var part) {
|
||||
if (part is InvenTreePart) {
|
||||
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
|
@ -13,7 +13,7 @@ class CompanyDetailWidget extends StatefulWidget {
|
||||
|
||||
final InvenTreeCompany company;
|
||||
|
||||
CompanyDetailWidget(this.company, {Key key}) : super(key: key);
|
||||
CompanyDetailWidget(this.company, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_CompanyDetailState createState() => _CompanyDetailState(company);
|
||||
@ -31,8 +31,8 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
||||
String getAppBarTitle(BuildContext context) => L10().company;
|
||||
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
await company.reload(context);
|
||||
Future<void> request() async {
|
||||
await company.reload();
|
||||
}
|
||||
|
||||
_CompanyDetailState(this.company) {
|
||||
@ -42,7 +42,7 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
||||
void _saveCompany(Map<String, String> values) async {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
var response = await company.update(context, values: values);
|
||||
var response = await company.update(values: values);
|
||||
|
||||
refresh();
|
||||
}
|
||||
@ -66,8 +66,8 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
||||
FlatButton(
|
||||
child: Text(L10().save),
|
||||
onPressed: () {
|
||||
if (_editCompanyKey.currentState.validate()) {
|
||||
_editCompanyKey.currentState.save();
|
||||
if (_editCompanyKey.currentState!.validate()) {
|
||||
_editCompanyKey.currentState!.save();
|
||||
|
||||
_saveCompany({
|
||||
"name": _name,
|
||||
@ -107,7 +107,7 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
||||
|
||||
List<Widget> _companyTiles() {
|
||||
|
||||
var tiles = List<Widget>();
|
||||
var tiles = List<Widget>.empty();
|
||||
|
||||
bool sep = false;
|
||||
|
||||
|
@ -11,8 +11,8 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
abstract class CompanyListWidget extends StatefulWidget {
|
||||
|
||||
String title;
|
||||
Map<String, String> filters;
|
||||
String title = "";
|
||||
Map<String, String> filters = {};
|
||||
|
||||
@override
|
||||
_CompanyListState createState() => _CompanyListState(title, filters);
|
||||
@ -39,9 +39,9 @@ class CustomerListWidget extends CompanyListWidget {
|
||||
|
||||
class _CompanyListState extends RefreshableState<CompanyListWidget> {
|
||||
|
||||
var _companies = new List<InvenTreeCompany>();
|
||||
var _companies = new List<InvenTreeCompany>.empty();
|
||||
|
||||
var _filteredCompanies = new List<InvenTreeCompany>();
|
||||
var _filteredCompanies = new List<InvenTreeCompany>.empty();
|
||||
|
||||
String _title = "Companies";
|
||||
|
||||
@ -58,9 +58,9 @@ class _CompanyListState extends RefreshableState<CompanyListWidget> {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
Future<void> request() async {
|
||||
|
||||
await InvenTreeCompany().list(context, filters: _filters).then((var companies) {
|
||||
await InvenTreeCompany().list(filters: _filters).then((var companies) {
|
||||
|
||||
_companies.clear();
|
||||
|
||||
@ -96,8 +96,10 @@ class _CompanyListState extends RefreshableState<CompanyListWidget> {
|
||||
leading: InvenTreeAPI().getImage(company.image),
|
||||
onTap: () {
|
||||
if (company.pk > 0) {
|
||||
InvenTreeCompany().get(context, company.pk).then((var c) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyDetailWidget(c)));
|
||||
InvenTreeCompany().get(company.pk).then((var c) {
|
||||
if (c != null && c is InvenTreeCompany) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyDetailWidget(c)));
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -8,15 +8,10 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:InvenTree/l10.dart';
|
||||
import 'package:one_context/one_context.dart';
|
||||
|
||||
Future<void> confirmationDialog(String title, String text, {String acceptText, String rejectText, Function onAccept, Function onReject}) async {
|
||||
Future<void> confirmationDialog(String title, String text, {String? acceptText, String? rejectText, Function? onAccept, Function? onReject}) async {
|
||||
|
||||
if (acceptText == null || acceptText.isEmpty) {
|
||||
acceptText = L10().ok;
|
||||
}
|
||||
|
||||
if (rejectText == null || rejectText.isEmpty) {
|
||||
rejectText = L10().cancel;
|
||||
}
|
||||
String _accept = acceptText ?? L10().ok;
|
||||
String _reject = rejectText ?? L10().cancel;
|
||||
|
||||
OneContext().showDialog(
|
||||
builder: (BuildContext context) {
|
||||
@ -28,7 +23,7 @@ Future<void> confirmationDialog(String title, String text, {String acceptText, S
|
||||
content: Text(text),
|
||||
actions: [
|
||||
FlatButton(
|
||||
child: Text(rejectText),
|
||||
child: Text(_reject),
|
||||
onPressed: () {
|
||||
// Close this dialog
|
||||
Navigator.pop(context);
|
||||
@ -39,7 +34,7 @@ Future<void> confirmationDialog(String title, String text, {String acceptText, S
|
||||
}
|
||||
),
|
||||
FlatButton(
|
||||
child: Text(acceptText),
|
||||
child: Text(_accept),
|
||||
onPressed: () {
|
||||
// Close this dialog
|
||||
Navigator.pop(context);
|
||||
@ -56,17 +51,14 @@ Future<void> confirmationDialog(String title, String text, {String acceptText, S
|
||||
}
|
||||
|
||||
|
||||
Future<void> showInfoDialog(BuildContext context, String title, String description, {IconData icon = FontAwesomeIcons.info, String info, Function onDismissed}) async {
|
||||
Future<void> showInfoDialog(String title, String description, {IconData icon = FontAwesomeIcons.info, String? info, Function()? onDismissed}) async {
|
||||
|
||||
if (info == null || info.isEmpty) {
|
||||
info = L10().info;
|
||||
}
|
||||
String _info = info ?? L10().info;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
OneContext().showDialog(
|
||||
builder: (BuildContext context) => SimpleDialog(
|
||||
title: ListTile(
|
||||
title: Text(info),
|
||||
title: Text(_info),
|
||||
leading: FaIcon(icon),
|
||||
),
|
||||
children: <Widget>[
|
||||
@ -83,16 +75,14 @@ Future<void> showInfoDialog(BuildContext context, String title, String descripti
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> showErrorDialog(String title, String description, {IconData icon = FontAwesomeIcons.exclamationCircle, String error, Function onDismissed}) async {
|
||||
Future<void> showErrorDialog(String title, String description, {IconData icon = FontAwesomeIcons.exclamationCircle, String? error, Function? onDismissed}) async {
|
||||
|
||||
if (error == null || error.isEmpty) {
|
||||
error = L10().error;
|
||||
}
|
||||
String _error = error ?? L10().error;
|
||||
|
||||
OneContext().showDialog(
|
||||
builder: (context) => SimpleDialog(
|
||||
title: ListTile(
|
||||
title: Text(error),
|
||||
title: Text(_error),
|
||||
leading: FaIcon(icon),
|
||||
),
|
||||
children: [
|
||||
@ -111,7 +101,7 @@ Future<void> showErrorDialog(String title, String description, {IconData icon =
|
||||
|
||||
Future<void> showServerError(String title, String description) async {
|
||||
|
||||
if (title == null || title.isEmpty) {
|
||||
if (title.isEmpty) {
|
||||
title = L10().serverError;
|
||||
}
|
||||
|
||||
@ -140,8 +130,6 @@ Future<void> showServerError(String title, String description) async {
|
||||
|
||||
Future<void> showStatusCodeError(int status, {int expected = 200}) async {
|
||||
|
||||
BuildContext ctx = OneContext().context;
|
||||
|
||||
String msg = L10().responseInvalid;
|
||||
String extra = "Server responded with status code ${status}";
|
||||
|
||||
@ -174,7 +162,7 @@ Future<void> showStatusCodeError(int status, {int expected = 200}) async {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showTimeoutError(BuildContext context) async {
|
||||
Future<void> showTimeoutError() async {
|
||||
|
||||
// Use OneContext as "sometimes" context is null here?
|
||||
var ctx = OneContext().context;
|
||||
@ -182,42 +170,49 @@ Future<void> showTimeoutError(BuildContext context) async {
|
||||
await showServerError(L10().timeout, L10().noResponse);
|
||||
}
|
||||
|
||||
void showFormDialog(String title, {String acceptText, String cancelText, GlobalKey<FormState> key, List<Widget> fields, List<Widget> actions, Function callback}) {
|
||||
void showFormDialog(String title, {String? acceptText, String? cancelText, GlobalKey<FormState>? key, List<Widget>? fields, List<Widget>? actions, Function? callback}) {
|
||||
|
||||
BuildContext dialogContext;
|
||||
BuildContext? dialogContext;
|
||||
|
||||
var ctx = OneContext().context;
|
||||
|
||||
if (acceptText == null) {
|
||||
acceptText = L10().save;
|
||||
}
|
||||
|
||||
if (cancelText == null) {
|
||||
cancelText = L10().cancel;
|
||||
}
|
||||
String _accept = acceptText ?? L10().save;
|
||||
String _cancel = cancelText ?? L10().cancel;
|
||||
|
||||
// Undefined actions = OK + Cancel
|
||||
if (actions == null) {
|
||||
actions = <Widget>[
|
||||
FlatButton(
|
||||
child: Text(cancelText),
|
||||
child: Text(_cancel),
|
||||
onPressed: () {
|
||||
// Close the form
|
||||
Navigator.pop(dialogContext);
|
||||
var _ctx = dialogContext;
|
||||
if (_ctx != null) {
|
||||
Navigator.pop(_ctx);
|
||||
}
|
||||
}
|
||||
),
|
||||
FlatButton(
|
||||
child: Text(acceptText),
|
||||
child: Text(_accept),
|
||||
onPressed: () {
|
||||
if (key.currentState.validate()) {
|
||||
key.currentState.save();
|
||||
|
||||
// Close the dialog
|
||||
Navigator.pop(dialogContext);
|
||||
var _key = key;
|
||||
|
||||
// Callback
|
||||
if (callback != null) {
|
||||
callback();
|
||||
if (_key != null && _key.currentState != null) {
|
||||
if (_key.currentState!.validate()) {
|
||||
_key.currentState!.save();
|
||||
|
||||
// Close the dialog
|
||||
var _ctx = dialogContext;
|
||||
|
||||
if (_ctx != null) {
|
||||
Navigator.pop(_ctx);
|
||||
}
|
||||
|
||||
// Callback
|
||||
if (callback != null) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -225,6 +220,8 @@ void showFormDialog(String title, {String acceptText, String cancelText, GlobalK
|
||||
];
|
||||
}
|
||||
|
||||
var _fields = fields ?? List<Widget>.empty();
|
||||
|
||||
OneContext().showDialog(
|
||||
builder: (BuildContext context) {
|
||||
dialogContext = context;
|
||||
@ -238,7 +235,7 @@ void showFormDialog(String title, {String acceptText, String cancelText, GlobalK
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: fields
|
||||
children: _fields
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -52,10 +52,10 @@ class ImagePickerField extends FormField<File> {
|
||||
|
||||
}
|
||||
|
||||
ImagePickerField(BuildContext context, {String label = "Attach Image", Function onSaved, bool required = false}) :
|
||||
ImagePickerField(BuildContext context, {String? label, Function(File?)? onSaved, bool required = false}) :
|
||||
super(
|
||||
onSaved: onSaved,
|
||||
validator: (File img) {
|
||||
validator: (File? img) {
|
||||
if (required && (img == null)) {
|
||||
return L10().required;
|
||||
}
|
||||
@ -63,10 +63,13 @@ class ImagePickerField extends FormField<File> {
|
||||
return null;
|
||||
},
|
||||
builder: (FormFieldState<File> state) {
|
||||
|
||||
String _label = label ?? L10().attachImage;
|
||||
|
||||
return InputDecorator(
|
||||
decoration: InputDecoration(
|
||||
errorText: state.errorText,
|
||||
labelText: required ? label + "*" : label,
|
||||
labelText: required ? _label + "*" : _label,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
@ -92,7 +95,7 @@ class ImagePickerField extends FormField<File> {
|
||||
|
||||
|
||||
class CheckBoxField extends FormField<bool> {
|
||||
CheckBoxField({String label, String hint, bool initial = false, Function onSaved}) :
|
||||
CheckBoxField({String? label, String? hint, bool initial = false, Function(bool?)? onSaved}) :
|
||||
super(
|
||||
onSaved: onSaved,
|
||||
initialValue: initial,
|
||||
@ -111,7 +114,7 @@ class CheckBoxField extends FormField<bool> {
|
||||
|
||||
class StringField extends TextFormField {
|
||||
|
||||
StringField({String label, String hint, String initial, Function onSaved, Function validator, bool allowEmpty = false, bool isEnabled = true}) :
|
||||
StringField({String label = "", String? hint, String? initial, Function(String?)? onSaved, Function? validator, bool allowEmpty = false, bool isEnabled = true}) :
|
||||
super(
|
||||
decoration: InputDecoration(
|
||||
labelText: allowEmpty ? label : label + "*",
|
||||
@ -121,7 +124,7 @@ class StringField extends TextFormField {
|
||||
onSaved: onSaved,
|
||||
enabled: isEnabled,
|
||||
validator: (value) {
|
||||
if (!allowEmpty && value.isEmpty) {
|
||||
if (!allowEmpty && value != null && value.isEmpty) {
|
||||
return L10().valueCannotBeEmpty;
|
||||
}
|
||||
|
||||
@ -140,7 +143,7 @@ class StringField extends TextFormField {
|
||||
*/
|
||||
class QuantityField extends TextFormField {
|
||||
|
||||
QuantityField({String label = "", String hint = "", String initial = "", double max = null, TextEditingController controller}) :
|
||||
QuantityField({String label = "", String hint = "", String initial = "", double? max, TextEditingController? controller}) :
|
||||
super(
|
||||
decoration: InputDecoration(
|
||||
labelText: label,
|
||||
@ -150,9 +153,9 @@ class QuantityField extends TextFormField {
|
||||
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
|
||||
validator: (value) {
|
||||
|
||||
if (value.isEmpty) return L10().quantityEmpty;
|
||||
if (value != null && value.isEmpty) return L10().quantityEmpty;
|
||||
|
||||
double quantity = double.tryParse(value);
|
||||
double quantity = double.tryParse(value ?? '0') ?? 0;
|
||||
|
||||
if (quantity == null) return L10().quantityInvalid;
|
||||
if (quantity <= 0) return L10().quantityPositive;
|
||||
|
@ -20,7 +20,7 @@ import 'package:InvenTree/widget/spinner.dart';
|
||||
import 'package:InvenTree/widget/drawer.dart';
|
||||
|
||||
class InvenTreeHomePage extends StatefulWidget {
|
||||
InvenTreeHomePage({Key key}) : super(key: key);
|
||||
InvenTreeHomePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_InvenTreeHomePageState createState() => _InvenTreeHomePageState();
|
||||
@ -37,9 +37,7 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
||||
}
|
||||
|
||||
// Selected user profile
|
||||
UserProfile _profile;
|
||||
|
||||
BuildContext _context;
|
||||
UserProfile? _profile;
|
||||
|
||||
void _searchParts() {
|
||||
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||
@ -113,7 +111,7 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
||||
if (!InvenTreeAPI().isConnected() && !InvenTreeAPI().isConnecting()) {
|
||||
|
||||
// Attempt server connection
|
||||
InvenTreeAPI().connectToServer(_homeKey.currentContext).then((result) {
|
||||
InvenTreeAPI().connectToServer().then((result) {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
@ -171,7 +169,7 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
||||
} else {
|
||||
return ListTile(
|
||||
title: Text(L10().serverCouldNotConnect),
|
||||
subtitle: Text("${_profile.server}"),
|
||||
subtitle: Text("${_profile!.server}"),
|
||||
leading: FaIcon(FontAwesomeIcons.server),
|
||||
trailing: FaIcon(
|
||||
FontAwesomeIcons.timesCircle,
|
||||
@ -187,8 +185,6 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
_context = context;
|
||||
|
||||
// This method is rerun every time setState is called, for instance as done
|
||||
// by the _incrementCounter method above.
|
||||
//
|
||||
|
@ -20,11 +20,11 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
|
||||
class LocationDisplayWidget extends StatefulWidget {
|
||||
|
||||
LocationDisplayWidget(this.location, {Key key}) : super(key: key);
|
||||
LocationDisplayWidget(this.location, {Key? key}) : super(key: key);
|
||||
|
||||
final InvenTreeStockLocation location;
|
||||
final InvenTreeStockLocation? location;
|
||||
|
||||
final String title = "Location";
|
||||
final String title = L10().stockLocation;
|
||||
|
||||
@override
|
||||
_LocationDisplayState createState() => _LocationDisplayState(location);
|
||||
@ -32,12 +32,12 @@ class LocationDisplayWidget extends StatefulWidget {
|
||||
|
||||
class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
|
||||
final InvenTreeStockLocation location;
|
||||
final InvenTreeStockLocation? location;
|
||||
|
||||
final _editLocationKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
String getAppBarTitle(BuildContext context) { return "Stock Location"; }
|
||||
String getAppBarTitle(BuildContext context) { return L10().stockLocation; }
|
||||
|
||||
@override
|
||||
List<Widget> getAppBarActions(BuildContext context) {
|
||||
@ -80,12 +80,16 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
|
||||
void _editLocation(Map<String, String> values) async {
|
||||
|
||||
final bool result = await location.update(context, values: values);
|
||||
bool result = false;
|
||||
|
||||
showSnackIcon(
|
||||
result ? "Location edited" : "Location editing failed",
|
||||
success: result
|
||||
);
|
||||
if (location != null) {
|
||||
result = await location!.update(values: values);
|
||||
|
||||
showSnackIcon(
|
||||
result ? "Location edited" : "Location editing failed",
|
||||
success: result
|
||||
);
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
@ -95,6 +99,10 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
var _name;
|
||||
var _description;
|
||||
|
||||
if (location == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
showFormDialog(L10().editLocation,
|
||||
key: _editLocationKey,
|
||||
callback: () {
|
||||
@ -106,12 +114,12 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
fields: <Widget> [
|
||||
StringField(
|
||||
label: L10().name,
|
||||
initial: location.name,
|
||||
initial: location?.name ?? '',
|
||||
onSaved: (value) => _name = value,
|
||||
),
|
||||
StringField(
|
||||
label: L10().description,
|
||||
initial: location.description,
|
||||
initial: location?.description ?? '',
|
||||
onSaved: (value) => _description = value,
|
||||
)
|
||||
]
|
||||
@ -120,7 +128,7 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
|
||||
_LocationDisplayState(this.location) {}
|
||||
|
||||
List<InvenTreeStockLocation> _sublocations = List<InvenTreeStockLocation>();
|
||||
List<InvenTreeStockLocation> _sublocations = List<InvenTreeStockLocation>.empty();
|
||||
|
||||
String _locationFilter = '';
|
||||
|
||||
@ -139,17 +147,17 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
Future<void> request() async {
|
||||
|
||||
int pk = location?.pk ?? -1;
|
||||
|
||||
// Reload location information
|
||||
if (location != null) {
|
||||
await location.reload(context);
|
||||
await location?.reload();
|
||||
}
|
||||
|
||||
// Request a list of sub-locations under this one
|
||||
await InvenTreeStockLocation().list(context, filters: {"parent": "$pk"}).then((var locs) {
|
||||
await InvenTreeStockLocation().list(filters: {"parent": "$pk"}).then((var locs) {
|
||||
_sublocations.clear();
|
||||
|
||||
for (var loc in locs) {
|
||||
@ -173,8 +181,8 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
|
||||
List<Widget> children = [
|
||||
ListTile(
|
||||
title: Text("${location.name}"),
|
||||
subtitle: Text("${location.description}"),
|
||||
title: Text("${location!.name}"),
|
||||
subtitle: Text("${location!.description}"),
|
||||
),
|
||||
];
|
||||
|
||||
@ -182,13 +190,17 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
children.add(
|
||||
ListTile(
|
||||
title: Text(L10().parentCategory),
|
||||
subtitle: Text("${location.parentpathstring}"),
|
||||
subtitle: Text("${location!.parentpathstring}"),
|
||||
leading: FaIcon(FontAwesomeIcons.levelUpAlt),
|
||||
onTap: () {
|
||||
if (location.parentId < 0) {
|
||||
|
||||
int parent = location?.parentId ?? -1;
|
||||
|
||||
if (parent < 0) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)));
|
||||
} else {
|
||||
InvenTreeStockLocation().get(context, location.parentId).then((var loc) {
|
||||
|
||||
InvenTreeStockLocation().get(parent).then((var loc) {
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc)));
|
||||
}
|
||||
@ -238,7 +250,7 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
Map<String, String> filters = {};
|
||||
|
||||
if (location != null) {
|
||||
filters["location"] = "${location.pk}";
|
||||
filters["location"] = "${location!.pk}";
|
||||
}
|
||||
|
||||
switch (index) {
|
||||
@ -256,7 +268,7 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
).toList()
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
return ListView();
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,7 +309,7 @@ List<Widget> detailTiles() {
|
||||
List<Widget> tiles = [];
|
||||
|
||||
tiles.add(locationDescriptionCard(includeActions: false));
|
||||
|
||||
|
||||
if (location != null) {
|
||||
// Stock adjustment actions
|
||||
if (InvenTreeAPI().checkPermission('stock', 'change')) {
|
||||
@ -308,14 +320,19 @@ List<Widget> detailTiles() {
|
||||
leading: FaIcon(FontAwesomeIcons.exchangeAlt),
|
||||
trailing: FaIcon(FontAwesomeIcons.qrcode),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) =>
|
||||
InvenTreeQRView(
|
||||
StockLocationScanInItemsHandler(location)))
|
||||
).then((context) {
|
||||
refresh();
|
||||
});
|
||||
|
||||
var _loc = location;
|
||||
|
||||
if (_loc != null) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) =>
|
||||
InvenTreeQRView(
|
||||
StockLocationScanInItemsHandler(_loc)))
|
||||
).then((context) {
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
);
|
||||
@ -361,7 +378,7 @@ class SublocationList extends StatelessWidget {
|
||||
|
||||
void _openLocation(BuildContext context, int pk) {
|
||||
|
||||
InvenTreeStockLocation().get(context, pk).then((var loc) {
|
||||
InvenTreeStockLocation().get(pk).then((var loc) {
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc)));
|
||||
@ -452,26 +469,32 @@ class _PaginatedStockListState extends State<PaginatedStockList> {
|
||||
params["cascade"] = "${cascade}";
|
||||
|
||||
final page = await InvenTreeStockItem().listPaginated(_pageSize, pageKey, filters: params);
|
||||
final isLastPage = page.length < _pageSize;
|
||||
|
||||
int pageLength = page?.length ?? 0;
|
||||
int pageCount = page?.count ?? 0;
|
||||
|
||||
final isLastPage = pageLength < _pageSize;
|
||||
|
||||
// Construct a list of stock item objects
|
||||
List<InvenTreeStockItem> items = [];
|
||||
|
||||
for (var result in page.results) {
|
||||
if (result is InvenTreeStockItem) {
|
||||
items.add(result);
|
||||
if (page != null) {
|
||||
for (var result in page.results) {
|
||||
if (result is InvenTreeStockItem) {
|
||||
items.add(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isLastPage) {
|
||||
_pagingController.appendLastPage(items);
|
||||
} else {
|
||||
final int nextPageKey = pageKey + page.length;
|
||||
final int nextPageKey = pageKey + pageLength;
|
||||
_pagingController.appendPage(items, nextPageKey);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
resultCount = page.count;
|
||||
resultCount = pageCount;
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
@ -480,7 +503,7 @@ class _PaginatedStockListState extends State<PaginatedStockList> {
|
||||
}
|
||||
|
||||
void _openItem(BuildContext context, int pk) {
|
||||
InvenTreeStockItem().get(context, pk).then((var item) {
|
||||
InvenTreeStockItem().get(pk).then((var item) {
|
||||
if (item is InvenTreeStockItem) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => StockDetailWidget(item)));
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import 'location_display.dart';
|
||||
|
||||
class PartDetailWidget extends StatefulWidget {
|
||||
|
||||
PartDetailWidget(this.part, {Key key}) : super(key: key);
|
||||
PartDetailWidget(this.part, {Key? key}) : super(key: key);
|
||||
|
||||
final InvenTreePart part;
|
||||
|
||||
@ -87,22 +87,22 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
await part.reload(context);
|
||||
await part.getTestTemplates(context);
|
||||
Future<void> request() async {
|
||||
await part.reload();
|
||||
await part.getTestTemplates();
|
||||
}
|
||||
|
||||
void _toggleStar() async {
|
||||
|
||||
if (InvenTreeAPI().checkPermission('part', 'view')) {
|
||||
await part.update(context, values: {"starred": "${!part.starred}"});
|
||||
await part.update(values: {"starred": "${!part.starred}"});
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void _savePart(Map<String, String> values) async {
|
||||
|
||||
final bool result = await part.update(context, values: values);
|
||||
final bool result = await part.update(values: values);
|
||||
|
||||
if (result) {
|
||||
showSnackIcon(L10().partEdited, success: true);
|
||||
@ -121,7 +121,11 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
* Upload image for this Part.
|
||||
* Show a SnackBar with upload result.
|
||||
*/
|
||||
void _uploadImage(File image) async {
|
||||
void _uploadImage(File? image) async {
|
||||
|
||||
if (image == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await part.uploadImage(image);
|
||||
|
||||
@ -143,7 +147,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
|
||||
void _selectImage() {
|
||||
|
||||
File _attachment;
|
||||
File? _attachment;
|
||||
|
||||
if (!InvenTreeAPI().checkPermission('part', 'change')) {
|
||||
return;
|
||||
@ -261,7 +265,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
}
|
||||
|
||||
// Category information
|
||||
if (part.categoryName != null && part.categoryName.isNotEmpty) {
|
||||
if (part.categoryName.isNotEmpty) {
|
||||
tiles.add(
|
||||
ListTile(
|
||||
title: Text(L10().partCategory),
|
||||
@ -269,9 +273,12 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
leading: FaIcon(FontAwesomeIcons.sitemap),
|
||||
onTap: () {
|
||||
if (part.categoryId > 0) {
|
||||
InvenTreePartCategory().get(context, part.categoryId).then((var cat) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => CategoryDisplayWidget(cat)));
|
||||
InvenTreePartCategory().get(part.categoryId).then((var cat) {
|
||||
|
||||
if (cat is InvenTreePartCategory) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => CategoryDisplayWidget(cat)));
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -499,7 +506,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
)
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
return Center();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ class PartNotesWidget extends StatefulWidget {
|
||||
|
||||
final InvenTreePart part;
|
||||
|
||||
PartNotesWidget(this.part, {Key key}) : super(key: key);
|
||||
PartNotesWidget(this.part, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_PartNotesState createState() => _PartNotesState(part);
|
||||
|
@ -11,7 +11,7 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
|
||||
final refreshableKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
// Storage for context once "Build" is called
|
||||
BuildContext context;
|
||||
BuildContext? _context;
|
||||
|
||||
// Current tab index (used for widgets which display bottom tabs)
|
||||
int tabIndex = 0;
|
||||
@ -36,7 +36,7 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
|
||||
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => onBuild(context));
|
||||
WidgetsBinding.instance?.addPostFrameCallback((_) => onBuild(_context!));
|
||||
}
|
||||
|
||||
// Function called after the widget is first build
|
||||
@ -45,7 +45,7 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
|
||||
}
|
||||
|
||||
// Function to request data for this page
|
||||
Future<void> request(BuildContext context) async {
|
||||
Future<void> request() async {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
|
||||
loading = true;
|
||||
});
|
||||
|
||||
await request(context);
|
||||
await request();
|
||||
|
||||
setState(() {
|
||||
loading = false;
|
||||
@ -77,14 +77,16 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
|
||||
|
||||
// Function to construct a body (MUST BE PROVIDED)
|
||||
Widget getBody(BuildContext context) {
|
||||
|
||||
// Default return is an empty ListView
|
||||
return ListView();
|
||||
}
|
||||
|
||||
Widget? getBottomNavBar(BuildContext context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Widget getBottomNavBar(BuildContext context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Widget getFab(BuildContext context) {
|
||||
Widget? getFab(BuildContext context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -92,7 +94,7 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
// Save the context for future use
|
||||
this.context = context;
|
||||
_context = context;
|
||||
|
||||
return Scaffold(
|
||||
key: refreshableKey,
|
||||
|
@ -15,25 +15,21 @@ import '../api.dart';
|
||||
|
||||
// TODO - Refactor duplicate code in this file!
|
||||
|
||||
class PartSearchDelegate extends SearchDelegate<InvenTreePart> {
|
||||
class PartSearchDelegate extends SearchDelegate<InvenTreePart?> {
|
||||
|
||||
final partSearchKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
BuildContext context;
|
||||
|
||||
// What did we search for last time?
|
||||
String _cachedQuery;
|
||||
String _cachedQuery = "";
|
||||
|
||||
bool _searching = false;
|
||||
|
||||
// Custom filters for the part search
|
||||
Map<String, String> filters = {};
|
||||
|
||||
PartSearchDelegate(this.context, {this.filters}) {
|
||||
if (filters == null) {
|
||||
filters = {};
|
||||
}
|
||||
}
|
||||
PartSearchDelegate(this.context, {this.filters = const {}});
|
||||
|
||||
@override
|
||||
String get searchFieldLabel => L10().searchParts;
|
||||
@ -71,7 +67,7 @@ class PartSearchDelegate extends SearchDelegate<InvenTreePart> {
|
||||
|
||||
for (int idx = 0; idx < results.length; idx++) {
|
||||
if (results[idx] is InvenTreePart) {
|
||||
partResults.add(results[idx]);
|
||||
partResults.add(results[idx] as InvenTreePart);
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,7 +128,7 @@ class PartSearchDelegate extends SearchDelegate<InvenTreePart> {
|
||||
),
|
||||
trailing: Text(part.inStockString),
|
||||
onTap: () {
|
||||
InvenTreePart().get(context, part.pk).then((var prt) {
|
||||
InvenTreePart().get(part.pk).then((var prt) {
|
||||
if (prt is InvenTreePart) {
|
||||
Navigator.push(
|
||||
context,
|
||||
@ -201,18 +197,18 @@ class PartSearchDelegate extends SearchDelegate<InvenTreePart> {
|
||||
}
|
||||
|
||||
|
||||
class StockSearchDelegate extends SearchDelegate<InvenTreeStockItem> {
|
||||
class StockSearchDelegate extends SearchDelegate<InvenTreeStockItem?> {
|
||||
|
||||
final stockSearchKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
final BuildContext context;
|
||||
|
||||
String _cachedQuery;
|
||||
String _cachedQuery = "";
|
||||
|
||||
bool _searching = false;
|
||||
|
||||
// Custom filters for the stock item search
|
||||
Map<String, String> filters;
|
||||
Map<String, String>? filters;
|
||||
|
||||
StockSearchDelegate(this.context, {this.filters}) {
|
||||
if (filters == null) {
|
||||
@ -247,7 +243,9 @@ class StockSearchDelegate extends SearchDelegate<InvenTreeStockItem> {
|
||||
showResults(context);
|
||||
|
||||
// Enable cascading part search by default
|
||||
filters["cascade"] = "true";
|
||||
if (filters != null) {
|
||||
filters?["cascade"] = "true";
|
||||
}
|
||||
|
||||
final results = await InvenTreeStockItem().search(
|
||||
context, query, filters: filters);
|
||||
@ -256,7 +254,7 @@ class StockSearchDelegate extends SearchDelegate<InvenTreeStockItem> {
|
||||
|
||||
for (int idx = 0; idx < results.length; idx++) {
|
||||
if (results[idx] is InvenTreeStockItem) {
|
||||
itemResults.add(results[idx]);
|
||||
itemResults.add(results[idx] as InvenTreeStockItem);
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,7 +313,7 @@ class StockSearchDelegate extends SearchDelegate<InvenTreeStockItem> {
|
||||
),
|
||||
trailing: Text(item.serialOrQuantityDisplay()),
|
||||
onTap: () {
|
||||
InvenTreeStockItem().get(context, item.pk).then((var it) {
|
||||
InvenTreeStockItem().get(item.pk).then((var it) {
|
||||
if (it is InvenTreeStockItem) {
|
||||
Navigator.push(
|
||||
context,
|
||||
|
@ -15,14 +15,14 @@ import 'package:one_context/one_context.dart';
|
||||
import 'package:InvenTree/l10.dart';
|
||||
|
||||
|
||||
void showSnackIcon(String text, {IconData icon, Function onAction, bool success, String actionText}) {
|
||||
void showSnackIcon(String text, {IconData? icon, Function()? onAction, bool? success, String? actionText}) {
|
||||
|
||||
OneContext().hideCurrentSnackBar();
|
||||
|
||||
Color backgroundColor;
|
||||
Color backgroundColor = Colors.deepOrange;
|
||||
|
||||
// Make some selections based on the "success" value
|
||||
if (success == true) {
|
||||
if (success != null && success == true) {
|
||||
backgroundColor = Colors.lightGreen;
|
||||
|
||||
// Select an icon if we do not have an action
|
||||
@ -30,26 +30,21 @@ void showSnackIcon(String text, {IconData icon, Function onAction, bool success,
|
||||
icon = FontAwesomeIcons.checkCircle;
|
||||
}
|
||||
|
||||
} else if (success == false) {
|
||||
} else if (success != null && success == false) {
|
||||
backgroundColor = Colors.deepOrange;
|
||||
|
||||
if (icon == null && onAction == null) {
|
||||
icon = FontAwesomeIcons.exclamationCircle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SnackBarAction action;
|
||||
String _action = actionText ?? L10().details;
|
||||
|
||||
SnackBarAction? action;
|
||||
|
||||
if (onAction != null) {
|
||||
|
||||
if (actionText == null) {
|
||||
// Default action text
|
||||
actionText = L10().details;
|
||||
}
|
||||
|
||||
action = SnackBarAction(
|
||||
label: actionText,
|
||||
label: _action,
|
||||
onPressed: onAction,
|
||||
);
|
||||
}
|
||||
|
@ -4,13 +4,13 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
class Spinner extends StatefulWidget {
|
||||
final IconData icon;
|
||||
final IconData? icon;
|
||||
final Duration duration;
|
||||
final Color color;
|
||||
|
||||
const Spinner({
|
||||
this.color = const Color.fromRGBO(150, 150, 150, 1),
|
||||
Key key,
|
||||
Key? key,
|
||||
@required this.icon,
|
||||
this.duration = const Duration(milliseconds: 1800),
|
||||
}) : super(key: key);
|
||||
@ -20,8 +20,8 @@ class Spinner extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SpinnerState extends State<Spinner> with SingleTickerProviderStateMixin {
|
||||
AnimationController _controller;
|
||||
Widget _child;
|
||||
AnimationController? _controller;
|
||||
Widget? _child;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -40,14 +40,14 @@ class _SpinnerState extends State<Spinner> with SingleTickerProviderStateMixin {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
_controller!.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RotationTransition(
|
||||
turns: _controller,
|
||||
turns: _controller!,
|
||||
child: _child,
|
||||
);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import '../api.dart';
|
||||
|
||||
class StarredPartWidget extends StatefulWidget {
|
||||
|
||||
StarredPartWidget({Key key}) : super(key: key);
|
||||
StarredPartWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_StarredPartState createState() => _StarredPartState();
|
||||
@ -29,15 +29,17 @@ class _StarredPartState extends RefreshableState<StarredPartWidget> {
|
||||
String getAppBarTitle(BuildContext context) => L10().partsStarred;
|
||||
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
Future<void> request() async {
|
||||
|
||||
final parts = await InvenTreePart().list(context, filters: {"starred": "true"});
|
||||
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]);
|
||||
if (parts != null) {
|
||||
for (int idx = 0; idx < parts.length; idx++) {
|
||||
if (parts[idx] is InvenTreePart) {
|
||||
starredParts.add(parts[idx] as InvenTreePart);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -54,7 +56,7 @@ class _StarredPartState extends RefreshableState<StarredPartWidget> {
|
||||
height: 40
|
||||
),
|
||||
onTap: () {
|
||||
InvenTreePart().get(context, part.pk).then((var prt) {
|
||||
InvenTreePart().get(part.pk).then((var prt) {
|
||||
if (prt is InvenTreePart) {
|
||||
Navigator.push(
|
||||
context,
|
||||
|
@ -25,7 +25,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
class StockDetailWidget extends StatefulWidget {
|
||||
|
||||
StockDetailWidget(this.item, {Key key}) : super(key: key);
|
||||
StockDetailWidget(this.item, {Key? key}) : super(key: key);
|
||||
|
||||
final InvenTreeStockItem item;
|
||||
|
||||
@ -77,7 +77,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
final InvenTreeStockItem item;
|
||||
|
||||
// Part object
|
||||
InvenTreePart part;
|
||||
InvenTreePart? part;
|
||||
|
||||
@override
|
||||
Future<void> onBuild(BuildContext context) async {
|
||||
@ -89,14 +89,14 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
await item.reload(context);
|
||||
Future<void> request() async {
|
||||
await item.reload();
|
||||
|
||||
// Request part information
|
||||
part = await InvenTreePart().get(context, item.partId);
|
||||
part = await InvenTreePart().get(item.partId) as InvenTreePart;
|
||||
|
||||
// Request test results...
|
||||
await item.getTestResults(context);
|
||||
await item.getTestResults();
|
||||
}
|
||||
|
||||
void _addStock() async {
|
||||
@ -227,7 +227,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
|
||||
void _unassignBarcode(BuildContext context) async {
|
||||
|
||||
final bool result = await item.update(context, values: {'uid': ''});
|
||||
final bool result = await item.update(values: {'uid': ''});
|
||||
|
||||
if (result) {
|
||||
showSnackIcon(
|
||||
@ -245,7 +245,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
}
|
||||
|
||||
|
||||
void _transferStock(BuildContext context, InvenTreeStockLocation location) async {
|
||||
void _transferStock(InvenTreeStockLocation location) async {
|
||||
|
||||
double quantity = double.tryParse(_quantityController.text) ?? item.quantity;
|
||||
String notes = _notesController.text;
|
||||
@ -264,17 +264,21 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
|
||||
void _transferStockDialog() async {
|
||||
|
||||
var locations = await InvenTreeStockLocation().list(context);
|
||||
var locations = await InvenTreeStockLocation().list();
|
||||
final _selectedController = TextEditingController();
|
||||
|
||||
InvenTreeStockLocation selectedLocation;
|
||||
InvenTreeStockLocation? selectedLocation;
|
||||
|
||||
_quantityController.text = "${item.quantityString}";
|
||||
|
||||
showFormDialog(L10().transferStock,
|
||||
key: _moveStockKey,
|
||||
callback: () {
|
||||
_transferStock(context, selectedLocation);
|
||||
var _loc = selectedLocation;
|
||||
|
||||
if (_loc != null) {
|
||||
_transferStock(_loc);
|
||||
}
|
||||
},
|
||||
fields: <Widget>[
|
||||
QuantityField(
|
||||
@ -292,7 +296,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
)
|
||||
),
|
||||
suggestionsCallback: (pattern) async {
|
||||
var suggestions = List<InvenTreeStockLocation>();
|
||||
var suggestions = List<InvenTreeStockLocation>.empty();
|
||||
|
||||
for (var loc in locations) {
|
||||
if (loc.matchAgainstString(pattern)) {
|
||||
@ -311,7 +315,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
},
|
||||
onSuggestionSelected: (suggestion) {
|
||||
selectedLocation = suggestion as InvenTreeStockLocation;
|
||||
_selectedController.text = selectedLocation.pathstring;
|
||||
_selectedController.text = selectedLocation!.pathstring;
|
||||
},
|
||||
onSaved: (value) {
|
||||
},
|
||||
@ -342,7 +346,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
),
|
||||
onTap: () {
|
||||
if (item.partId > 0) {
|
||||
InvenTreePart().get(context, item.partId).then((var part) {
|
||||
InvenTreePart().get(item.partId).then((var part) {
|
||||
if (part is InvenTreePart) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
}
|
||||
@ -397,9 +401,12 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
leading: FaIcon(FontAwesomeIcons.mapMarkerAlt),
|
||||
onTap: () {
|
||||
if (item.locationId > 0) {
|
||||
InvenTreeStockLocation().get(context, item.locationId).then((var loc) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => LocationDisplayWidget(loc)));
|
||||
InvenTreeStockLocation().get(item.locationId).then((var loc) {
|
||||
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => LocationDisplayWidget(loc)));
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -442,7 +449,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
if ((item.testResultCount > 0) || (part != null && part.isTrackable)) {
|
||||
if ((item.testResultCount > 0) || (part?.isTrackable ?? false)) {
|
||||
tiles.add(
|
||||
ListTile(
|
||||
title: Text(L10().testResults),
|
||||
@ -641,7 +648,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
).toList()
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
return ListView();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
class StockItemTestResultsWidget extends StatefulWidget {
|
||||
|
||||
StockItemTestResultsWidget(this.item, {Key key}) : super(key: key);
|
||||
StockItemTestResultsWidget(this.item, {Key? key}) : super(key: key);
|
||||
|
||||
final InvenTreeStockItem item;
|
||||
|
||||
@ -36,16 +36,16 @@ class _StockItemTestResultDisplayState extends RefreshableState<StockItemTestRes
|
||||
String getAppBarTitle(BuildContext context) => L10().testResults;
|
||||
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
await item.getTestTemplates(context);
|
||||
await item.getTestResults(context);
|
||||
Future<void> request() async {
|
||||
await item.getTestTemplates();
|
||||
await item.getTestResults();
|
||||
}
|
||||
|
||||
final InvenTreeStockItem item;
|
||||
|
||||
_StockItemTestResultDisplayState(this.item);
|
||||
|
||||
void uploadTestResult(String name, bool result, String value, String notes, File attachment) async {
|
||||
void uploadTestResult(String name, bool result, String value, String notes, File? attachment) async {
|
||||
|
||||
final success = await item.uploadTestResult(
|
||||
context, name, result,
|
||||
@ -64,11 +64,11 @@ class _StockItemTestResultDisplayState extends RefreshableState<StockItemTestRes
|
||||
|
||||
void addTestResult({String name = '', bool nameIsEditable = true, bool result = false, String value = '', bool valueRequired = false, bool attachmentRequired = false}) async {
|
||||
|
||||
String _name;
|
||||
bool _result;
|
||||
String _value;
|
||||
String _notes;
|
||||
File _attachment;
|
||||
String _name = "";
|
||||
bool _result = false;
|
||||
String _value = "";
|
||||
String _notes = "";
|
||||
File? _attachment;
|
||||
|
||||
showFormDialog(L10().testResultAdd,
|
||||
key: _addResultKey,
|
||||
@ -80,21 +80,21 @@ class _StockItemTestResultDisplayState extends RefreshableState<StockItemTestRes
|
||||
label: L10().testName,
|
||||
initial: name,
|
||||
isEnabled: nameIsEditable,
|
||||
onSaved: (value) => _name = value,
|
||||
onSaved: (value) => _name = value ?? '',
|
||||
),
|
||||
CheckBoxField(
|
||||
label: L10().result,
|
||||
hint: L10().testPassedOrFailed,
|
||||
initial: true,
|
||||
onSaved: (value) => _result = value,
|
||||
onSaved: (value) => _result = value ?? false,
|
||||
),
|
||||
StringField(
|
||||
label: L10().value,
|
||||
initial: value,
|
||||
allowEmpty: true,
|
||||
onSaved: (value) => _value = value,
|
||||
onSaved: (value) => _value = value ?? '',
|
||||
validator: (String value) {
|
||||
if (valueRequired && (value == null || value.isEmpty)) {
|
||||
if (valueRequired && value.isEmpty) {
|
||||
return L10().valueRequired;
|
||||
}
|
||||
return null;
|
||||
@ -109,7 +109,7 @@ class _StockItemTestResultDisplayState extends RefreshableState<StockItemTestRes
|
||||
StringField(
|
||||
allowEmpty: true,
|
||||
label: L10().notes,
|
||||
onSaved: (value) => _notes = value,
|
||||
onSaved: (value) => _notes = value ?? '',
|
||||
),
|
||||
]
|
||||
);
|
||||
@ -202,10 +202,11 @@ class _StockItemTestResultDisplayState extends RefreshableState<StockItemTestRes
|
||||
for (var item in results) {
|
||||
|
||||
bool _required = false;
|
||||
String _test;
|
||||
bool _result = null;
|
||||
String _value;
|
||||
String _notes;
|
||||
String _test = "";
|
||||
bool _result = false;
|
||||
String _value = "";
|
||||
String _notes = "";
|
||||
|
||||
FaIcon _icon = FaIcon(FontAwesomeIcons.questionCircle, color: Color.fromRGBO(0, 0, 250, 1));
|
||||
bool _valueRequired = false;
|
||||
bool _attachmentRequired = false;
|
||||
|
@ -10,7 +10,7 @@ class StockNotesWidget extends StatefulWidget {
|
||||
|
||||
final InvenTreeStockItem item;
|
||||
|
||||
StockNotesWidget(this.item, {Key key}) : super(key: key);
|
||||
StockNotesWidget(this.item, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_StockNotesState createState() => _StockNotesState(item);
|
||||
|
Reference in New Issue
Block a user