diff --git a/android/app/build.gradle b/android/app/build.gradle index 29d8a3d..9c1dbee 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -41,8 +41,7 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.cdb_ui" + applicationId "de.hydrar.red.cdb" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion flutter.minSdkVersion diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index cf5af51..b1ca99c 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ + init() async { + Future prefetch() async { + // todo : prefetch + // fetch items + var resp = jsonDecode(await getRequest("$instance/items")); + var lst = resp["items"] as List; + items = lst.map((x) => Item(x)).toList(); + + // fetch locations + var locResp = jsonDecode(await getRequest("$instance/locations")) + as Map; + locations = locResp.map((key, value) => MapEntry(key, Location(value))); + + // fetch flowInfos + var flowResp = + jsonDecode(await getRequest("$instance/flows")) as Map; + + flowInfos = flowResp.map((key, value) => MapEntry(key, FlowInfo(value))); + } + + Future init(Function refresh) async { pref = await SharedPreferences.getInstance(); - instance = pref.getString("instance") ?? ""; + instance = pref!.getString("instance") ?? ""; + refresh(); } bool isInit() { - return pref.containsKey("token") && pref.containsKey("instance"); + if (pref == null) { + return false; + } + + return pref!.containsKey("token") && pref!.containsKey("instance"); + } + + bool isPrefetched() { + return items != null && locations != null && flowInfos != null; } void save(String instance, String token) { - pref.setString("instance", instance); - pref.setString("token", token); + pref!.setString("instance", instance); + pref!.setString("token", token); this.instance = instance; } @@ -41,7 +69,7 @@ class API { var resp = await http.get(Uri.parse(url), headers: { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json; charset=UTF-8', - 'Token': pref.getString("token")! + 'Token': pref!.getString("token")! }); return utf8.decode(resp.bodyBytes); @@ -52,7 +80,7 @@ class API { headers: { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json; charset=UTF-8', - 'Token': pref.getString("token")! + 'Token': pref!.getString("token")! }, body: jsonEncode(data)); @@ -60,14 +88,7 @@ class API { } // /items - Future> getItems() async { - if (items != null) { - return items!; - } - - var resp = jsonDecode(await getRequest("$instance/items")); - var lst = resp["items"] as List; - items = lst.map((x) => Item(x)).toList(); + List getItems() { return items!; } @@ -90,23 +111,12 @@ class API { return ret; } - Future> getLocations() async { - if (locations != null) { - return locations!; - } - - var resp = jsonDecode(await getRequest("$instance/locations")) - as Map; - locations = resp.map((key, value) => MapEntry(key, Location(value))); + Map getLocations() { return locations!; } - Future getItem(String item) async { - if (items != null) { - return items!.firstWhere((x) => x.id == item); - } - - return Item(jsonDecode(await getRequest("$instance/item/$item"))); + Item getItem(String item) { + return items!.firstWhere((x) => x.id == item); } Future getTransaction(String id) async { @@ -162,7 +172,7 @@ class API { } // /supply - Future supplyItem(String item, String variant, String price, + Future supplyItem(String item, String variant, double price, String? origin, String? location, String? note) async { if (origin!.isEmpty) { origin = null; @@ -191,13 +201,19 @@ class API { // /demand Future consumeItem( - String transaction, String destination, String price) async { + String transaction, String destination, double price) async { await postRequest("$instance/demand", {"uuid": transaction, "destination": destination, "price": price}); } // /item///stat - Future getStat(String item, String variant) async { + Future getStat(String item, String variant, + {bool full = false}) async { + if (full) { + return FullItemVariantStat(jsonDecode( + await getRequest("$instance/item/$item/$variant/stat?full=true"))); + } + return ItemVariantStat( jsonDecode(await getRequest("$instance/item/$item/$variant/stat"))); } @@ -207,7 +223,7 @@ class API { } // /item///price_history? - Future> getPriceHistory(String item, String variant, + Future> getPriceHistory(String item, String variant, {String? origin}) async { var url = "$instance/item/$item/$variant/price_history"; @@ -217,10 +233,10 @@ class API { var resp = jsonDecode(await getRequest(url)) as List; - return resp.map((x) => Price(x)).toList(); + return resp.map((x) => x as double).toList(); } - Future getLatestPrice(String item, String variant, + Future getLatestPrice(String item, String variant, {String? origin}) async { var url = "$instance/item/$item/$variant/price_latest"; @@ -230,31 +246,19 @@ class API { var resp = jsonDecode(await getRequest(url)) as Map; - return Price(resp); + return resp as double; } // Flows // /flows - Future> getFlows() async { - if (flowInfos != null) { - return flowInfos!; - } - - var resp = - jsonDecode(await getRequest("$instance/flows")) as Map; - - flowInfos = resp.map((key, value) => MapEntry(key, FlowInfo(value))); + Map getFlows() { return flowInfos!; } // /flow//info - Future getFlowInfo(String id) async { - if (flowInfos != null) { - return flowInfos![id]!; - } - - return FlowInfo(jsonDecode(await getRequest("$instance/flow/$id/info"))); + FlowInfo getFlowInfo(String id) { + return flowInfos![id]!; } Future getFlow(String id) async { @@ -277,12 +281,18 @@ class API { } // /flow//end - Future?> endFlow(String id, {List? produced}) async { + Future>?> endFlow(String id, + {List? produced}) async { var resp = jsonDecode(await postRequest("$instance/flow/$id/end", {"produced": produced?.map((x) => x.json()).toList()})); if (produced != null) { - return (resp["produced"] as List).cast(); + var produced = resp["produced"] as Map; + return produced.map( + (key, value) { + return MapEntry(key, (value as List).cast()); + }, + ); } return null; @@ -314,13 +324,8 @@ class API { "$instance/transaction/$id/move", {"to": newLocation})); } - Future getLocation(String id) async { - if (locations != null) { - return locations![id]!; - } - - var resp = jsonDecode(await getRequest("$instance/location/$id")); - return Location(resp); + Location getLocation(String id) { + return locations![id]!; } Future addNoteToFlow(String flowID, String content) async { @@ -356,12 +361,14 @@ class FlowInfo { class Item { late String id; + late String? image; late String name; late String? category; late Map variants; Item(Map json) { id = json["uuid"]; + image = json["image"]; name = json["name"]; category = json["category"]; variants = {}; @@ -378,6 +385,7 @@ class ItemVariant { late String name; int? min; int? expiry; + List? barcodes; ItemVariant(Map json) { item = json["item"]; @@ -385,26 +393,9 @@ class ItemVariant { name = json["name"]; min = json["min"]; expiry = json["expiry"]; - } -} - -class Price { - late double value; - late String currency; - - Price(Map json) { - value = json["value"]; - currency = json["currency"]; - } - - Price.fromString(String value) { - var priceSplit = value.split(" "); - this.value = double.parse(priceSplit[0]); - currency = priceSplit[1]; - } - - String format() { - return "${value.toStringAsFixed(2)} $currency"; + barcodes = json["barcodes"] != null + ? (json["barcodes"] as List).cast() + : null; } } @@ -412,7 +403,7 @@ class Transaction { late String uuid; late String item; late String variant; - late Price price; + late double price; String? origin; late int timestamp; ConsumeInfo? consumed; @@ -424,7 +415,7 @@ class Transaction { uuid = json["uuid"]; item = json["item"]; variant = json["variant"]; - price = Price(json["price"]); + price = json["price"]; origin = json["origin"]; timestamp = json["timestamp"]; expired = json["expired"]; @@ -433,12 +424,11 @@ class Transaction { location = json["location"] != null ? Location(json["location"]) : null; } - Transaction.inMemory(String itemID, String variantID, String price, + Transaction.inMemory(String itemID, String variantID, this.price, String? origin, Location? location, String? note) { uuid = ""; item = itemID; variant = variantID; - this.price = Price.fromString(price); origin = origin; timestamp = 0; consumed = null; @@ -450,12 +440,12 @@ class Transaction { class ConsumeInfo { late String destination; - late Price price; + late double price; late int timestamp; ConsumeInfo(Map json) { destination = json["destination"]; - price = Price(json["price"]); + price = json["price"]; timestamp = json["timestamp"]; } } @@ -563,14 +553,14 @@ class FlowNote { } class GlobalItemStat { - late int item_count; - late int total_transactions; - late double total_price; + late int itemCount; + late int totalTransactions; + late double totalPrice; GlobalItemStat(Map json) { - item_count = json["item_count"]; - total_transactions = json["total_transactions"]; - total_price = json["total_price"]; + itemCount = json["item_count"]; + totalTransactions = json["total_transactions"]; + totalPrice = json["total_price"]; } } @@ -578,3 +568,28 @@ class GlobalItemStat { var split = iv.split("::"); return (split[0], split[1]); } + +class FullItemVariantStat { + late int amount; + late double totalPrice; + late double expiryRate; + late Map origins; + + FullItemVariantStat(Map json) { + amount = json["amount"]; + totalPrice = json["total_price"]; + expiryRate = json["expiry_rate"]; + origins = (json["origins"] as Map) + .map((key, value) => MapEntry(key, OriginStat(value))); + } +} + +class OriginStat { + late double averagePrice; + late int inventory; + + OriginStat(Map json) { + averagePrice = json["average_price"]; + inventory = json["inventory"]; + } +} diff --git a/lib/main.dart b/lib/main.dart index 6df00dd..815427e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,24 +7,53 @@ import 'package:cdb_ui/pages/stats.dart'; import 'package:flutter/material.dart'; Future main() async { - await API().init(); runApp(const MyApp()); } -class MyApp extends StatelessWidget { +class MyApp extends StatefulWidget { const MyApp({super.key}); + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + bool init = false; + + refresh() { + setState(() {}); + } + + @override + void initState() { + super.initState(); + () async { + await API().init(refresh); + if (API().isInit()) { + await API().prefetch(); + setState(() { + init = true; + }); + } + }(); + } + @override Widget build(BuildContext context) { return MaterialApp( - title: 'CDB', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed( - seedColor: Colors.deepPurple, brightness: Brightness.dark), - useMaterial3: true, - ), - home: API().isInit() ? const MyHomePage() : const SetupPage(), - ); + title: 'CDB', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.deepPurple, brightness: Brightness.dark), + useMaterial3: true, + ), + home: API().isInit() + ? (API().isPrefetched() + ? const MyHomePage() + : const Scaffold( + body: Center(child: CircularProgressIndicator()), + )) + : const SetupPage()); } } diff --git a/lib/pages/consume.dart b/lib/pages/consume.dart index 9888126..d3f03e2 100644 --- a/lib/pages/consume.dart +++ b/lib/pages/consume.dart @@ -27,8 +27,8 @@ class _ConsumePageState extends State { _formKey.currentState!.save(); API() - .consumeItem( - widget.transaction.uuid, _selectedDestination, "$_price €") + .consumeItem(widget.transaction.uuid, _selectedDestination, + double.parse(_price)) .then((_) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Item consumed successfully!')), diff --git a/lib/pages/flow/active_flow_page.dart b/lib/pages/flow/active_flow_page.dart index d1f963e..d43b74e 100644 --- a/lib/pages/flow/active_flow_page.dart +++ b/lib/pages/flow/active_flow_page.dart @@ -56,7 +56,7 @@ class _ActiveFlowPageState extends State { ), ); - if (confirm) { + if (confirm ?? false) { await API .API() .endFlow(widget.flow.id) @@ -67,23 +67,22 @@ class _ActiveFlowPageState extends State { if (widget.info.next != null) IconButton( onPressed: () { - API.API().getFlowInfo(widget.info.next!).then((newInfo) { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) { - return CreateFlowPage( - newInfo, - () {}, - previousFlow: widget.flow, - ); - }, - )); - }); + var newInfo = API.API().getFlowInfo(widget.info.next!); + Navigator.of(context).push(MaterialPageRoute( + builder: (context) { + return CreateFlowPage( + newInfo, + () {}, + previousFlow: widget.flow, + ); + }, + )); }, icon: const Icon(Icons.arrow_forward)), ], ), body: Padding( - padding: EdgeInsets.symmetric(horizontal: 18.0), + padding: const EdgeInsets.symmetric(horizontal: 18.0), child: Column( children: [ Row( diff --git a/lib/pages/flow/create_flow_page.dart b/lib/pages/flow/create_flow_page.dart index 2d66a66..35d5fb2 100644 --- a/lib/pages/flow/create_flow_page.dart +++ b/lib/pages/flow/create_flow_page.dart @@ -25,11 +25,10 @@ class _CreateFlowPageState extends State { input: depends.map((x) => x.uuid).toList()) .then((x) { API.API().getFlow(x).then((flow) { - API.API().getFlowInfo(flow.kind).then((info) { - Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (context) => ActiveFlowPage(flow, info), - )); - }); + var info = API.API().getFlowInfo(flow.kind); + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (context) => ActiveFlowPage(flow, info), + )); }); }); return; @@ -65,26 +64,42 @@ class _CreateFlowPageState extends State { }); } - Widget buildInputSelection(BuildContext context) { - return Column( - children: widget.info.depends.map((x) { - return ElevatedButton( - onPressed: () { - selectDependItems(context, x); - }, - child: Text("Add $x")); - }).toList()); + List buildInputSelection(BuildContext context) { + if (widget.info.depends.isEmpty) { + return []; + } + + return [ + Column( + children: widget.info.depends.map((x) { + var (item, variant) = API.itemVariant(x); + return ElevatedButton( + onPressed: () { + selectDependItems(context, x); + }, + child: + Text("Add ${API.API().getItem(item).variants[variant]!.name}")); + }).toList()), + const Divider(), + ]; } Widget flowContinuation() { return Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ Card( - child: Text(widget.previousFlow!.kind), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(API.API().getFlowInfo(widget.previousFlow!.kind).name), + ), ), - const Text(" -> "), + const Card(child: Icon(Icons.arrow_right)), Card( - child: Text(widget.info.name), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(widget.info.name), + ), ) ], ); @@ -93,15 +108,17 @@ class _CreateFlowPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text("Create new ${widget.info.name} Flow")), + appBar: AppBar( + title: Text(widget.previousFlow != null + ? "Continue to ${widget.previousFlow!.kind}" + : "Create new ${widget.info.name} Flow")), body: Column( children: [ if (widget.previousFlow != null) ...[ flowContinuation(), const Divider() ], - buildInputSelection(context), - const Divider(), + ...buildInputSelection(context), Card( child: Column( children: depends diff --git a/lib/pages/flow/end_flow_page.dart b/lib/pages/flow/end_flow_page.dart index 212aaef..32f233d 100644 --- a/lib/pages/flow/end_flow_page.dart +++ b/lib/pages/flow/end_flow_page.dart @@ -1,5 +1,3 @@ -import 'dart:js_interop_unsafe'; - import 'package:cdb_ui/api.dart' as API; import 'package:cdb_ui/api.dart'; import 'package:cdb_ui/pages/supply.dart'; @@ -23,13 +21,7 @@ class _EndFlowWithProduceState extends State { @override void initState() { super.initState(); - API.API().getLocations().then( - (value) { - setState(() { - locations = value; - }); - }, - ); + locations = API.API().getLocations(); } refresh() { @@ -46,25 +38,24 @@ class _EndFlowWithProduceState extends State { List ret = []; for (var i in widget.info.produces!) { + var (itemID, variant) = API.itemVariant(i); + var item = API.API().getItem(itemID); ret.add(ElevatedButton( onPressed: () { - var (itemID, variant) = API.itemVariant(i); - API.API().getItem(itemID).then((item) { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) { - return SupplyPage( - item, - refresh, - onlyVariants: [variant], - forcePrice: "0.00", - forceOrigin: "flow::${widget.flow.kind}::${widget.flow.id}", - onCreate: addProduced, - ); - }, - )); - }); + Navigator.of(context).push(MaterialPageRoute( + builder: (context) { + return SupplyPage( + item, + refresh, + onlyVariants: [variant], + forcePrice: "0.00", + forceOrigin: "flow::${widget.flow.kind}::${widget.flow.id}", + onCreate: addProduced, + ); + }, + )); }, - child: Text("Produced $i"))); + child: Text("Produced ${item.variants[variant]!.name}"))); } return ret; diff --git a/lib/pages/flow/flow_info_page.dart b/lib/pages/flow/flow_info_page.dart index aea4b78..7ba14e7 100644 --- a/lib/pages/flow/flow_info_page.dart +++ b/lib/pages/flow/flow_info_page.dart @@ -24,13 +24,17 @@ class _FlowInfoPageState extends State { body: Column( children: [ // todo : ui improve - if (widget.info.next != null) Text("Next: ${widget.info.next}"), + if (widget.info.next != null) + Text("Next: ${API.API().getFlowInfo(widget.info.next!).name}"), if (widget.info.depends.isNotEmpty) Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ const Text("Flow can use: "), - ...widget.info.depends.map((x) => Text(x)).toList(), + ...widget.info.depends.map((x) { + var (item, variant) = API.itemVariant(x); + return Text(API.API().getItem(item).variants[variant]!.name); + }).toList(), ], ), if (widget.info.produces?.isNotEmpty ?? false) @@ -38,7 +42,10 @@ class _FlowInfoPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ const Text("Flow can produce: "), - ...widget.info.produces!.map((x) => Text(x)).toList(), + ...widget.info.produces!.map((x) { + var (item, variant) = API.itemVariant(x); + return Text(API.API().getItem(item).variants[variant]!.name); + }).toList(), ], ), const Divider(), diff --git a/lib/pages/flow/flows_page.dart b/lib/pages/flow/flows_page.dart index 8bcac95..f172bea 100644 --- a/lib/pages/flow/flows_page.dart +++ b/lib/pages/flow/flows_page.dart @@ -2,9 +2,8 @@ import 'package:cdb_ui/api.dart' as API; import 'package:cdb_ui/pages/expandable_list.dart'; import 'package:cdb_ui/pages/flow/active_flow_page.dart'; import 'package:cdb_ui/pages/flow/flow_info_page.dart'; -import 'package:cdb_ui/pages/transaction.dart'; +import 'package:cdb_ui/pages/supply.dart'; import 'package:flutter/material.dart'; -import 'package:qr_bar_code_scanner_dialog/qr_bar_code_scanner_dialog.dart'; class FlowsPage extends StatefulWidget { const FlowsPage({super.key}); @@ -20,13 +19,7 @@ class _FlowsPageState extends State { @override void initState() { super.initState(); - API.API().getFlows().then( - (value) { - setState(() { - flowInfos = value; - }); - }, - ); + flowInfos = API.API().getFlows(); } Widget flowTile(BuildContext context, API.FlowInfo x) { @@ -94,7 +87,8 @@ class _FlowsPageState extends State { children: producedMapping[key]!.map((x) { return flowTile(context, x); }).toList()); - items.add(ExpandableListItem(body: flows, header: Text(key))); + items.add(ExpandableListItem( + body: flows, header: Text(API.API().getItem(key).name))); } return ExpandableList(items); @@ -122,7 +116,8 @@ class _FlowsPageState extends State { children: dependsMapping[key]!.map((x) { return flowTile(context, x); }).toList()); - items.add(ExpandableListItem(body: flows, header: Text(key))); + items.add(ExpandableListItem( + body: flows, header: Text(API.API().getItem(key).name))); } return ExpandableList(items); @@ -160,25 +155,18 @@ class _FlowsPageState extends State { _ => const Text("..."), }, floatingActionButton: FloatingActionButton( - onPressed: () { + onPressed: () async { // scan flow code - QrBarCodeScannerDialog().getScannedQrBarCode( - context: context, - onCode: (code) { - // library is retarded - code = code!.replaceFirst("Code scanned = ", ""); + var code = await scanQRCode(context, title: "Scan Flow Code"); - API.API().getFlow(code).then((flow) { - API.API().getFlowInfo(flow.kind).then((info) { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) { - return ActiveFlowPage(flow, info); - }, - )); - }); - }); - }, - ); + API.API().getFlow(code!).then((flow) { + var info = API.API().getFlowInfo(flow.kind); + Navigator.of(context).push(MaterialPageRoute( + builder: (context) { + return ActiveFlowPage(flow, info); + }, + )); + }); }, child: const Icon(Icons.qr_code), ), diff --git a/lib/pages/items.dart b/lib/pages/items.dart index fa5db2f..b4daa87 100644 --- a/lib/pages/items.dart +++ b/lib/pages/items.dart @@ -1,48 +1,61 @@ import 'package:cdb_ui/api.dart'; import 'package:cdb_ui/pages/itemview.dart'; +import 'package:cdb_ui/pages/supply.dart'; import 'package:cdb_ui/pages/transaction.dart'; import 'package:flutter/material.dart'; -import 'package:qr_bar_code_scanner_dialog/qr_bar_code_scanner_dialog.dart'; class ItemsPage extends StatelessWidget { const ItemsPage({super.key}); + _scanBarcodeSupply(BuildContext context) async { + var code = + int.parse(await scanQRCode(context, title: "Scan Barcode") ?? "0"); + + var items = API().getItems(); + for (var item in items) { + for (var variant in item.variants.keys) { + for (var barcode in item.variants[variant]!.barcodes ?? []) { + if (code == barcode) { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => SupplyPage( + item, + () {}, + onlyVariants: [variant], + ))); + } + } + } + } + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Items"), + // todo : add barcode scan + actions: [ + ElevatedButton( + onPressed: () { + _scanBarcodeSupply(context); + }, + child: const Icon(Icons.add_box)) + ], ), - body: FutureBuilder( - future: API().getItems(), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const CircularProgressIndicator(); - } - - var items = snapshot.data!; - - return ListView( - children: items.map((x) { - return ItemTile(x); - }).toList()); - }), + body: ListView( + children: API().getItems().map((x) { + return ItemTile(x); + }).toList()), floatingActionButton: FloatingActionButton( - onPressed: () { + onPressed: () async { // scan transaction code - QrBarCodeScannerDialog().getScannedQrBarCode( - context: context, - onCode: (code) { - // library is retarded - code = code!.replaceFirst("Code scanned = ", ""); + var code = await scanQRCode(context, title: "Scan Transaction Code"); - API().getTransaction(code).then((t) { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => TransactionPage(t), - )); - }); - }, - ); + API().getTransaction(code!).then((t) { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => TransactionPage(t), + )); + }); }, child: const Icon(Icons.qr_code), ), diff --git a/lib/pages/itemview.dart b/lib/pages/itemview.dart index ab9e418..8ea7415 100644 --- a/lib/pages/itemview.dart +++ b/lib/pages/itemview.dart @@ -2,7 +2,6 @@ import 'package:cdb_ui/api.dart'; import 'package:cdb_ui/pages/stats.dart'; import 'package:cdb_ui/pages/transaction.dart'; import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; import 'supply.dart'; // todo : show est. time remaining until inventory gets empty (based on demand) @@ -33,7 +32,7 @@ class _ItemViewState extends State { builder: (context) => ItemStatPage(widget.item), )); }, - icon: Icon(Icons.bar_chart)) + icon: const Icon(Icons.bar_chart)) ], ), body: Column(children: [ @@ -44,12 +43,15 @@ class _ItemViewState extends State { Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - const Align( + Align( alignment: Alignment.centerLeft, - child: Placeholder( - fallbackWidth: 100, - fallbackHeight: 100, - ), + child: widget.item.image != null + ? Image.network( + "${API().instance}/${widget.item.image}", + height: 100, + width: 100, + ) + : null, ), const SizedBox( width: 16.0, diff --git a/lib/pages/locations.dart b/lib/pages/locations.dart index 7d00bd9..5e62a79 100644 --- a/lib/pages/locations.dart +++ b/lib/pages/locations.dart @@ -1,8 +1,8 @@ import 'package:cdb_ui/api.dart'; +import 'package:cdb_ui/pages/supply.dart'; import 'package:cdb_ui/pages/transaction.dart'; import 'package:flutter/material.dart'; import 'package:flutter_simple_treeview/flutter_simple_treeview.dart'; -import 'package:qr_bar_code_scanner_dialog/qr_bar_code_scanner_dialog.dart'; class LocationsPage extends StatefulWidget { const LocationsPage({super.key}); @@ -17,13 +17,7 @@ class _LocationsPageState extends State { @override void initState() { super.initState(); - API().getLocations().then( - (value) { - setState(() { - locations = value; - }); - }, - ); + locations = API().getLocations(); } TreeNode buildTree(BuildContext context, String locID) { @@ -69,25 +63,20 @@ class _LocationsPageState extends State { return buildTree(context, key); }).toList()), floatingActionButton: FloatingActionButton( - onPressed: () { + onPressed: () async { // scan location code - QrBarCodeScannerDialog().getScannedQrBarCode( - context: context, - onCode: (code) { - // library is retarded - code = code!.replaceFirst("Code scanned = ", ""); - if (!locations!.containsKey(code)) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('The location $code does not exist.')), - ); - return; - } + var code = await scanQRCode(context, title: "Scan Location Code"); - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => LocationView(locations![code]!), - )); - }, - ); + if (!locations!.containsKey(code)) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('The location $code does not exist.')), + ); + return; + } + + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => LocationView(locations![code]!), + )); }, child: const Icon(Icons.qr_code), ), @@ -118,7 +107,7 @@ class _LocationViewState extends State { title: Text(widget.location.name), ), body: Padding( - padding: EdgeInsets.symmetric(horizontal: 10.0), + padding: const EdgeInsets.symmetric(horizontal: 10.0), child: Column( children: [ Card( diff --git a/lib/pages/setup.dart b/lib/pages/setup.dart index 620413d..667861d 100644 --- a/lib/pages/setup.dart +++ b/lib/pages/setup.dart @@ -31,6 +31,8 @@ class _SetupPageState extends State { const SnackBar(content: Text('Setup Complete!')), ); + await API().prefetch(); + // Navigate or close the setup screen Navigator.pushReplacement( context, diff --git a/lib/pages/stats.dart b/lib/pages/stats.dart index b612280..80ecceb 100644 --- a/lib/pages/stats.dart +++ b/lib/pages/stats.dart @@ -1,4 +1,5 @@ import 'package:cdb_ui/api.dart'; +import 'package:cdb_ui/pages/expandable_list.dart'; import 'package:cdb_ui/pages/transaction.dart'; import 'package:flutter/material.dart'; @@ -23,6 +24,9 @@ class StatsPage extends StatelessWidget { // global origin / destinations return Scaffold( + appBar: AppBar( + title: const Text("Home"), + ), body: FutureBuilder( future: _fetchData(), builder: (context, snapshot) { @@ -45,26 +49,36 @@ class StatsPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconText(Icons.data_object, - "Items: ${globalStat.item_count}"), + "Items: ${globalStat.itemCount}"), IconText(Icons.list_alt, - "Inventory: ${globalStat.total_transactions}"), + "Inventory: ${globalStat.totalTransactions}"), IconText(Icons.money, - "Price: ${globalStat.total_price.toStringAsFixed(2)} €") + "Price: ${globalStat.totalPrice.toStringAsFixed(2)} €") ], ), )), if (min.isNotEmpty) - const ListTile(title: Text("Items under Minimum")), + const ListTile( + title: Text( + "Items under Minimum", + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), + )), ...min.map((item) { + var (itemID, variant) = itemVariant(item.itemVariant); + var name = API().getItem(itemID).variants[variant]!.name; + return ListTile( - title: Text( - "Item ${item.itemVariant} under minimum. Needs ${item.need} more."), + title: Text("$name under minimum. Needs ${item.need} more."), ); }).toList(), if (expired.isNotEmpty) - const ListTile(title: Text("Expired Items")), + const ListTile( + title: Text( + "Expired Items", + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), + )), // Mapping expired list to widgets ...expired.map((item) { @@ -85,16 +99,48 @@ class ItemStatPage extends StatelessWidget { const ItemStatPage(this.item, {super.key}); - // todo : expiry ratio // todo : avg time of transaction active + ExpandableListItem buildVariantStat(String variant) { + return ExpandableListItem( + body: FutureBuilder( + future: API().getStat(item.id, variant, full: true), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const CircularProgressIndicator(); + } + + var data = snapshot.data! as FullItemVariantStat; + + return Column( + children: [ + Text("Amount: ${data.amount}"), + Text("Total Cost: ${data.totalPrice}"), + Text("Expiry Rate: ${data.expiryRate}"), + ...data.origins.keys.map((key) { + var originStat = data.origins[key]!; + return Column(children: [ + Text("Inventory: ${originStat.inventory}"), + Text( + "Average Price: ${originStat.averagePrice.toStringAsFixed(2)} €"), + ]); + }).toList() + ], + ); + }, + ), + header: Text(item.variants[variant]!.name)); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Statistics of ${item.name}"), ), - body: null, + body: Column(children: [ + ExpandableList(item.variants.keys.map(buildVariantStat).toList()) + ]), ); } } diff --git a/lib/pages/supply.dart b/lib/pages/supply.dart index 342a352..792b766 100644 --- a/lib/pages/supply.dart +++ b/lib/pages/supply.dart @@ -1,6 +1,21 @@ import 'package:cdb_ui/api.dart'; import 'package:flutter/material.dart'; -import 'package:qr_bar_code_scanner_dialog/qr_bar_code_scanner_dialog.dart'; +import 'package:simple_barcode_scanner/enum.dart'; +import 'package:simple_barcode_scanner/simple_barcode_scanner.dart'; + +Future scanQRCode(BuildContext context, + {String title = "Scan QR Code"}) async { + var res = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SimpleBarcodeScannerPage( + scanType: ScanType.qr, + appBarTitle: title, + ), + )); + + return res; +} class SupplyPage extends StatefulWidget { final Item item; @@ -53,7 +68,7 @@ class _SupplyPageState extends State { var t = SupplyForm( itemID: widget.item.id, variant: variant, - price: "${_priceController.text} €", + price: double.parse(_priceController.text), origin: _selectedOrigin, location: _selectedLocation, note: _noteController.text); @@ -66,8 +81,13 @@ class _SupplyPageState extends State { } API() - .supplyItem(widget.item.id, variant, "${_priceController.text} €", - _selectedOrigin, _selectedLocation, _noteController.text) + .supplyItem( + widget.item.id, + variant, + double.parse(_priceController.text), + _selectedOrigin, + _selectedLocation, + _noteController.text) .then((_) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Item added successfully!')), @@ -126,7 +146,7 @@ class _SupplyPageState extends State { const SizedBox(height: 16), // Origin Field with Dropdown and Text Input - if (widget.forceOrigin == null) + if (widget.forceOrigin == null) ...[ AutocompletedTextField( options: origins, getValue: () => _selectedOrigin, @@ -138,7 +158,7 @@ class _SupplyPageState extends State { ? await API() .getLatestPrice(widget.item.id, variant, origin: selection) - .then((x) => x.value.toStringAsFixed(2)) + .then((x) => x.toStringAsFixed(2)) : _priceController.text; setState(() { _priceController.text = price; @@ -146,11 +166,11 @@ class _SupplyPageState extends State { }); }, label: "Origin"), - - const SizedBox(height: 16), + const SizedBox(height: 16), + ], // Price Field - if (widget.forcePrice == null) + if (widget.forcePrice == null) ...[ TextFormField( decoration: const InputDecoration(labelText: 'Price'), keyboardType: TextInputType.number, @@ -165,8 +185,8 @@ class _SupplyPageState extends State { return null; }, ), - - const SizedBox(height: 16), + const SizedBox(height: 16), + ], // Location Dropdown Row( @@ -197,15 +217,13 @@ class _SupplyPageState extends State { width: 12, ), IconButton( - onPressed: () { - QrBarCodeScannerDialog().getScannedQrBarCode( - context: context, - onCode: (code) { - setState(() { - _selectedLocation = code!; - }); - }, - ); + onPressed: () async { + var code = await scanQRCode(context); + setState(() { + if (API().getLocations().keys.contains(code)) { + _selectedLocation = code!; + } + }); }, icon: const Icon(Icons.qr_code), ), @@ -235,7 +253,7 @@ class _SupplyPageState extends State { } Future> _fetchData() async { - var locations = await API().getLocations(); + var locations = API().getLocations(); var origins = await API().getUniqueField(widget.item.id, variant, "origin"); origins.insert(0, ""); locations[""] = Location.zero(); @@ -293,7 +311,7 @@ class AutocompletedTextField extends StatelessWidget { class SupplyForm { final String itemID; final String variant; - final String price; + final double price; final String? origin; final String? location; final String note; diff --git a/lib/pages/transaction.dart b/lib/pages/transaction.dart index a7d2d62..79e674d 100644 --- a/lib/pages/transaction.dart +++ b/lib/pages/transaction.dart @@ -1,9 +1,9 @@ import 'package:cdb_ui/api.dart'; import 'package:cdb_ui/pages/consume.dart'; +import 'package:cdb_ui/pages/supply.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:qr_bar_code/qr/qr.dart'; -import 'package:qr_bar_code_scanner_dialog/qr_bar_code_scanner_dialog.dart'; class TransactionPage extends StatefulWidget { final Transaction transaction; @@ -40,84 +40,73 @@ class _TransactionPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text(widget.transaction.item), + title: Text(API().getItem(transaction.item).name), actions: [ IconButton( onPressed: () { - API().getLocations().then((locations) { - List locationList = locations.keys.toList(); - String? selectedLocationID; + var locations = API().getLocations(); + List locationList = locations.keys.toList(); + String? selectedLocationID; + + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Move Transaction'), + content: Row( + mainAxisSize: MainAxisSize.min, + children: [ + DropdownButton( + value: selectedLocationID, + onChanged: (value) { + selectedLocationID = value!; + API() + .moveTransaction(widget.transaction.uuid, + selectedLocationID!) + .then((x) { + Navigator.of(context).pop(); + }); + + setState(() {}); + }, + items: locationList + .map>((locationID) { + return DropdownMenuItem( + value: locationID, + child: Text(locations[locationID]!.name), + ); + }).toList(), + ), + IconButton( + onPressed: () async { + var locations = API().getLocations(); + var code = await scanQRCode(context, + title: "Scan Location Code"); + if (!locations.containsKey(code)) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'The location $code does not exist.')), + ); + return; + } - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('Move Transaction'), - content: Row( - mainAxisSize: MainAxisSize.min, - children: [ - DropdownButton( - value: selectedLocationID, - onChanged: (value) { - selectedLocationID = value!; API() .moveTransaction(widget.transaction.uuid, selectedLocationID!) - .then((x) { - Navigator.of(context).pop(); - }); - + .then( + (x) { + Navigator.of(context).pop(); + }, + ); setState(() {}); }, - items: locationList - .map>((locationID) { - return DropdownMenuItem( - value: locationID, - child: Text(locations[locationID]!.name), - ); - }).toList(), - ), - IconButton( - onPressed: () { - API().getLocations().then((locations) { - QrBarCodeScannerDialog() - .getScannedQrBarCode( - context: context, - onCode: (code) { - // library is retarded - code = code!.replaceFirst( - "Code scanned = ", ""); - if (!locations - .containsKey(code)) { - ScaffoldMessenger.of(context) - .showSnackBar( - SnackBar( - content: Text( - 'The location $code does not exist.')), - ); - return; - } - - API() - .moveTransaction( - widget.transaction.uuid, - selectedLocationID!) - .then( - (x) { - Navigator.of(context).pop(); - }, - ); - }); - }); - setState(() {}); - }, - icon: const Icon(Icons.qr_code)) - ], - ), - ); - }, - ); - }); + icon: const Icon(Icons.qr_code)) + ], + ), + ); + }, + ); }, icon: const Icon(Icons.move_up)) ], @@ -143,12 +132,15 @@ class _TransactionPageState extends State { children: [ // todo : human names Text( - "${transaction.item}", + API().getItem(transaction.item).name, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 28), ), Text( - "${transaction.variant}", + API() + .getItem(transaction.item) + .variants[transaction.variant]! + .name, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 20, @@ -169,7 +161,7 @@ class _TransactionPageState extends State { if (transaction.expired) const Text("Transaction is Expired!"), - IconText(Icons.money, transaction.price.format(), + IconText(Icons.money, transaction.price.toStringAsFixed(2), color: Colors.green), if (transaction.origin != null) @@ -185,7 +177,8 @@ class _TransactionPageState extends State { Text("Consumed on: ${tsFormat(transaction.consumed!.timestamp)}"), IconText(Icons.store, transaction.consumed!.destination, color: Colors.blue), - IconText(Icons.money, transaction.consumed!.price.format(), + IconText( + Icons.money, transaction.consumed!.price.toStringAsFixed(2), color: Colors.green), ] @@ -262,14 +255,14 @@ class TransactionCard extends StatelessWidget { Row( children: [ Text( - t.item, + API().getItem(t.item).name, style: const TextStyle(fontSize: 16), ), const SizedBox( - width: 4, + width: 8, ), Text( - t.variant, + API().getItem(t.item).variants[t.variant]!.name, style: TextStyle(fontSize: 14, color: Colors.grey[400]), ), ], @@ -290,8 +283,7 @@ class TransactionCard extends StatelessWidget { const SizedBox( height: 10, ), - IconText(Icons.money, - "${t.price.value.toStringAsFixed(2)} ${t.price.currency}", + IconText(Icons.money, "${t.price.toStringAsFixed(2)} €", color: Colors.green), if (t.origin != null) ...[ const SizedBox(height: 8), @@ -332,6 +324,7 @@ class IconText extends StatelessWidget { Text( text, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + overflow: TextOverflow.fade, ), ], ); @@ -358,29 +351,28 @@ class TransactionSelectPage extends StatelessWidget { selectionList.add(s); } - if (selectionList.isEmpty) { - Navigator.of(context).pop(); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('No Transactions to select')), - ); - } - return Scaffold( appBar: AppBar( title: const Text("Select a Transaction"), ), body: ListView( - children: selectionList - .map((x) => TransactionCard( - x, - () {}, - onLongPress: (x) {}, - onTap: (t) { - onSelect(t); - Navigator.of(context).pop(); - }, - )) - .toList()), + children: selectionList.isEmpty + ? [ + const ListTile( + title: Center(child: Text("No Transactions available")), + ) + ] + : selectionList + .map((x) => TransactionCard( + x, + () {}, + onLongPress: (x) {}, + onTap: (t) { + onSelect(t); + Navigator.of(context).pop(); + }, + )) + .toList()), ); } } diff --git a/pubspec.lock b/pubspec.lock index 8ec77fd..92bae6e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -94,6 +94,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_barcode_scanner: + dependency: transitive + description: + name: flutter_barcode_scanner + sha256: a4ba37daf9933f451a5e812c753ddd045d6354e4a3280342d895b07fecaab3fa + url: "https://pub.dev" + source: hosted + version: "2.0.0" flutter_lints: dependency: "direct dev" description: @@ -102,6 +110,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "9ee02950848f61c4129af3d6ec84a1cfc0e47931abc746b03e7a3bc3e8ff6eda" + url: "https://pub.dev" + source: hosted + version: "2.0.22" flutter_simple_treeview: dependency: "direct main" description: @@ -124,10 +140,10 @@ packages: dependency: "direct main" description: name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" http_parser: dependency: transitive description: @@ -144,30 +160,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.18.1" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -196,18 +204,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" path: dependency: transitive description: @@ -240,6 +248,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + permission_handler: + dependency: transitive + description: + name: permission_handler + sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb" + url: "https://pub.dev" + source: hosted + version: "11.3.1" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "76e4ab092c1b240d31177bb64d2b0bea43f43d0e23541ec866151b9f7b2490fa" + url: "https://pub.dev" + source: hosted + version: "12.0.12" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0 + url: "https://pub.dev" + source: hosted + version: "9.4.5" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: af26edbbb1f2674af65a8f4b56e1a6f526156bc273d0e65dd8075fab51c78851 + url: "https://pub.dev" + source: hosted + version: "0.1.3+2" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9 + url: "https://pub.dev" + source: hosted + version: "4.2.3" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" platform: dependency: transitive description: @@ -264,22 +320,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" - qr_bar_code_scanner_dialog: - dependency: "direct main" - description: - name: qr_bar_code_scanner_dialog - sha256: dcb937816d4e562141530265bd1ca39fe00f57000fd79e26c163c957d443e9e4 - url: "https://pub.dev" - source: hosted - version: "0.0.5" - qr_code_scanner: - dependency: transitive - description: - name: qr_code_scanner - sha256: f23b68d893505a424f0bd2e324ebea71ed88465d572d26bb8d2e78a4749591fd - url: "https://pub.dev" - source: hosted - version: "1.0.1" shared_preferences: dependency: "direct main" description: @@ -336,6 +376,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.1" + simple_barcode_scanner: + dependency: "direct main" + description: + name: simple_barcode_scanner + sha256: "52b30082ebd6fab1e6314cb9bfc1aca5372890616dcb89d0e254edf7b7ef4951" + url: "https://pub.dev" + source: hosted + version: "0.1.2" sky_engine: dependency: transitive description: flutter @@ -385,10 +433,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" typed_data: dependency: transitive description: @@ -409,18 +457,26 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.5" web: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "1.1.0" + webview_windows: + dependency: transitive + description: + name: webview_windows + sha256: "47fcad5875a45db29dbb5c9e6709bf5c88dcc429049872701343f91ed7255730" + url: "https://pub.dev" + source: hosted + version: "0.4.0" xdg_directories: dependency: transitive description: @@ -430,5 +486,5 @@ packages: source: hosted version: "1.0.4" sdks: - dart: ">=3.4.0 <4.0.0" + dart: ">=3.5.3 <4.0.0" flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index c19fe54..461ce7e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: cdb_ui -description: A new Flutter project. +description: Economic Database. # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev @@ -32,7 +32,7 @@ dependencies: sdk: flutter flutter_simple_treeview: ^3.0.2 qr_bar_code: ^1.3.0 - qr_bar_code_scanner_dialog: ^0.0.5 + simple_barcode_scanner: ^0.1.2 intl: ^0.18.0 shared_preferences: ^2.1.0 fl_chart: ^0.69.0 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index e8bdf58..966ec1a 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,9 +6,15 @@ #include "generated_plugin_registrant.h" +#include #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); QrBarCodePluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("QrBarCodePluginCApi")); + WebviewWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WebviewWindowsPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 6108823..038df8f 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,7 +3,9 @@ # list(APPEND FLUTTER_PLUGIN_LIST + permission_handler_windows qr_bar_code + webview_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST