feat: Adds product image field to data models

Includes an `image` field in the `ProductStruct` and `ProductEntity` data models.

Updates the ObjectBox schema and regenerates the necessary code to persist the new field.

Refactors the `ProductScannedComponent` to accept a `ProductStruct` object, simplifying parameter passing.

Updates the `ScannerPage` to build and pass the `ProductStruct` to the component, enabling the display of the product image and using the structured data for other details like quantity. The 'Marque' display is replaced with 'Quantité' in the component.
This commit is contained in:
mandreshope 2025-07-03 17:20:44 +03:00
parent 8eddb5d4b7
commit 44289e29dc
9 changed files with 79 additions and 37 deletions

View File

@ -12,6 +12,7 @@ class ProductEntity {
String? description;
String? price;
String? quantity;
String? image;
ProductEntity({
this.id = 0,
@ -20,6 +21,7 @@ class ProductEntity {
this.description,
this.price,
this.quantity,
this.image,
});
/// Convertir vers ProductStruct
@ -31,6 +33,7 @@ class ProductEntity {
description: description,
price: price,
quantity: quantity,
image: image,
);
}
@ -43,6 +46,7 @@ class ProductEntity {
description: struct.description,
price: struct.price,
quantity: struct.quantity,
image: struct.image,
);
}
}

View File

@ -5,7 +5,7 @@
"entities": [
{
"id": "1:6757833172062715556",
"lastPropertyId": "6:7033704955625644592",
"lastPropertyId": "7:1825580906382154543",
"name": "ProductEntity",
"properties": [
{
@ -38,6 +38,11 @@
"id": "6:7033704955625644592",
"name": "quantity",
"type": 9
},
{
"id": "7:1825580906382154543",
"name": "image",
"type": 9
}
],
"relations": []

View File

@ -22,7 +22,7 @@ final _entities = <obx_int.ModelEntity>[
obx_int.ModelEntity(
id: const obx_int.IdUid(1, 6757833172062715556),
name: 'ProductEntity',
lastPropertyId: const obx_int.IdUid(6, 7033704955625644592),
lastPropertyId: const obx_int.IdUid(7, 1825580906382154543),
flags: 0,
properties: <obx_int.ModelProperty>[
obx_int.ModelProperty(
@ -61,6 +61,12 @@ final _entities = <obx_int.ModelEntity>[
type: 9,
flags: 0,
),
obx_int.ModelProperty(
id: const obx_int.IdUid(7, 1825580906382154543),
name: 'image',
type: 9,
flags: 0,
),
],
relations: <obx_int.ModelRelation>[],
backlinks: <obx_int.ModelBacklink>[],
@ -143,13 +149,17 @@ obx_int.ModelDefinition getObjectBoxModel() {
final quantityOffset = object.quantity == null
? null
: fbb.writeString(object.quantity!);
fbb.startTable(7);
final imageOffset = object.image == null
? null
: fbb.writeString(object.image!);
fbb.startTable(8);
fbb.addInt64(0, object.id);
fbb.addOffset(1, codeOffset);
fbb.addOffset(2, nameOffset);
fbb.addOffset(3, descriptionOffset);
fbb.addOffset(4, priceOffset);
fbb.addOffset(5, quantityOffset);
fbb.addOffset(6, imageOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -177,6 +187,9 @@ obx_int.ModelDefinition getObjectBoxModel() {
final quantityParam = const fb.StringReader(
asciiOptimization: true,
).vTableGetNullable(buffer, rootOffset, 14);
final imageParam = const fb.StringReader(
asciiOptimization: true,
).vTableGetNullable(buffer, rootOffset, 16);
final object = ProductEntity(
id: idParam,
code: codeParam,
@ -184,6 +197,7 @@ obx_int.ModelDefinition getObjectBoxModel() {
description: descriptionParam,
price: priceParam,
quantity: quantityParam,
image: imageParam,
);
return object;
@ -225,4 +239,9 @@ class ProductEntity_ {
static final quantity = obx.QueryStringProperty<ProductEntity>(
_entities[0].properties[5],
);
/// See [ProductEntity.image].
static final image = obx.QueryStringProperty<ProductEntity>(
_entities[0].properties[6],
);
}

View File

@ -12,6 +12,7 @@ abstract class ProductStruct with _$ProductStruct {
String? description,
String? price,
String? quantity,
String? image,
}) = _ProductStruct;
factory ProductStruct.fromJson(Map<String, dynamic> json) =>

View File

@ -16,7 +16,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ProductStruct {
int get id; String? get code; String? get name; String? get description; String? get price; String? get quantity;
int get id; String? get code; String? get name; String? get description; String? get price; String? get quantity; String? get image;
/// Create a copy of ProductStruct
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@ -29,16 +29,16 @@ $ProductStructCopyWith<ProductStruct> get copyWith => _$ProductStructCopyWithImp
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProductStruct&&(identical(other.id, id) || other.id == id)&&(identical(other.code, code) || other.code == code)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.price, price) || other.price == price)&&(identical(other.quantity, quantity) || other.quantity == quantity));
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProductStruct&&(identical(other.id, id) || other.id == id)&&(identical(other.code, code) || other.code == code)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.price, price) || other.price == price)&&(identical(other.quantity, quantity) || other.quantity == quantity)&&(identical(other.image, image) || other.image == image));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,code,name,description,price,quantity);
int get hashCode => Object.hash(runtimeType,id,code,name,description,price,quantity,image);
@override
String toString() {
return 'ProductStruct(id: $id, code: $code, name: $name, description: $description, price: $price, quantity: $quantity)';
return 'ProductStruct(id: $id, code: $code, name: $name, description: $description, price: $price, quantity: $quantity, image: $image)';
}
@ -49,7 +49,7 @@ abstract mixin class $ProductStructCopyWith<$Res> {
factory $ProductStructCopyWith(ProductStruct value, $Res Function(ProductStruct) _then) = _$ProductStructCopyWithImpl;
@useResult
$Res call({
int id, String? code, String? name, String? description, String? price, String? quantity
int id, String? code, String? name, String? description, String? price, String? quantity, String? image
});
@ -66,7 +66,7 @@ class _$ProductStructCopyWithImpl<$Res>
/// Create a copy of ProductStruct
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? code = freezed,Object? name = freezed,Object? description = freezed,Object? price = freezed,Object? quantity = freezed,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? code = freezed,Object? name = freezed,Object? description = freezed,Object? price = freezed,Object? quantity = freezed,Object? image = freezed,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as int,code: freezed == code ? _self.code : code // ignore: cast_nullable_to_non_nullable
@ -74,6 +74,7 @@ as String?,name: freezed == name ? _self.name : name // ignore: cast_nullable_to
as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,price: freezed == price ? _self.price : price // ignore: cast_nullable_to_non_nullable
as String?,quantity: freezed == quantity ? _self.quantity : quantity // ignore: cast_nullable_to_non_nullable
as String?,image: freezed == image ? _self.image : image // ignore: cast_nullable_to_non_nullable
as String?,
));
}
@ -85,7 +86,7 @@ as String?,
@JsonSerializable()
class _ProductStruct implements ProductStruct {
_ProductStruct({this.id = 0, this.code, this.name, this.description, this.price, this.quantity});
_ProductStruct({this.id = 0, this.code, this.name, this.description, this.price, this.quantity, this.image});
factory _ProductStruct.fromJson(Map<String, dynamic> json) => _$ProductStructFromJson(json);
@override@JsonKey() final int id;
@ -94,6 +95,7 @@ class _ProductStruct implements ProductStruct {
@override final String? description;
@override final String? price;
@override final String? quantity;
@override final String? image;
/// Create a copy of ProductStruct
/// with the given fields replaced by the non-null parameter values.
@ -108,16 +110,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProductStruct&&(identical(other.id, id) || other.id == id)&&(identical(other.code, code) || other.code == code)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.price, price) || other.price == price)&&(identical(other.quantity, quantity) || other.quantity == quantity));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProductStruct&&(identical(other.id, id) || other.id == id)&&(identical(other.code, code) || other.code == code)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.price, price) || other.price == price)&&(identical(other.quantity, quantity) || other.quantity == quantity)&&(identical(other.image, image) || other.image == image));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,code,name,description,price,quantity);
int get hashCode => Object.hash(runtimeType,id,code,name,description,price,quantity,image);
@override
String toString() {
return 'ProductStruct(id: $id, code: $code, name: $name, description: $description, price: $price, quantity: $quantity)';
return 'ProductStruct(id: $id, code: $code, name: $name, description: $description, price: $price, quantity: $quantity, image: $image)';
}
@ -128,7 +130,7 @@ abstract mixin class _$ProductStructCopyWith<$Res> implements $ProductStructCopy
factory _$ProductStructCopyWith(_ProductStruct value, $Res Function(_ProductStruct) _then) = __$ProductStructCopyWithImpl;
@override @useResult
$Res call({
int id, String? code, String? name, String? description, String? price, String? quantity
int id, String? code, String? name, String? description, String? price, String? quantity, String? image
});
@ -145,7 +147,7 @@ class __$ProductStructCopyWithImpl<$Res>
/// Create a copy of ProductStruct
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? code = freezed,Object? name = freezed,Object? description = freezed,Object? price = freezed,Object? quantity = freezed,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? code = freezed,Object? name = freezed,Object? description = freezed,Object? price = freezed,Object? quantity = freezed,Object? image = freezed,}) {
return _then(_ProductStruct(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as int,code: freezed == code ? _self.code : code // ignore: cast_nullable_to_non_nullable
@ -153,6 +155,7 @@ as String?,name: freezed == name ? _self.name : name // ignore: cast_nullable_to
as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,price: freezed == price ? _self.price : price // ignore: cast_nullable_to_non_nullable
as String?,quantity: freezed == quantity ? _self.quantity : quantity // ignore: cast_nullable_to_non_nullable
as String?,image: freezed == image ? _self.image : image // ignore: cast_nullable_to_non_nullable
as String?,
));
}

View File

@ -14,6 +14,7 @@ _ProductStruct _$ProductStructFromJson(Map<String, dynamic> json) =>
description: json['description'] as String?,
price: json['price'] as String?,
quantity: json['quantity'] as String?,
image: json['image'] as String?,
);
Map<String, dynamic> _$ProductStructToJson(_ProductStruct instance) =>
@ -24,4 +25,5 @@ Map<String, dynamic> _$ProductStructToJson(_ProductStruct instance) =>
'description': instance.description,
'price': instance.price,
'quantity': instance.quantity,
'image': instance.image,
};

View File

@ -1,20 +1,16 @@
import 'package:barcode_scanner/backend/schema/product/product_struct.dart';
import 'package:barcode_scanner/themes/app_theme.dart';
import 'package:flutter/material.dart';
class ProductScannedComponent extends StatefulWidget {
const ProductScannedComponent({
super.key,
required this.codeScanned,
required this.productName,
required this.brands,
required this.img,
required this.productStruct,
this.onRescan,
this.onDetails,
});
final String codeScanned;
final String productName;
final String brands;
final String img;
final ProductStruct productStruct;
final Future Function()? onRescan;
final Function()? onDetails;
@ -76,7 +72,7 @@ class _ProductScannedComponentState extends State<ProductScannedComponent> {
Align(
alignment: Alignment.topCenter,
child: Image.network(
widget.img,
widget.productStruct.image ?? '',
height: MediaQuery.sizeOf(context).height * .2,
),
),
@ -91,7 +87,7 @@ class _ProductScannedComponentState extends State<ProductScannedComponent> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Code scanné:'),
Text(widget.codeScanned),
Text(widget.productStruct.id.toString()),
],
),
Divider(
@ -106,7 +102,7 @@ class _ProductScannedComponentState extends State<ProductScannedComponent> {
Text('Produit: '),
Expanded(
child: Text(
widget.productName,
widget.productStruct.name ?? '',
textAlign: TextAlign.end,
),
),
@ -120,7 +116,10 @@ class _ProductScannedComponentState extends State<ProductScannedComponent> {
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [Text('Marque:'), Text(widget.brands)],
children: [
Text('Quantité:'),
Text(widget.productStruct.quantity ?? ''),
],
),
Divider(
height: 1.0,

View File

@ -95,7 +95,7 @@ class LoginPageModel extends StateNotifier<LoginPageState> {
user.setToLocalStorage(),
]);
state = state.copyWith(loading: false, status: LoginPageStateStatus.logged);
debugPrint("$token");
debugPrint(token);
}
Future<void> logOut() async {

View File

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:barcode_scanner/backend/api/api_calls.dart';
import 'package:barcode_scanner/backend/schema/product/product_struct.dart';
import 'package:barcode_scanner/components/product_scanned_component.dart';
import 'package:barcode_scanner/router/go_secure_router_builder.dart';
import 'package:barcode_scanner/themes/app_theme.dart';
@ -104,6 +105,13 @@ class _ScannerPageState extends ConsumerState<ScannerPage>
debugPrint('Nom du produit : ${product["product_name"]}');
debugPrint('Marque : ${product["brands"]}');
debugPrint('Image : ${product["image_url"]}');
final productStruct = ProductStruct(
id: int.parse(product["id"]),
name: product["product_name"],
image: product["image_thumb_url"],
description: product["categories"],
quantity: product["quantity"],
);
//show dialog
await showDialog(
barrierDismissible: false,
@ -125,10 +133,7 @@ class _ScannerPageState extends ConsumerState<ScannerPage>
child: SizedBox(
width: MediaQuery.sizeOf(context).width * 0.9,
child: ProductScannedComponent(
img: product["image_url"],
productName: product["product_name"],
brands: product["brands"],
codeScanned: qrcodeValue,
productStruct: productStruct,
onRescan: () async {
Navigator.of(context).pop();
qrcodeFound = false;
@ -171,6 +176,13 @@ class _ScannerPageState extends ConsumerState<ScannerPage>
debugPrint('Nom du produit : ${product["product_name"]}');
debugPrint('Marque : ${product["brands"]}');
debugPrint('Image : ${product["image_url"]}');
final productStruct = ProductStruct(
id: int.parse(product["id"]),
image: product["image_thumb_url"],
name: product["product_name"],
description: product["categories"],
quantity: product["quantity"],
);
//show dialog
await showDialog(
barrierDismissible: false,
@ -192,10 +204,7 @@ class _ScannerPageState extends ConsumerState<ScannerPage>
child: SizedBox(
width: MediaQuery.sizeOf(context).width * 0.9,
child: ProductScannedComponent(
img: product["image_url"],
productName: product["product_name"],
brands: product["brands"],
codeScanned: qrcodeValue,
productStruct: productStruct,
onRescan: () async {
Navigator.of(context).pop();
qrcodeFound = false;
@ -245,7 +254,7 @@ class _ScannerPageState extends ConsumerState<ScannerPage>
child: IconButton(
icon: Icon(Icons.help_outline, color: Colors.white, size: 24.0),
onPressed: () {
print('IconButton pressed ...');
debugPrint('IconButton pressed ...');
},
),
),