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

View File

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

View File

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

View File

@ -16,7 +16,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$ProductStruct { 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 /// Create a copy of ProductStruct
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@ -29,16 +29,16 @@ $ProductStructCopyWith<ProductStruct> get copyWith => _$ProductStructCopyWithImp
@override @override
bool operator ==(Object other) { 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) @JsonKey(includeFromJson: false, includeToJson: false)
@override @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 @override
String toString() { 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; factory $ProductStructCopyWith(ProductStruct value, $Res Function(ProductStruct) _then) = _$ProductStructCopyWithImpl;
@useResult @useResult
$Res call({ $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 /// Create a copy of ProductStruct
/// with the given fields replaced by the non-null parameter values. /// 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( return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable 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 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?,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?,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?,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?, as String?,
)); ));
} }
@ -85,7 +86,7 @@ as String?,
@JsonSerializable() @JsonSerializable()
class _ProductStruct implements ProductStruct { 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); factory _ProductStruct.fromJson(Map<String, dynamic> json) => _$ProductStructFromJson(json);
@override@JsonKey() final int id; @override@JsonKey() final int id;
@ -94,6 +95,7 @@ class _ProductStruct implements ProductStruct {
@override final String? description; @override final String? description;
@override final String? price; @override final String? price;
@override final String? quantity; @override final String? quantity;
@override final String? image;
/// Create a copy of ProductStruct /// Create a copy of ProductStruct
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -108,16 +110,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { 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) @JsonKey(includeFromJson: false, includeToJson: false)
@override @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 @override
String toString() { 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; factory _$ProductStructCopyWith(_ProductStruct value, $Res Function(_ProductStruct) _then) = __$ProductStructCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $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 /// Create a copy of ProductStruct
/// with the given fields replaced by the non-null parameter values. /// 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( return _then(_ProductStruct(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable 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 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?,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?,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?,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?, as String?,
)); ));
} }

View File

@ -14,6 +14,7 @@ _ProductStruct _$ProductStructFromJson(Map<String, dynamic> json) =>
description: json['description'] as String?, description: json['description'] as String?,
price: json['price'] as String?, price: json['price'] as String?,
quantity: json['quantity'] as String?, quantity: json['quantity'] as String?,
image: json['image'] as String?,
); );
Map<String, dynamic> _$ProductStructToJson(_ProductStruct instance) => Map<String, dynamic> _$ProductStructToJson(_ProductStruct instance) =>
@ -24,4 +25,5 @@ Map<String, dynamic> _$ProductStructToJson(_ProductStruct instance) =>
'description': instance.description, 'description': instance.description,
'price': instance.price, 'price': instance.price,
'quantity': instance.quantity, '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:barcode_scanner/themes/app_theme.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ProductScannedComponent extends StatefulWidget { class ProductScannedComponent extends StatefulWidget {
const ProductScannedComponent({ const ProductScannedComponent({
super.key, super.key,
required this.codeScanned, required this.productStruct,
required this.productName,
required this.brands,
required this.img,
this.onRescan, this.onRescan,
this.onDetails, this.onDetails,
}); });
final String codeScanned; final ProductStruct productStruct;
final String productName;
final String brands;
final String img;
final Future Function()? onRescan; final Future Function()? onRescan;
final Function()? onDetails; final Function()? onDetails;
@ -76,7 +72,7 @@ class _ProductScannedComponentState extends State<ProductScannedComponent> {
Align( Align(
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
child: Image.network( child: Image.network(
widget.img, widget.productStruct.image ?? '',
height: MediaQuery.sizeOf(context).height * .2, height: MediaQuery.sizeOf(context).height * .2,
), ),
), ),
@ -91,7 +87,7 @@ class _ProductScannedComponentState extends State<ProductScannedComponent> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text('Code scanné:'), Text('Code scanné:'),
Text(widget.codeScanned), Text(widget.productStruct.id.toString()),
], ],
), ),
Divider( Divider(
@ -106,7 +102,7 @@ class _ProductScannedComponentState extends State<ProductScannedComponent> {
Text('Produit: '), Text('Produit: '),
Expanded( Expanded(
child: Text( child: Text(
widget.productName, widget.productStruct.name ?? '',
textAlign: TextAlign.end, textAlign: TextAlign.end,
), ),
), ),
@ -120,7 +116,10 @@ class _ProductScannedComponentState extends State<ProductScannedComponent> {
Row( Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [Text('Marque:'), Text(widget.brands)], children: [
Text('Quantité:'),
Text(widget.productStruct.quantity ?? ''),
],
), ),
Divider( Divider(
height: 1.0, height: 1.0,

View File

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

View File

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