Compare commits

...

10 commits

Author SHA1 Message Date
e366960ce7
fix 2024-10-19 21:15:02 +02:00
7ee1cda550
image 2024-10-18 11:02:28 +02:00
61134f7b63
fix 2024-10-08 13:47:17 +02:00
0e2ccb9e54
add barcode quick add 2024-10-08 10:55:30 +02:00
fcecb5aad5
update 2024-10-08 10:11:18 +02:00
b13b385cb7
update 2024-10-07 21:48:48 +02:00
d21554cddc
update stat 2024-09-27 13:15:53 +02:00
167a4620fe
api 2024-09-27 09:27:29 +02:00
2f24e02696
sync api + ui refactor 2024-09-26 21:35:28 +02:00
a608c50447
flow update 2024-09-26 21:01:45 +02:00
21 changed files with 601 additions and 429 deletions

View file

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

View file

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="cdb_ui"
android:label="CDB"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity

View file

@ -6,7 +6,7 @@ import 'package:shared_preferences/shared_preferences.dart';
// todo : api errors
class API {
late SharedPreferences pref;
SharedPreferences? pref;
static final API _instance = API._internal();
// cache
@ -20,18 +20,46 @@ class API {
API._internal();
Future<void> init() async {
Future<void> prefetch() async {
// todo : prefetch
// fetch items
var resp = jsonDecode(await getRequest("$instance/items"));
var lst = resp["items"] as List<dynamic>;
items = lst.map((x) => Item(x)).toList();
// fetch locations
var locResp = jsonDecode(await getRequest("$instance/locations"))
as Map<String, dynamic>;
locations = locResp.map((key, value) => MapEntry(key, Location(value)));
// fetch flowInfos
var flowResp =
jsonDecode(await getRequest("$instance/flows")) as Map<String, dynamic>;
flowInfos = flowResp.map((key, value) => MapEntry(key, FlowInfo(value)));
}
Future<void> 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: <String, String>{
'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: <String, String>{
'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<List<Item>> getItems() async {
if (items != null) {
return items!;
}
var resp = jsonDecode(await getRequest("$instance/items"));
var lst = resp["items"] as List<dynamic>;
items = lst.map((x) => Item(x)).toList();
List<Item> getItems() {
return items!;
}
@ -90,23 +111,12 @@ class API {
return ret;
}
Future<Map<String, Location>> getLocations() async {
if (locations != null) {
return locations!;
}
var resp = jsonDecode(await getRequest("$instance/locations"))
as Map<String, dynamic>;
locations = resp.map((key, value) => MapEntry(key, Location(value)));
Map<String, Location> getLocations() {
return locations!;
}
Future<Item> 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<Transaction> getTransaction(String id) async {
@ -162,7 +172,7 @@ class API {
}
// /supply
Future<String> supplyItem(String item, String variant, String price,
Future<String> 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<void> 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/<item_id>/<variant_id>/stat
Future<ItemVariantStat> getStat(String item, String variant) async {
Future<dynamic> 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/<item_id>/<variant_id>/price_history?<origin>
Future<List<Price>> getPriceHistory(String item, String variant,
Future<List<double>> 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<dynamic>;
return resp.map((x) => Price(x)).toList();
return resp.map((x) => x as double).toList();
}
Future<Price> getLatestPrice(String item, String variant,
Future<double> 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<String, dynamic>;
return Price(resp);
return resp as double;
}
// Flows
// /flows
Future<Map<String, FlowInfo>> getFlows() async {
if (flowInfos != null) {
return flowInfos!;
}
var resp =
jsonDecode(await getRequest("$instance/flows")) as Map<String, dynamic>;
flowInfos = resp.map((key, value) => MapEntry(key, FlowInfo(value)));
Map<String, FlowInfo> getFlows() {
return flowInfos!;
}
// /flow/<id>/info
Future<FlowInfo> 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<Flow> getFlow(String id) async {
@ -277,12 +281,18 @@ class API {
}
// /flow/<id>/end
Future<List<String>?> endFlow(String id, {List<SupplyForm>? produced}) async {
Future<Map<String, List<String>>?> endFlow(String id,
{List<SupplyForm>? 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<dynamic>).cast<String>();
var produced = resp["produced"] as Map<String, dynamic>;
return produced.map(
(key, value) {
return MapEntry(key, (value as List<dynamic>).cast<String>());
},
);
}
return null;
@ -314,13 +324,8 @@ class API {
"$instance/transaction/$id/move", {"to": newLocation}));
}
Future<Location> 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<String> 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<String, ItemVariant> variants;
Item(Map<String, dynamic> json) {
id = json["uuid"];
image = json["image"];
name = json["name"];
category = json["category"];
variants = <String, ItemVariant>{};
@ -378,6 +385,7 @@ class ItemVariant {
late String name;
int? min;
int? expiry;
List<int>? barcodes;
ItemVariant(Map<String, dynamic> 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<String, dynamic> 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<dynamic>).cast<int>()
: 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<String, dynamic> 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<String, dynamic> 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<String, OriginStat> origins;
FullItemVariantStat(Map<String, dynamic> json) {
amount = json["amount"];
totalPrice = json["total_price"];
expiryRate = json["expiry_rate"];
origins = (json["origins"] as Map<String, dynamic>)
.map((key, value) => MapEntry(key, OriginStat(value)));
}
}
class OriginStat {
late double averagePrice;
late int inventory;
OriginStat(Map<String, dynamic> json) {
averagePrice = json["average_price"];
inventory = json["inventory"];
}
}

View file

@ -7,24 +7,53 @@ import 'package:cdb_ui/pages/stats.dart';
import 'package:flutter/material.dart';
Future<void> main() async {
await API().init();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
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());
}
}

View file

@ -27,8 +27,8 @@ class _ConsumePageState extends State<ConsumePage> {
_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!')),

View file

@ -56,7 +56,7 @@ class _ActiveFlowPageState extends State<ActiveFlowPage> {
),
);
if (confirm) {
if (confirm ?? false) {
await API
.API()
.endFlow(widget.flow.id)
@ -67,23 +67,22 @@ class _ActiveFlowPageState extends State<ActiveFlowPage> {
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(

View file

@ -25,11 +25,10 @@ class _CreateFlowPageState extends State<CreateFlowPage> {
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<CreateFlowPage> {
});
}
Widget buildInputSelection(BuildContext context) {
return Column(
children: widget.info.depends.map((x) {
return ElevatedButton(
onPressed: () {
selectDependItems(context, x);
},
child: Text("Add $x"));
}).toList());
List<Widget> 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<CreateFlowPage> {
@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

View file

@ -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<EndFlowWithProduce> {
@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<EndFlowWithProduce> {
List<Widget> 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;

View file

@ -24,13 +24,17 @@ class _FlowInfoPageState extends State<FlowInfoPage> {
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<FlowInfoPage> {
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(),

View file

@ -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<FlowsPage> {
@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<FlowsPage> {
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<FlowsPage> {
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<FlowsPage> {
_ => 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),
),

View file

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

View file

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

View file

@ -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<LocationsPage> {
@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<LocationsPage> {
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<LocationView> {
title: Text(widget.location.name),
),
body: Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0),
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Column(
children: [
Card(

View file

@ -31,6 +31,8 @@ class _SetupPageState extends State<SetupPage> {
const SnackBar(content: Text('Setup Complete!')),
);
await API().prefetch();
// Navigate or close the setup screen
Navigator.pushReplacement(
context,

View file

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

View file

@ -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<String?> 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<SupplyPage> {
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<SupplyPage> {
}
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<SupplyPage> {
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<SupplyPage> {
? 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<SupplyPage> {
});
},
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<SupplyPage> {
return null;
},
),
const SizedBox(height: 16),
const SizedBox(height: 16),
],
// Location Dropdown
Row(
@ -197,15 +217,13 @@ class _SupplyPageState extends State<SupplyPage> {
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<SupplyPage> {
}
Future<Map<String, dynamic>> _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;

View file

@ -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<TransactionPage> {
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<String> locationList = locations.keys.toList();
String? selectedLocationID;
var locations = API().getLocations();
List<String> 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<String>(
value: selectedLocationID,
onChanged: (value) {
selectedLocationID = value!;
API()
.moveTransaction(widget.transaction.uuid,
selectedLocationID!)
.then((x) {
Navigator.of(context).pop();
});
setState(() {});
},
items: locationList
.map<DropdownMenuItem<String>>((locationID) {
return DropdownMenuItem<String>(
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<String>(
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<DropdownMenuItem<String>>((locationID) {
return DropdownMenuItem<String>(
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<TransactionPage> {
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<TransactionPage> {
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<TransactionPage> {
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()),
);
}
}

View file

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

View file

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

View file

@ -6,9 +6,15 @@
#include "generated_plugin_registrant.h"
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <qr_bar_code/qr_bar_code_plugin_c_api.h>
#include <webview_windows/webview_windows_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
QrBarCodePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("QrBarCodePluginCApi"));
WebviewWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WebviewWindowsPlugin"));
}

View file

@ -3,7 +3,9 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
permission_handler_windows
qr_bar_code
webview_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST