2
0
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:
Oliver
2021-07-09 23:56:38 +10:00
parent 2988716bf3
commit d3eec6a79e
30 changed files with 563 additions and 456 deletions

View File

@ -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)));

View File

@ -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;

View File

@ -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)));
}
});
}
},

View File

@ -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
)
)
)

View File

@ -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;

View File

@ -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.
//

View File

@ -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)));
}

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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,
);
}

View File

@ -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,
);
}

View File

@ -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,

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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);