Skip to content

Commit e93f069

Browse files
feat: Added Robotoff nutrition extraction to nutrition edit page (#6366)
* Add robotoff nutrition extraction to nutrition edit page * feat: Enhance nutrition extraction UI with loading indicators and improved nutrient handling * feat: Add localized messages for nutrient extraction actions and results * feat: Track robotoff nutrition extraction events and update UI components * Removed duplicated translation entries * feat: Refactor analytics tracking for Robotoff nutrition events
1 parent cb27d0d commit e93f069

File tree

10 files changed

+514
-130
lines changed

10 files changed

+514
-130
lines changed

packages/smooth_app/ios/Podfile.lock

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -250,52 +250,52 @@ EXTERNAL SOURCES:
250250
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
251251

252252
SPEC CHECKSUMS:
253-
app_settings: 3507c575c2b18a462c99948f61d5de21d4420999
254-
audioplayers_darwin: ccf9c770ee768abb07e26d90af093f7bab1c12ab
255-
camera_avfoundation: 04b44aeb14070126c6529e5ab82cc7c9fca107cf
256-
connectivity_plus: 2256d3e20624a7749ed21653aafe291a46446fee
257-
device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896
253+
app_settings: 017320c6a680cdc94c799949d95b84cb69389ebc
254+
audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40
255+
camera_avfoundation: dd002b0330f4981e1bbcb46ae9b62829237459a4
256+
connectivity_plus: 18382e7311ba19efcaee94442b23b32507b20695
257+
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
258258
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
259-
flutter_custom_tabs_ios: dd647919edd75e82ba6b00009eb3460a28c011b8
260-
flutter_email_sender: 2397f5e84aaacfb61af569637a963e7c687858d8
261-
flutter_icmp_ping: 47c1df3440c18ddd39fc457e607bb3b42d4a339f
262-
flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1
263-
flutter_native_splash: 6cad9122ea0fad137d23137dd14b937f3e90b145
264-
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
259+
flutter_custom_tabs_ios: a651b18786388923b62de8c0537607de87c2eccf
260+
flutter_email_sender: 10a22605f92809a11ef52b2f412db806c6082d40
261+
flutter_icmp_ping: 2b159955eee0c487c766ad83fec224ae35e7c935
262+
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
263+
flutter_native_splash: f71420956eb811e6d310720fee915f1d42852e7a
264+
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
265265
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
266266
GoogleMLKit: eff9e23ec1d90ea4157a1ee2e32a4f610c5b3318
267267
GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8
268268
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
269269
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
270-
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
271-
in_app_review: 5596fe56fab799e8edb3561c03d053363ab13457
272-
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
273-
iso_countries: 7ca741b02ae533b86f1f18a8bb52961d776ac77e
270+
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
271+
in_app_review: a31b5257259646ea78e0e35fc914979b0031d011
272+
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
273+
iso_countries: eb09d40f388e4c65e291e0bb36a701dfe7de6c74
274274
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
275275
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
276276
MLImage: 0ad1c5f50edd027672d8b26b0fee78a8b4a0fc56
277277
MLKitBarcodeScanning: 0a3064da0a7f49ac24ceb3cb46a5bc67496facd2
278278
MLKitCommon: 07c2c33ae5640e5380beaaa6e4b9c249a205542d
279279
MLKitVision: 45e79d68845a2de77e2dd4d7f07947f0ed157b0e
280-
mobile_scanner: af8f71879eaba2bbcb4d86c6a462c3c0e7f23036
280+
mobile_scanner: fd0054c52ede661e80bf5a4dea477a2467356bee
281281
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
282282
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
283-
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
284-
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
285-
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
283+
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
284+
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
285+
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
286286
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
287-
qr_code_scanner: d77f94ecc9abf96d9b9b8fc04ef13f611e5a147a
288-
rive_common: dd421daaf9ae69f0125aa761dd96abd278399952
287+
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
288+
rive_common: 4743dbfd2911c99066547a3c6454681e0fa907df
289289
SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8
290290
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
291291
Sentry: 38ed8bf38eab5812787274bf591e528074c19e02
292-
sentry_flutter: a72ca0eb6e78335db7c4ddcddd1b9f6c8ed5b764
293-
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
294-
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
295-
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
296-
torch_light: d093d579a221a59ef8a6b8c0eca20d52f7178087
297-
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
298-
webview_flutter_wkwebview: 44d4dee7d7056d5ad185d25b38404436d56c547c
292+
sentry_flutter: 7d1f1df30f3768c411603ed449519bbb90a7d87b
293+
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
294+
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
295+
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
296+
torch_light: 682062fa12102172fa38b6b14c106d93b060f83e
297+
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
298+
webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4
299299

300300
PODFILE CHECKSUM: 6ac49c02151268e5844d0422787205b7cbdd62d2
301301

packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart

Lines changed: 95 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,31 @@ class SmoothCardWithRoundedHeader extends StatelessWidget {
177177
}
178178
}
179179

