mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-28 05:26:47 +00:00
Merge branch 'barcodez'
This commit is contained in:
commit
54d6e1c6d3
13
lib/api.dart
13
lib/api.dart
@ -182,15 +182,22 @@ class InvenTreeAPI {
|
||||
var response = await get("").timeout(Duration(seconds: 10)).catchError((error) {
|
||||
|
||||
if (error is SocketException) {
|
||||
errorMessage = "Could not connect to server.";
|
||||
print(errorMessage);
|
||||
throw errorMessage;
|
||||
print("Could not connect to server");
|
||||
return null;
|
||||
} else if (error is TimeoutException) {
|
||||
print("Server timeout");
|
||||
return null;
|
||||
} else {
|
||||
// Unknown error type, re-throw error
|
||||
print("Unknown error: ${error.toString()}");
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
if (response == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
print("Invalid status code: " + response.statusCode.toString());
|
||||
return false;
|
||||
|
163
lib/barcode.dart
163
lib/barcode.dart
@ -1,3 +1,4 @@
|
||||
import 'package:InvenTree/widget/dialogs.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qr_utils/qr_utils.dart';
|
||||
@ -5,6 +6,8 @@ import 'package:qr_utils/qr_utils.dart';
|
||||
import 'package:InvenTree/inventree/stock.dart';
|
||||
import 'package:InvenTree/inventree/part.dart';
|
||||
|
||||
import 'package:InvenTree/api.dart';
|
||||
|
||||
import 'package:InvenTree/widget/location_display.dart';
|
||||
import 'package:InvenTree/widget/part_detail.dart';
|
||||
import 'package:InvenTree/widget/category_display.dart';
|
||||
@ -12,59 +15,139 @@ import 'package:InvenTree/widget/stock_detail.dart';
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
void scanQrCode(BuildContext context) async {
|
||||
Future<void> scanQrCode(BuildContext context) async {
|
||||
|
||||
QrUtils.scanQR.then((String result) {
|
||||
QrUtils.scanQR.then((String barcode) {
|
||||
|
||||
print("Scanned: $result");
|
||||
print("Scanned: $barcode");
|
||||
|
||||
// Look for JSON data in the result...
|
||||
final data = json.decode(result);
|
||||
showProgressDialog(context, "Querying Server", "Sending barcode data to server");
|
||||
|
||||
// Look for an 'InvenTree' style barcode
|
||||
if ((data['tool'] ?? '').toString().toLowerCase() == 'inventree') {
|
||||
_handleInvenTreeBarcode(context, data);
|
||||
}
|
||||
/*
|
||||
* POST the scanned barcode data to the server.
|
||||
* It is the responsibility of the server to validate and sanitize the barcode data,
|
||||
* and return a "common" response that we know how to deal with.
|
||||
*/
|
||||
InvenTreeAPI().post("barcode/", body: {"barcode": barcode}).then((var response) {
|
||||
|
||||
// Unknown barcode style!
|
||||
else {
|
||||
showDialog(
|
||||
context: context,
|
||||
child: new SimpleDialog(
|
||||
title: new Text("Unknown barcode"),
|
||||
children: <Widget>[
|
||||
Text("Data: $result"),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
hideProgressDialog(context);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
showDialog(
|
||||
context: context,
|
||||
child: new SimpleDialog(
|
||||
title: Text("Server Error"),
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
title: Text("Error ${response.statusCode}"),
|
||||
subtitle: Text("${response.body.toString().split("\n").first}"),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<String, dynamic> body = json.decode(response.body);
|
||||
|
||||
// TODO - Handle potential error decoding response
|
||||
|
||||
print("Barcode response:");
|
||||
print(body.toString());
|
||||
|
||||
if (body.containsKey('error')) {
|
||||
showDialog(
|
||||
context: context,
|
||||
child: new SimpleDialog(
|
||||
title: Text("Barcode Error"),
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
title: Text("${body['error']}"),
|
||||
subtitle: Text("Plugin: ${body['plugin'] ?? '<no plugin information>'}"),
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
} else if (body.containsKey('success')) {
|
||||
// Decode the barcode!
|
||||
// Ideally, the server has returned unto us something sensible...
|
||||
_handleBarcode(context, body);
|
||||
} else {
|
||||
showDialog(
|
||||
context: context,
|
||||
child: new SimpleDialog(
|
||||
title: Text("Unknown response"),
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
title: Text("Response data"),
|
||||
subtitle: Text("${body.toString()}"),
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
print("body: ${body.toString()}");
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void _handleInvenTreeBarcode(BuildContext context, Map<String, dynamic> data) {
|
||||
void _handleBarcode(BuildContext context, Map<String, dynamic> data) {
|
||||
|
||||
final String codeType = (data['type'] ?? '').toString().toLowerCase();
|
||||
int pk;
|
||||
|
||||
final int pk = (data['id'] ?? -1) as int;
|
||||
// A stocklocation has been passed?
|
||||
if (data.containsKey('stocklocation')) {
|
||||
|
||||
if (codeType == 'stocklocation') {
|
||||
pk = data['stocklocation']['pk'] as int ?? null;
|
||||
|
||||
// Try to open a stock location...
|
||||
InvenTreeStockLocation().get(pk).then((var loc) {
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc)));
|
||||
}
|
||||
});
|
||||
if (pk != null) {
|
||||
InvenTreeStockLocation().get(context, pk).then((var loc) {
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc)));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// TODO - Show an error here!
|
||||
}
|
||||
|
||||
} else if (codeType == 'stockitem') {
|
||||
InvenTreeStockItem().get(pk).then((var item) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => StockDetailWidget(item)));
|
||||
});
|
||||
} else if (codeType == 'part') {
|
||||
InvenTreePart().get(pk).then((var part) {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
});
|
||||
} else if (data.containsKey('stockitem')) {
|
||||
|
||||
pk = data['stockitem']['pk'] as int ?? null;
|
||||
|
||||
if (pk != null) {
|
||||
InvenTreeStockItem().get(context, pk).then((var item) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => StockDetailWidget(item)));
|
||||
});
|
||||
} else {
|
||||
// TODO - Show an error here!
|
||||
}
|
||||
} else if (data.containsKey('part')) {
|
||||
|
||||
pk = data['part']['pk'] as int ?? null;
|
||||
|
||||
if (pk != null) {
|
||||
InvenTreePart().get(context, pk).then((var part) {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
});
|
||||
} else {
|
||||
// TODO - Show an error here!
|
||||
}
|
||||
} else {
|
||||
showDialog(
|
||||
context: context,
|
||||
child: SimpleDialog(
|
||||
title: Text("Unknown response"),
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
title: Text("Response data"),
|
||||
subtitle: Text(data.toString()),
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -5,6 +5,10 @@ import 'model.dart';
|
||||
* The InvenTreeCompany class repreents the Company model in the InvenTree database.
|
||||
*/
|
||||
class InvenTreeCompany extends InvenTreeModel {
|
||||
|
||||
@override
|
||||
String NAME = "Company";
|
||||
|
||||
@override
|
||||
String URL = "company/";
|
||||
|
||||
|
@ -1,4 +1,8 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:InvenTree/api.dart';
|
||||
import 'package:InvenTree/widget/dialogs.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
@ -15,6 +19,8 @@ class InvenTreeModel {
|
||||
// Override the endpoint URL for each subclass
|
||||
String URL = "";
|
||||
|
||||
String NAME = "Model";
|
||||
|
||||
// JSON data which defines this object
|
||||
Map<String, dynamic> jsondata = {};
|
||||
|
||||
@ -83,9 +89,30 @@ class InvenTreeModel {
|
||||
/*
|
||||
* Reload this object, by requesting data from the server
|
||||
*/
|
||||
Future<bool> reload() async {
|
||||
Future<bool> reload(BuildContext context) async {
|
||||
|
||||
var response = await api.get(url, params: defaultGetFilters());
|
||||
showProgressDialog(context, "Refreshing data", "Refreshing data for ${NAME}");
|
||||
|
||||
var response = await api.get(url, params: defaultGetFilters())
|
||||
.timeout(Duration(seconds: 10))
|
||||
.catchError((e) {
|
||||
|
||||
hideProgressDialog(context);
|
||||
|
||||
if (e is TimeoutException) {
|
||||
showErrorDialog(context, "Timeout", "No response from server");
|
||||
} else {
|
||||
showErrorDialog(context, "Error", e.toString());
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
if (response == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hideProgressDialog(context);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
print("Error retrieving data");
|
||||
@ -100,7 +127,7 @@ class InvenTreeModel {
|
||||
}
|
||||
|
||||
// Return the detail view for the associated pk
|
||||
Future<InvenTreeModel> get(int pk, {Map<String, String> filters}) async {
|
||||
Future<InvenTreeModel> get(BuildContext context, int pk, {Map<String, String> filters}) async {
|
||||
|
||||
// TODO - Add "timeout"
|
||||
// TODO - Add error catching
|
||||
@ -122,7 +149,27 @@ class InvenTreeModel {
|
||||
|
||||
print("GET: $addr ${params.toString()}");
|
||||
|
||||
var response = await api.get(addr, params: params);
|
||||
showProgressDialog(context, "Requesting Data", "Requesting ${NAME} data from server");
|
||||
|
||||
var response = await api.get(addr, params: params)
|
||||
.timeout(Duration(seconds: 10))
|
||||
.catchError((e) {
|
||||
|
||||
hideProgressDialog(context);
|
||||
|
||||
if (e is TimeoutException) {
|
||||
showErrorDialog(context, "Timeout", "No response from server");
|
||||
} else {
|
||||
showErrorDialog(context, "Error", e.toString());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
hideProgressDialog(context);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
print("Error retrieving data");
|
||||
@ -135,7 +182,7 @@ class InvenTreeModel {
|
||||
}
|
||||
|
||||
// Return list of objects from the database, with optional filters
|
||||
Future<List<InvenTreeModel>> list({Map<String, String> filters}) async {
|
||||
Future<List<InvenTreeModel>> list(BuildContext context, {Map<String, String> filters}) async {
|
||||
|
||||
if (filters == null) {
|
||||
filters = {};
|
||||
@ -154,7 +201,28 @@ class InvenTreeModel {
|
||||
// TODO - Add "timeout"
|
||||
// TODO - Add error catching
|
||||
|
||||
var response = await api.get(URL, params:params);
|
||||
showProgressDialog(context, "Requesting Data", "Requesting ${NAME} data from server");
|
||||
|
||||
var response = await api.get(URL, params:params)
|
||||
.timeout(Duration(seconds: 10))
|
||||
.catchError((e) {
|
||||
|
||||
hideProgressDialog(context);
|
||||
|
||||
if (e is TimeoutException) {
|
||||
showErrorDialog(context, "Timeout", "No response from server");
|
||||
} else {
|
||||
showErrorDialog(context, "Error", e.toString());
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
hideProgressDialog(context);
|
||||
|
||||
// A list of "InvenTreeModel" items
|
||||
List<InvenTreeModel> results = new List<InvenTreeModel>();
|
||||
|
@ -9,6 +9,10 @@ import 'package:path/path.dart' as path;
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
class InvenTreePartCategory extends InvenTreeModel {
|
||||
|
||||
@override
|
||||
String NAME = "PartCategory";
|
||||
|
||||
@override
|
||||
String URL = "part/category/";
|
||||
|
||||
@ -61,6 +65,9 @@ class InvenTreePartCategory extends InvenTreeModel {
|
||||
|
||||
class InvenTreePart extends InvenTreeModel {
|
||||
|
||||
@override
|
||||
String NAME = "Part";
|
||||
|
||||
@override
|
||||
String URL = "part/";
|
||||
|
||||
|
@ -6,6 +6,10 @@ import 'model.dart';
|
||||
import 'package:InvenTree/api.dart';
|
||||
|
||||
class InvenTreeStockItem extends InvenTreeModel {
|
||||
|
||||
@override
|
||||
String NAME = "StockItem";
|
||||
|
||||
@override
|
||||
String URL = "stock/";
|
||||
|
||||
@ -208,6 +212,10 @@ class InvenTreeStockItem extends InvenTreeModel {
|
||||
|
||||
|
||||
class InvenTreeStockLocation extends InvenTreeModel {
|
||||
|
||||
@override
|
||||
String NAME = "StockLocation";
|
||||
|
||||
@override
|
||||
String URL = "stock/location/";
|
||||
|
||||
|
@ -22,6 +22,8 @@ import 'preferences.dart';
|
||||
|
||||
import 'package:InvenTree/inventree/part.dart';
|
||||
|
||||
|
||||
|
||||
void main() async {
|
||||
|
||||
// await PrefService.init(prefix: "inventree_");
|
||||
@ -31,10 +33,10 @@ void main() async {
|
||||
// Load login details
|
||||
InvenTreePreferences().loadLoginDetails();
|
||||
|
||||
runApp(MyApp());
|
||||
runApp(InvenTreeApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
class InvenTreeApp extends StatelessWidget {
|
||||
// This widget is the root of your application.
|
||||
|
||||
@override
|
||||
@ -42,16 +44,8 @@ class MyApp extends StatelessWidget {
|
||||
return MaterialApp(
|
||||
title: 'InvenTree',
|
||||
theme: ThemeData(
|
||||
// This is the theme of your application.
|
||||
//
|
||||
// Try running your application with "flutter run". You'll see the
|
||||
// application has a blue toolbar. Then, without quitting the app, try
|
||||
// changing the primarySwatch below to Colors.green and then invoke
|
||||
// "hot reload" (press "r" in the console where you ran "flutter run",
|
||||
// or simply save your changes to "hot reload" in a Flutter IDE).
|
||||
// Notice that the counter didn't reset back to zero; the application
|
||||
// is not restarted.
|
||||
primarySwatch: Colors.lightGreen,
|
||||
primarySwatch: Colors.lightBlue,
|
||||
secondaryHeaderColor: Colors.blueGrey,
|
||||
),
|
||||
home: MyHomePage(title: 'InvenTree'),
|
||||
);
|
||||
@ -59,34 +53,6 @@ class MyApp extends StatelessWidget {
|
||||
}
|
||||
|
||||
|
||||
class ProductList extends StatelessWidget {
|
||||
final List<InvenTreePart> _parts;
|
||||
|
||||
ProductList(this._parts);
|
||||
|
||||
Widget _buildPart(BuildContext context, int index) {
|
||||
InvenTreePart part;
|
||||
|
||||
if (index < _parts.length) {
|
||||
part = _parts[index];
|
||||
}
|
||||
|
||||
return Card(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Text('${part.name} - ${part.description}'),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(itemBuilder: _buildPart, itemCount: _parts.length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MyHomePage extends StatefulWidget {
|
||||
MyHomePage({Key key, this.title}) : super(key: key);
|
||||
|
||||
@ -168,22 +134,6 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
onConnectFailure("Could not connect to server");
|
||||
}
|
||||
|
||||
}).catchError((e) {
|
||||
|
||||
String fault = "Connection error";
|
||||
|
||||
_serverConnection = false;
|
||||
_serverStatusColor = Color.fromARGB(255, 250, 50, 50);
|
||||
|
||||
_serverStatus = "Error connecting to $_serverAddress";
|
||||
|
||||
if (e is TimeoutException) {
|
||||
fault = "Timeout: No response from server";
|
||||
} else {
|
||||
fault = e.toString();
|
||||
}
|
||||
|
||||
onConnectFailure(fault);
|
||||
});
|
||||
|
||||
// Update widget state
|
||||
|
@ -5,6 +5,7 @@ import 'package:InvenTree/preferences.dart';
|
||||
|
||||
import 'package:InvenTree/widget/part_detail.dart';
|
||||
import 'package:InvenTree/widget/drawer.dart';
|
||||
import 'package:InvenTree/widget/refreshable_state.dart';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
@ -26,11 +27,12 @@ class CategoryDisplayWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
|
||||
class _CategoryDisplayState extends State<CategoryDisplayWidget> {
|
||||
class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
|
||||
_CategoryDisplayState(this.category) {
|
||||
_requestData();
|
||||
}
|
||||
@override
|
||||
String getAppBarTitle(BuildContext context) { return "Part Category"; }
|
||||
|
||||
_CategoryDisplayState(this.category) {}
|
||||
|
||||
// The local InvenTreePartCategory object
|
||||
final InvenTreePartCategory category;
|
||||
@ -39,24 +41,18 @@ class _CategoryDisplayState extends State<CategoryDisplayWidget> {
|
||||
|
||||
List<InvenTreePart> _parts = List<InvenTreePart>();
|
||||
|
||||
String get _titleString {
|
||||
|
||||
if (category == null) {
|
||||
return "Part Categories";
|
||||
} else {
|
||||
return "Part Category - ${category.name}";
|
||||
}
|
||||
@override
|
||||
Future<void> onBuild(BuildContext context) async {
|
||||
refresh();
|
||||
}
|
||||
|
||||
/*
|
||||
* Request data from the server
|
||||
*/
|
||||
void _requestData() {
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
|
||||
int pk = category?.pk ?? -1;
|
||||
|
||||
// Request a list of sub-categories under this one
|
||||
InvenTreePartCategory().list(filters: {"parent": "$pk"}).then((var cats) {
|
||||
InvenTreePartCategory().list(context, filters: {"parent": "$pk"}).then((var cats) {
|
||||
_subcategories.clear();
|
||||
|
||||
for (var cat in cats) {
|
||||
@ -70,7 +66,7 @@ class _CategoryDisplayState extends State<CategoryDisplayWidget> {
|
||||
});
|
||||
|
||||
// Request a list of parts under this category
|
||||
InvenTreePart().list(filters: {"category": "$pk"}).then((var parts) {
|
||||
InvenTreePart().list(context, filters: {"category": "$pk"}).then((var parts) {
|
||||
_parts.clear();
|
||||
|
||||
for (var part in parts) {
|
||||
@ -112,7 +108,7 @@ class _CategoryDisplayState extends State<CategoryDisplayWidget> {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)));
|
||||
} else {
|
||||
// TODO - Refactor this code into the InvenTreePart class
|
||||
InvenTreePartCategory().get(category.parentId).then((var cat) {
|
||||
InvenTreePartCategory().get(context, category.parentId).then((var cat) {
|
||||
if (cat is InvenTreePartCategory) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(cat)));
|
||||
}
|
||||
@ -127,74 +123,68 @@ class _CategoryDisplayState extends State<CategoryDisplayWidget> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(_titleString),
|
||||
),
|
||||
drawer: new InvenTreeDrawer(context),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
getCategoryDescriptionCard(),
|
||||
ExpansionPanelList(
|
||||
expansionCallback: (int index, bool isExpanded) {
|
||||
setState(() {
|
||||
Widget getBody(BuildContext context) {
|
||||
return ListView(
|
||||
children: <Widget>[
|
||||
getCategoryDescriptionCard(),
|
||||
ExpansionPanelList(
|
||||
expansionCallback: (int index, bool isExpanded) {
|
||||
setState(() {
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
InvenTreePreferences().expandCategoryList = !isExpanded;
|
||||
break;
|
||||
case 1:
|
||||
InvenTreePreferences().expandPartList = !isExpanded;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
switch (index) {
|
||||
case 0:
|
||||
InvenTreePreferences().expandCategoryList = !isExpanded;
|
||||
break;
|
||||
case 1:
|
||||
InvenTreePreferences().expandPartList = !isExpanded;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
},
|
||||
children: <ExpansionPanel> [
|
||||
ExpansionPanel(
|
||||
headerBuilder: (BuildContext context, bool isExpanded) {
|
||||
return ListTile(
|
||||
title: Text("Subcategories"),
|
||||
leading: FaIcon(FontAwesomeIcons.stream),
|
||||
trailing: Text("${_subcategories.length}"),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
InvenTreePreferences().expandCategoryList = !InvenTreePreferences().expandCategoryList;
|
||||
});
|
||||
},
|
||||
onLongPress: () {
|
||||
// TODO - Context menu for e.g. creating a new PartCategory
|
||||
},
|
||||
);
|
||||
},
|
||||
children: <ExpansionPanel> [
|
||||
ExpansionPanel(
|
||||
headerBuilder: (BuildContext context, bool isExpanded) {
|
||||
return ListTile(
|
||||
title: Text("Subcategories"),
|
||||
leading: FaIcon(FontAwesomeIcons.stream),
|
||||
trailing: Text("${_subcategories.length}"),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
InvenTreePreferences().expandCategoryList = !InvenTreePreferences().expandCategoryList;
|
||||
});
|
||||
},
|
||||
onLongPress: () {
|
||||
// TODO - Context menu for e.g. creating a new PartCategory
|
||||
},
|
||||
);
|
||||
},
|
||||
body: SubcategoryList(_subcategories),
|
||||
isExpanded: InvenTreePreferences().expandCategoryList && _subcategories.length > 0,
|
||||
),
|
||||
ExpansionPanel(
|
||||
headerBuilder: (BuildContext context, bool isExpanded) {
|
||||
return ListTile(
|
||||
title: Text("Parts"),
|
||||
leading: FaIcon(FontAwesomeIcons.shapes),
|
||||
trailing: Text("${_parts.length}"),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
InvenTreePreferences().expandPartList = !InvenTreePreferences().expandPartList;
|
||||
});
|
||||
},
|
||||
onLongPress: () {
|
||||
// TODO - Context menu for e.g. creating a new Part
|
||||
},
|
||||
);
|
||||
},
|
||||
body: PartList(_parts),
|
||||
isExpanded: InvenTreePreferences().expandPartList && _parts.length > 0,
|
||||
)
|
||||
],
|
||||
body: SubcategoryList(_subcategories),
|
||||
isExpanded: InvenTreePreferences().expandCategoryList && _subcategories.length > 0,
|
||||
),
|
||||
]
|
||||
)
|
||||
ExpansionPanel(
|
||||
headerBuilder: (BuildContext context, bool isExpanded) {
|
||||
return ListTile(
|
||||
title: Text("Parts"),
|
||||
leading: FaIcon(FontAwesomeIcons.shapes),
|
||||
trailing: Text("${_parts.length}"),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
InvenTreePreferences().expandPartList = !InvenTreePreferences().expandPartList;
|
||||
});
|
||||
},
|
||||
onLongPress: () {
|
||||
// TODO - Context menu for e.g. creating a new Part
|
||||
},
|
||||
);
|
||||
},
|
||||
body: PartList(_parts),
|
||||
isExpanded: InvenTreePreferences().expandPartList && _parts.length > 0,
|
||||
)
|
||||
],
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -211,7 +201,7 @@ class SubcategoryList extends StatelessWidget {
|
||||
void _openCategory(BuildContext context, int pk) {
|
||||
|
||||
// Attempt to load the sub-category.
|
||||
InvenTreePartCategory().get(pk).then((var cat) {
|
||||
InvenTreePartCategory().get(context, pk).then((var cat) {
|
||||
if (cat is InvenTreePartCategory) {
|
||||
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(cat)));
|
||||
@ -252,7 +242,7 @@ class PartList extends StatelessWidget {
|
||||
|
||||
void _openPart(BuildContext context, int pk) {
|
||||
// Attempt to load the part information
|
||||
InvenTreePart().get(pk).then((var part) {
|
||||
InvenTreePart().get(context, pk).then((var part) {
|
||||
if (part is InvenTreePart) {
|
||||
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
|
@ -2,6 +2,7 @@
|
||||
import 'package:InvenTree/api.dart';
|
||||
import 'package:InvenTree/inventree/company.dart';
|
||||
import 'package:InvenTree/widget/drawer.dart';
|
||||
import 'package:InvenTree/widget/refreshable_state.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
@ -18,10 +19,18 @@ class CompanyDetailWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
|
||||
class _CompanyDetailState extends State<CompanyDetailWidget> {
|
||||
class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
||||
|
||||
final InvenTreeCompany company;
|
||||
|
||||
@override
|
||||
String getAppBarTitle(BuildContext context) { return "Company"; }
|
||||
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
await company.reload(context);
|
||||
}
|
||||
|
||||
_CompanyDetailState(this.company) {
|
||||
// TODO
|
||||
}
|
||||
@ -122,17 +131,11 @@ class _CompanyDetailState extends State<CompanyDetailWidget> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget getBody(BuildContext context) {
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("${company.name}"),
|
||||
),
|
||||
drawer: new InvenTreeDrawer(context),
|
||||
body: Center(
|
||||
child: ListView(
|
||||
children: _companyTiles(),
|
||||
)
|
||||
return Center(
|
||||
child: ListView(
|
||||
children: _companyTiles(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:InvenTree/api.dart';
|
||||
import 'package:InvenTree/inventree/company.dart';
|
||||
import 'package:InvenTree/widget/drawer.dart';
|
||||
import 'package:InvenTree/widget/refreshable_state.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
abstract class CompanyListWidget extends StatefulWidget {
|
||||
@ -30,23 +31,30 @@ class CustomerListWidget extends CompanyListWidget {
|
||||
}
|
||||
|
||||
|
||||
class _CompanyListState extends State<CompanyListWidget> {
|
||||
class _CompanyListState extends RefreshableState<CompanyListWidget> {
|
||||
|
||||
var _companies = new List<InvenTreeCompany>();
|
||||
|
||||
var _filteredCompanies = new List<InvenTreeCompany>();
|
||||
|
||||
var _title = "Companies";
|
||||
String _title = "Companies";
|
||||
|
||||
@override
|
||||
String getAppBarTitle(BuildContext context) { return _title; }
|
||||
|
||||
Map<String, String> _filters = Map<String, String>();
|
||||
|
||||
_CompanyListState(this._title, this._filters) {
|
||||
_requestData();
|
||||
_CompanyListState(this._title, this._filters) {}
|
||||
|
||||
@override
|
||||
Future<void> onBuild(BuildContext context) async {
|
||||
refresh();
|
||||
}
|
||||
|
||||
void _requestData() {
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
|
||||
InvenTreeCompany().list(filters: _filters).then((var companies) {
|
||||
InvenTreeCompany().list(context, filters: _filters).then((var companies) {
|
||||
|
||||
_companies.clear();
|
||||
|
||||
@ -85,7 +93,7 @@ class _CompanyListState extends State<CompanyListWidget> {
|
||||
),
|
||||
onTap: () {
|
||||
if (company.pk > 0) {
|
||||
InvenTreeCompany().get(company.pk).then((var c) {
|
||||
InvenTreeCompany().get(context, company.pk).then((var c) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyDetailWidget(c)));
|
||||
});
|
||||
}
|
||||
@ -94,38 +102,24 @@ class _CompanyListState extends State<CompanyListWidget> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("$_title"),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: FaIcon(FontAwesomeIcons.plus),
|
||||
tooltip: 'New',
|
||||
onPressed: null,
|
||||
)
|
||||
],
|
||||
Widget getBody(BuildContext context) {
|
||||
return ListView(
|
||||
children: <Widget>[
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Filter results',
|
||||
),
|
||||
onChanged: (String text) {
|
||||
setState(() {
|
||||
_filterResults(text);
|
||||
});
|
||||
},
|
||||
),
|
||||
drawer: new InvenTreeDrawer(context),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Filter results',
|
||||
),
|
||||
onChanged: (String text) {
|
||||
setState(() {
|
||||
_filterResults(text);
|
||||
});
|
||||
},
|
||||
),
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: ClampingScrollPhysics(),
|
||||
itemBuilder: _showCompany, itemCount: _filteredCompanies.length)
|
||||
],
|
||||
)
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: ClampingScrollPhysics(),
|
||||
itemBuilder: _showCompany, itemCount: _filteredCompanies.length)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
}
|
41
lib/widget/dialogs.dart
Normal file
41
lib/widget/dialogs.dart
Normal file
@ -0,0 +1,41 @@
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
void showErrorDialog(BuildContext context, String title, String description) {
|
||||
showDialog(
|
||||
context: context,
|
||||
child: SimpleDialog(
|
||||
title: ListTile(
|
||||
title: Text("Error"),
|
||||
leading: FaIcon(FontAwesomeIcons.exclamationCircle),
|
||||
),
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
title: Text(title),
|
||||
subtitle: Text(description)
|
||||
)
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void showProgressDialog(BuildContext context, String title, String description) {
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
child: SimpleDialog(
|
||||
title: Text(title),
|
||||
children: <Widget>[
|
||||
CircularProgressIndicator(),
|
||||
Text(description),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void hideProgressDialog(BuildContext context) {
|
||||
Navigator.pop(context);
|
||||
}
|
@ -8,6 +8,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
import 'package:InvenTree/widget/refreshable_state.dart';
|
||||
|
||||
class LocationDisplayWidget extends StatefulWidget {
|
||||
|
||||
LocationDisplayWidget(this.location, {Key key}) : super(key: key);
|
||||
@ -20,15 +22,15 @@ class LocationDisplayWidget extends StatefulWidget {
|
||||
_LocationDisplayState createState() => _LocationDisplayState(location);
|
||||
}
|
||||
|
||||
|
||||
class _LocationDisplayState extends State<LocationDisplayWidget> {
|
||||
|
||||
_LocationDisplayState(this.location) {
|
||||
_requestData();
|
||||
}
|
||||
class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
|
||||
final InvenTreeStockLocation location;
|
||||
|
||||
@override
|
||||
String getAppBarTitle(BuildContext context) { return "Stock Location"; }
|
||||
|
||||
_LocationDisplayState(this.location) {}
|
||||
|
||||
List<InvenTreeStockLocation> _sublocations = List<InvenTreeStockLocation>();
|
||||
|
||||
String _locationFilter = '';
|
||||
@ -44,28 +46,18 @@ class _LocationDisplayState extends State<LocationDisplayWidget> {
|
||||
|
||||
List<InvenTreeStockItem> _items = List<InvenTreeStockItem>();
|
||||
|
||||
String get _title {
|
||||
|
||||
if (location == null) {
|
||||
return "Stock Locations";
|
||||
} else {
|
||||
return "Stock Location - ${location.name}";
|
||||
}
|
||||
@override
|
||||
Future<void> onBuild(BuildContext context) async {
|
||||
refresh();
|
||||
}
|
||||
|
||||
/*
|
||||
* Request data from the server.
|
||||
* It will be displayed once loaded
|
||||
*
|
||||
* - List of sublocations under this one
|
||||
* - List of stock items at this location
|
||||
*/
|
||||
void _requestData() {
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
|
||||
int pk = location?.pk ?? -1;
|
||||
|
||||
// Request a list of sub-locations under this one
|
||||
InvenTreeStockLocation().list(filters: {"parent": "$pk"}).then((var locs) {
|
||||
InvenTreeStockLocation().list(context, filters: {"parent": "$pk"}).then((var locs) {
|
||||
_sublocations.clear();
|
||||
|
||||
for (var loc in locs) {
|
||||
@ -76,18 +68,18 @@ class _LocationDisplayState extends State<LocationDisplayWidget> {
|
||||
|
||||
setState(() {});
|
||||
|
||||
// Request a list of stock-items under this one
|
||||
InvenTreeStockItem().list(filters: {"location": "$pk"}).then((var items) {
|
||||
_items.clear();
|
||||
// Request a list of stock-items under this one
|
||||
InvenTreeStockItem().list(context, filters: {"location": "$pk"}).then((var items) {
|
||||
_items.clear();
|
||||
|
||||
for (var item in items) {
|
||||
if (item is InvenTreeStockItem) {
|
||||
_items.add(item);
|
||||
for (var item in items) {
|
||||
if (item is InvenTreeStockItem) {
|
||||
_items.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setState(() {});
|
||||
});
|
||||
setState(() {});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
@ -119,7 +111,7 @@ class _LocationDisplayState extends State<LocationDisplayWidget> {
|
||||
if (location.parentId < 0) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)));
|
||||
} else {
|
||||
InvenTreeStockLocation().get(location.parentId).then((var loc) {
|
||||
InvenTreeStockLocation().get(context, location.parentId).then((var loc) {
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc)));
|
||||
}
|
||||
@ -134,68 +126,63 @@ class _LocationDisplayState extends State<LocationDisplayWidget> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(_title),
|
||||
),
|
||||
drawer: new InvenTreeDrawer(context),
|
||||
body: ListView(
|
||||
children: <Widget> [
|
||||
locationDescriptionCard(),
|
||||
ExpansionPanelList(
|
||||
expansionCallback: (int index, bool isExpanded) {
|
||||
setState(() {
|
||||
switch (index) {
|
||||
case 0:
|
||||
InvenTreePreferences().expandLocationList = !isExpanded;
|
||||
break;
|
||||
case 1:
|
||||
InvenTreePreferences().expandStockList = !isExpanded;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
Widget getBody(BuildContext context) {
|
||||
|
||||
},
|
||||
children: <ExpansionPanel> [
|
||||
ExpansionPanel(
|
||||
headerBuilder: (BuildContext context, bool isExpanded) {
|
||||
return ListTile(
|
||||
title: Text("Sublocations"),
|
||||
leading: FaIcon(FontAwesomeIcons.mapMarkerAlt),
|
||||
trailing: Text("${_sublocations.length}"),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
InvenTreePreferences().expandLocationList = !InvenTreePreferences().expandLocationList;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
body: SublocationList(_sublocations),
|
||||
isExpanded: InvenTreePreferences().expandLocationList && _sublocations.length > 0,
|
||||
),
|
||||
ExpansionPanel(
|
||||
headerBuilder: (BuildContext context, bool isExpanded) {
|
||||
return ListTile(
|
||||
title: Text("Stock Items"),
|
||||
leading: FaIcon(FontAwesomeIcons.boxes),
|
||||
trailing: Text("${_items.length}"),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
InvenTreePreferences().expandStockList = !InvenTreePreferences().expandStockList;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
body: StockList(_items),
|
||||
isExpanded: InvenTreePreferences().expandStockList && _items.length > 0,
|
||||
)
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
return ListView(
|
||||
children: <Widget> [
|
||||
locationDescriptionCard(),
|
||||
ExpansionPanelList(
|
||||
expansionCallback: (int index, bool isExpanded) {
|
||||
setState(() {
|
||||
switch (index) {
|
||||
case 0:
|
||||
InvenTreePreferences().expandLocationList = !isExpanded;
|
||||
break;
|
||||
case 1:
|
||||
InvenTreePreferences().expandStockList = !isExpanded;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
children: <ExpansionPanel> [
|
||||
ExpansionPanel(
|
||||
headerBuilder: (BuildContext context, bool isExpanded) {
|
||||
return ListTile(
|
||||
title: Text("Sublocations"),
|
||||
leading: FaIcon(FontAwesomeIcons.mapMarkerAlt),
|
||||
trailing: Text("${_sublocations.length}"),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
InvenTreePreferences().expandLocationList = !InvenTreePreferences().expandLocationList;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
body: SublocationList(_sublocations),
|
||||
isExpanded: InvenTreePreferences().expandLocationList && _sublocations.length > 0,
|
||||
),
|
||||
ExpansionPanel(
|
||||
headerBuilder: (BuildContext context, bool isExpanded) {
|
||||
return ListTile(
|
||||
title: Text("Stock Items"),
|
||||
leading: FaIcon(FontAwesomeIcons.boxes),
|
||||
trailing: Text("${_items.length}"),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
InvenTreePreferences().expandStockList = !InvenTreePreferences().expandStockList;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
body: StockList(_items),
|
||||
isExpanded: InvenTreePreferences().expandStockList && _items.length > 0,
|
||||
)
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -208,7 +195,7 @@ class SublocationList extends StatelessWidget {
|
||||
|
||||
void _openLocation(BuildContext context, int pk) {
|
||||
|
||||
InvenTreeStockLocation().get(pk).then((var loc) {
|
||||
InvenTreeStockLocation().get(context, pk).then((var loc) {
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc)));
|
||||
@ -244,7 +231,7 @@ class StockList extends StatelessWidget {
|
||||
StockList(this._items);
|
||||
|
||||
void _openItem(BuildContext context, int pk) {
|
||||
InvenTreeStockItem().get(pk).then((var item) {
|
||||
InvenTreeStockItem().get(context, pk).then((var item) {
|
||||
if (item is InvenTreeStockItem) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => StockDetailWidget(item)));
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:InvenTree/api.dart';
|
||||
import 'package:InvenTree/widget/refreshable_state.dart';
|
||||
import 'package:InvenTree/widget/drawer.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
@ -21,7 +22,10 @@ class PartDetailWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
|
||||
class _PartDisplayState extends State<PartDetailWidget> {
|
||||
class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
|
||||
@override
|
||||
String getAppBarTitle(BuildContext context) { return "Part"; }
|
||||
|
||||
_PartDisplayState(this.part) {
|
||||
// TODO
|
||||
@ -29,6 +33,11 @@ class _PartDisplayState extends State<PartDetailWidget> {
|
||||
|
||||
InvenTreePart part;
|
||||
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
await part.reload(context);
|
||||
}
|
||||
|
||||
/*
|
||||
* Build a list of tiles to display under the part description
|
||||
*/
|
||||
@ -62,7 +71,7 @@ class _PartDisplayState extends State<PartDetailWidget> {
|
||||
leading: FaIcon(FontAwesomeIcons.stream),
|
||||
onTap: () {
|
||||
if (part.categoryId > 0) {
|
||||
InvenTreePartCategory().get(part.categoryId).then((var cat) {
|
||||
InvenTreePartCategory().get(context, part.categoryId).then((var cat) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => CategoryDisplayWidget(cat)));
|
||||
});
|
||||
@ -154,22 +163,11 @@ class _PartDisplayState extends State<PartDetailWidget> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Part Details"),
|
||||
Widget getBody(BuildContext context) {
|
||||
return Center(
|
||||
child: ListView(
|
||||
children: partTiles(),
|
||||
),
|
||||
drawer: new InvenTreeDrawer(context),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
child: FaIcon(FontAwesomeIcons.ellipsisH),
|
||||
// TODO - Add pop-up icons
|
||||
// Ref: https://stackoverflow.com/questions/46480221/flutter-floating-action-button-with-speed-dial#46480722
|
||||
),
|
||||
body: Center(
|
||||
child: ListView(
|
||||
children: partTiles(),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
66
lib/widget/refreshable_state.dart
Normal file
66
lib/widget/refreshable_state.dart
Normal file
@ -0,0 +1,66 @@
|
||||
import 'package:InvenTree/widget/drawer.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'package:InvenTree/widget/drawer.dart';
|
||||
|
||||
|
||||
abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
|
||||
|
||||
// Storage for context once "Build" is called
|
||||
BuildContext context;
|
||||
|
||||
String getAppBarTitle(BuildContext context) { return "App Bar Title"; }
|
||||
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => onBuild(context));
|
||||
}
|
||||
|
||||
// Function called after the widget is first build
|
||||
Future<void> onBuild(BuildContext context) async {
|
||||
return;
|
||||
}
|
||||
|
||||
// Function to request data for this page
|
||||
Future<void> request(BuildContext context) async {
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
await request(context);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
// Function to construct an appbar (override if needed)
|
||||
AppBar getAppBar(BuildContext context) {
|
||||
return AppBar(
|
||||
title: Text(getAppBarTitle(context))
|
||||
);
|
||||
}
|
||||
|
||||
// Function to construct a drawer (override if needed)
|
||||
Widget getDrawer(BuildContext context) {
|
||||
return InvenTreeDrawer(context);
|
||||
}
|
||||
|
||||
// Function to construct a body (MUST BE PROVIDED)
|
||||
Widget getBody(BuildContext context);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
// Save the context for future use
|
||||
this.context = context;
|
||||
|
||||
return Scaffold(
|
||||
appBar: getAppBar(context),
|
||||
drawer: getDrawer(context),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: refresh,
|
||||
child: getBody(context)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -4,12 +4,15 @@ import 'package:InvenTree/inventree/stock.dart';
|
||||
import 'package:InvenTree/inventree/part.dart';
|
||||
import 'package:InvenTree/widget/location_display.dart';
|
||||
import 'package:InvenTree/widget/part_detail.dart';
|
||||
import 'package:InvenTree/widget/refreshable_state.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:InvenTree/api.dart';
|
||||
|
||||
import 'package:InvenTree/widget/drawer.dart';
|
||||
import 'package:InvenTree/widget/refreshable_state.dart';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
|
||||
@ -25,7 +28,10 @@ class StockDetailWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
|
||||
class _StockItemDisplayState extends State<StockDetailWidget> {
|
||||
class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
|
||||
@override
|
||||
String getAppBarTitle(BuildContext context) { return "Stock Item"; }
|
||||
|
||||
final TextEditingController _quantityController = TextEditingController();
|
||||
final TextEditingController _notesController = TextEditingController();
|
||||
@ -42,13 +48,9 @@ class _StockItemDisplayState extends State<StockDetailWidget> {
|
||||
|
||||
final InvenTreeStockItem item;
|
||||
|
||||
/**
|
||||
* Function to reload the page data
|
||||
*/
|
||||
Future<void> _refresh() async {
|
||||
|
||||
await item.reload();
|
||||
setState(() {});
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
await item.reload(context);
|
||||
}
|
||||
|
||||
void _editStockItem() {
|
||||
@ -92,7 +94,7 @@ class _StockItemDisplayState extends State<StockDetailWidget> {
|
||||
_notesController.clear();
|
||||
|
||||
// TODO - Handle error cases
|
||||
_refresh();
|
||||
refresh();
|
||||
}
|
||||
|
||||
void _addStockDialog() async {
|
||||
@ -158,7 +160,7 @@ class _StockItemDisplayState extends State<StockDetailWidget> {
|
||||
|
||||
// TODO - Handle error cases
|
||||
|
||||
_refresh();
|
||||
refresh();
|
||||
}
|
||||
|
||||
void _removeStockDialog() {
|
||||
@ -227,7 +229,7 @@ class _StockItemDisplayState extends State<StockDetailWidget> {
|
||||
|
||||
// TODO - Handle error cases
|
||||
|
||||
_refresh();
|
||||
refresh();
|
||||
}
|
||||
|
||||
void _countStockDialog() async {
|
||||
@ -322,7 +324,7 @@ class _StockItemDisplayState extends State<StockDetailWidget> {
|
||||
leading: FaIcon(FontAwesomeIcons.shapes),
|
||||
onTap: () {
|
||||
if (item.partId > 0) {
|
||||
InvenTreePart().get(item.partId).then((var part) {
|
||||
InvenTreePart().get(context, item.partId).then((var part) {
|
||||
if (part is InvenTreePart) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
}
|
||||
@ -362,7 +364,7 @@ class _StockItemDisplayState extends State<StockDetailWidget> {
|
||||
leading: FaIcon(FontAwesomeIcons.mapMarkerAlt),
|
||||
onTap: () {
|
||||
if (item.locationId > 0) {
|
||||
InvenTreeStockLocation().get(item.locationId).then((var loc) {
|
||||
InvenTreeStockLocation().get(context, item.locationId).then((var loc) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => LocationDisplayWidget(loc)));
|
||||
});
|
||||
@ -464,26 +466,30 @@ class _StockItemDisplayState extends State<StockDetailWidget> {
|
||||
return buttons;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget getBody(BuildContext context) {
|
||||
return ListView(
|
||||
children: stockTiles()
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
this.context = context;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Stock Item"),
|
||||
),
|
||||
drawer: new InvenTreeDrawer(context),
|
||||
appBar: getAppBar(context),
|
||||
drawer: getDrawer(context),
|
||||
floatingActionButton: SpeedDial(
|
||||
visible: true,
|
||||
animatedIcon: AnimatedIcons.menu_close,
|
||||
heroTag: 'stock-item-fab',
|
||||
children: actionButtons(),
|
||||
),
|
||||
body: Center(
|
||||
child: new RefreshIndicator(
|
||||
onRefresh: _refresh,
|
||||
child: ListView(
|
||||
children: stockTiles(),
|
||||
)
|
||||
)
|
||||
body: RefreshIndicator(
|
||||
onRefresh: refresh,
|
||||
child: getBody(context)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user