180+
class SmoothCardWithRoundedHeaderBanner extends StatelessWidget {
181+
const SmoothCardWithRoundedHeaderBanner({
182+
required this.child,
183+
});
184+
185+
final Widget child;
186+
187+
@override
188+
Widget build(BuildContext context) {
189+
final SmoothColorsThemeExtension extension =
190+
context.extension<SmoothColorsThemeExtension>();
191+
192+
return CustomPaint(
193+
painter: _SmoothCardWithRoundedHeaderBackgroundPainter(
194+
color: context.lightTheme() ? extension.greyLight : extension.greyDark,
195+
radius: ROUNDED_RADIUS,
196+
shadowElevation:
197+
SmoothCardWithRoundedHeaderTopShadowProvider.of(context)?.shadow ??
198+
0.0,
199+
),
200+
child: child,
201+
);
202+
}
203+
}
204+
180205
class SmoothCardWithRoundedHeaderTop extends StatelessWidget {
181206
const SmoothCardWithRoundedHeaderTop({
182207
required this.title,
@@ -188,6 +213,7 @@ class SmoothCardWithRoundedHeaderTop extends StatelessWidget {
188213
this.titleTextStyle,
189214
this.titlePadding,
190215
this.borderRadius,
216+
this.banner,
191217
});
192218

193219
final String title;
@@ -199,6 +225,7 @@ class SmoothCardWithRoundedHeaderTop extends StatelessWidget {
199225
final TextStyle? titleTextStyle;
200226
final EdgeInsetsGeometry? titlePadding;
201227
final BorderRadius? borderRadius;
228+
final Widget? banner;
202229

203230
static const double _DEFAULT_LEADING_ICON_SIZE = 17.0;
204231

@@ -209,74 +236,79 @@ class SmoothCardWithRoundedHeaderTop extends StatelessWidget {
209236
return Semantics(
210237
label: title,
211238
excludeSemantics: true,
212-
child: CustomPaint(
213-
painter: _SmoothCardWithRoundedHeaderBackgroundPainter(
214-
color: color,
215-
radius: borderRadius?.topRight ?? ROUNDED_RADIUS,
216-
shadowElevation:
217-
SmoothCardWithRoundedHeaderTopShadowProvider.of(context)
218-
?.shadow ??
219-
0.0,
220-
),
221-
child: Padding(
222-
padding: titlePadding ??
223-
(trailing != null
224-
? const EdgeInsetsDirectional.only(
225-
top: 2.0,
226-
start: LARGE_SPACE,
227-
end: SMALL_SPACE,
228-
bottom: 2.0,
229-
)
230-
: const EdgeInsetsDirectional.symmetric(
231-
vertical: BALANCED_SPACE,
232-
horizontal: LARGE_SPACE,
233-
)),
234-
child: Row(
235-
children: <Widget>[
236-
if (leading != null)
237-
IconTheme(
238-
data: IconThemeData(
239-
color: titleBackgroundColor,
240-
size: leadingIconSize ?? _DEFAULT_LEADING_ICON_SIZE,
241-
),
242-
child: DecoratedBox(
243-
decoration: const BoxDecoration(
244-
color: Colors.white,
245-
shape: BoxShape.circle,
239+
child: Column(
240+
children: <Widget>[
241+
if (banner != null) SmoothCardWithRoundedHeaderBanner(child: banner!),
242+
CustomPaint(
243+
painter: _SmoothCardWithRoundedHeaderBackgroundPainter(
244+
color: color,
245+
radius: borderRadius?.topRight ?? ROUNDED_RADIUS,
246+
shadowElevation:
247+
SmoothCardWithRoundedHeaderTopShadowProvider.of(context)
248+
?.shadow ??
249+
0.0,
250+
),
251+
child: Padding(
252+
padding: titlePadding ??
253+
(trailing != null
254+
? const EdgeInsetsDirectional.only(
255+
top: 2.0,
256+
start: LARGE_SPACE,
257+
end: SMALL_SPACE,
258+
bottom: 2.0,
259+
)
260+
: const EdgeInsetsDirectional.symmetric(
261+
vertical: BALANCED_SPACE,
262+
horizontal: LARGE_SPACE,
263+
)),
264+
child: Row(
265+
children: <Widget>[
266+
if (leading != null)
267+
IconTheme(
268+
data: IconThemeData(
269+
color: titleBackgroundColor,
270+
size: leadingIconSize ?? _DEFAULT_LEADING_ICON_SIZE,
271+
),
272+
child: DecoratedBox(
273+
decoration: const BoxDecoration(
274+
color: Colors.white,
275+
shape: BoxShape.circle,
276+
),
277+
child: Padding(
278+
padding: leadingPadding ??
279+
const EdgeInsetsDirectional.all(6.0),
280+
child: leading,
281+
),
282+
),
246283
),
247-
child: Padding(
248-
padding: leadingPadding ??
249-
const EdgeInsetsDirectional.all(6.0),
250-
child: leading,
284+
const SizedBox(width: MEDIUM_SPACE),
285+
Expanded(
286+
child: Text(
287+
title,
288+
maxLines: 1,
289+
overflow: TextOverflow.ellipsis,
290+
style: (titleTextStyle ??
291+
Theme.of(context).textTheme.displaySmall)
292+
?.copyWith(
293+
color: Colors.white,
294+
),
251295
),
252296
),
253-
),
254-
const SizedBox(width: MEDIUM_SPACE),
255-
Expanded(
256-
child: Text(
257-
title,
258-
maxLines: 1,
259-
overflow: TextOverflow.ellipsis,
260-
style: (titleTextStyle ??
261-
Theme.of(context).textTheme.displaySmall)
262-
?.copyWith(
263-
color: Colors.white,
264-
),
265-
),
297+
if (trailing != null) ...<Widget>[
298+
const SizedBox(width: MEDIUM_SPACE),
299+
IconTheme(
300+
data: const IconThemeData(
301+
color: Colors.white,
302+
size: 20.0,
303+
),
304+
child: trailing!,
305+
),
306+
],
307+
],
266308
),
267-
if (trailing != null) ...<Widget>[
268-
const SizedBox(width: MEDIUM_SPACE),
269-
IconTheme(
270-
data: const IconThemeData(
271-
color: Colors.white,
272-
size: 20.0,
273-
),
274-
child: trailing!,
275-
),
276-
],
277-
],
309+
),
278310
),
279-
),
311+
],
280312
),
281313
);
282314
}

packages/smooth_app/lib/generic_lib/widgets/smooth_sliver_card.dart

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class SliverCardWithRoundedHeader extends StatefulWidget {
1919
this.titleBackgroundColor,
2020
this.contentBackgroundColor,
2121
this.borderRadius,
22+
this.banner,
2223
super.key,
2324
});
2425

@@ -34,6 +35,7 @@ class SliverCardWithRoundedHeader extends StatefulWidget {
3435
final Color? titleBackgroundColor;
3536
final Color? contentBackgroundColor;
3637
final BorderRadius? borderRadius;
38+
final Widget? banner;
3739

3840
@override
3941
State<SliverCardWithRoundedHeader> createState() =>
@@ -43,6 +45,19 @@ class SliverCardWithRoundedHeader extends StatefulWidget {
4345
class _SliverCardWithRoundedHeaderState
4446
extends State<SliverCardWithRoundedHeader> {
4547
double? _height;
48+
double? _bannerHeight;
49+
50+
@override
51+
void didUpdateWidget(SliverCardWithRoundedHeader oldWidget) {
52+
super.didUpdateWidget(oldWidget);
53+
54+
if (oldWidget.banner != null && widget.banner == null) {
55+
if (_height != null && _bannerHeight != null) {
56+
_height = _height! - _bannerHeight!;
57+
_bannerHeight = null;
58+
}
59+
}
60+
}
4661

4762
@override
4863
Widget build(BuildContext context) {
@@ -56,15 +71,25 @@ class _SliverCardWithRoundedHeaderState
5671
titleTextStyle: widget.titleTextStyle,
5772
titlePadding: widget.titlePadding,
5873
borderRadius: widget.borderRadius,
74+
banner: widget.banner,
5975
);
6076

6177
if (_height == null) {
6278
return SliverToBoxAdapter(
63-
child: MeasureSize(
64-
onChange: (Size size) => setState(() => _height = size.height),
65-
child: Opacity(opacity: 0.0, child: child),
66-
),
67-
);
79+
child: Column(
80+
children: <Widget>[
81+
if (widget.banner != null)
82+
MeasureSize(
83+
onChange: (Size size) =>
84+
setState(() => _bannerHeight = size.height),
85+
child: widget.banner!,
86+
),
87+
MeasureSize(
88+
onChange: (Size size) => setState(() => _height = size.height),
89+
child: Opacity(opacity: 0.0, child: child),
90+
),
91+
],
92+
));
6893
}
6994

7095
return MultiSliver(

packages/smooth_app/lib/helpers/analytics_helper.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,22 @@ enum AnalyticsEvent {
168168
final AnalyticsCategory category;
169169
}
170170

171+
enum AnalyticsRobotoffEvents {
172+
robotoffNutritionExtracted(
173+
name: 'robotoff nutrition extracted',
174+
),
175+
robotoffNutritionInsightAccepted(
176+
name: 'robotoff nutrition insight accepted',
177+
),
178+
robotoffNutritionInsightRejected(
179+
name: 'robotoff nutrition insight rejected',
180+
);
181+
182+
const AnalyticsRobotoffEvents({required this.name});
183+
184+
final String name;
185+
}
186+
171187
enum AnalyticsEditEvents {
172188
basicDetails(name: 'BasicDetails'),
173189
photos(name: 'Photos'),
@@ -377,6 +393,19 @@ class AnalyticsHelper {
377393
);
378394
}
379395

396+
static void trackRobotoffExtraction(
397+
AnalyticsRobotoffEvents event,
398+
Nutrient nutrient,
399+
Product product,
400+
) =>
401+
trackCustomEvent(
402+
event.name,
403+
AnalyticsCategory.robotoff.tag,
404+
action: nutrient.name,
405+
barcode: product.barcode,
406+
productType: product.productType ?? ProductType.food,
407+
);
408+
380409
static void trackProductEdit(
381410
AnalyticsEditEvents editEventName,
382411
Product product, [

0 commit comments

Comments
 (0)