From 25687059097e19f0e02b488d47d94d6edbc920fd Mon Sep 17 00:00:00 2001 From: Edouard Marquez Date: Mon, 6 Jan 2025 11:44:08 +0100 Subject: [PATCH 1/7] UI improvements for the edit mode --- .../smooth_large_button_with_icon.dart | 2 +- .../buttons/smooth_simple_button.dart | 2 +- .../lib/generic_lib/widgets/smooth_card.dart | 121 ++++++++++++++++++ .../lib/helpers/product_cards_helper.dart | 2 + .../input/smooth_autocomplete_text_field.dart | 17 ++- .../lib/pages/product/simple_input_page.dart | 54 ++++---- .../product/simple_input_page_helpers.dart | 9 +- .../product/simple_input_text_field.dart | 10 +- .../pages/product/simple_input_widget.dart | 115 +++++++++++------ 9 files changed, 249 insertions(+), 83 deletions(-) diff --git a/packages/smooth_app/lib/generic_lib/buttons/smooth_large_button_with_icon.dart b/packages/smooth_app/lib/generic_lib/buttons/smooth_large_button_with_icon.dart index 266c04bf1f41..013a84218b54 100644 --- a/packages/smooth_app/lib/generic_lib/buttons/smooth_large_button_with_icon.dart +++ b/packages/smooth_app/lib/generic_lib/buttons/smooth_large_button_with_icon.dart @@ -18,7 +18,7 @@ class SmoothLargeButtonWithIcon extends StatelessWidget { final String text; final IconData icon; final VoidCallback? onPressed; - final EdgeInsets? padding; + final EdgeInsetsGeometry? padding; final IconData? trailing; final Color? backgroundColor; final Color? foregroundColor; diff --git a/packages/smooth_app/lib/generic_lib/buttons/smooth_simple_button.dart b/packages/smooth_app/lib/generic_lib/buttons/smooth_simple_button.dart index 9fc70f77aebe..c60c671a6660 100644 --- a/packages/smooth_app/lib/generic_lib/buttons/smooth_simple_button.dart +++ b/packages/smooth_app/lib/generic_lib/buttons/smooth_simple_button.dart @@ -19,7 +19,7 @@ class SmoothSimpleButton extends StatelessWidget { final double minWidth; final double height; final BorderRadius borderRadius; - final EdgeInsets padding; + final EdgeInsetsGeometry padding; final Color? buttonColor; @override diff --git a/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart b/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart index 58aa22ab3d36..d0eaa976ffde 100644 --- a/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart +++ b/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/themes/smooth_theme.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; +import 'package:smooth_app/themes/theme_provider.dart'; /// Renders a Material card with elevation, shadow, Border radius etc... /// Note: If the caller updates BoxDecoration of the [header] or [child] widget, @@ -100,3 +103,121 @@ class SmoothCard extends StatelessWidget { ); } } + +class SmoothCardWithRoundedHeader extends StatelessWidget { + const SmoothCardWithRoundedHeader({ + required this.title, + required this.child, + this.iconTitle, + }); + + final String title; + final Widget? iconTitle; + final Widget child; + + @override + Widget build(BuildContext context) { + final ThemeData themeData = Theme.of(context); + final SmoothColorsThemeExtension extension = + context.extension(); + + final Color color = + context.lightTheme() ? extension.primaryBlack : Colors.black; + + return Column( + children: [ + Semantics( + label: title, + excludeSemantics: true, + child: CustomPaint( + painter: _SmoothCardWithRoundedHeaderBackgroundPainter( + color: color, + radius: ROUNDED_RADIUS, + ), + child: Padding( + padding: const EdgeInsetsDirectional.symmetric( + vertical: MEDIUM_SPACE, + horizontal: LARGE_SPACE, + ), + child: Row( + children: [ + if (iconTitle != null) + DecoratedBox( + decoration: const BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + child: Padding( + padding: const EdgeInsetsDirectional.all(SMALL_SPACE), + child: IconTheme( + data: IconThemeData( + color: color, + size: 18.0, + ), + child: iconTitle!, + ), + ), + ), + const SizedBox(width: MEDIUM_SPACE), + Text( + title, + style: themeData.textTheme.displaySmall?.copyWith( + color: Colors.white, + ), + ), + ], + ), + ), + ), + ), + SmoothCard( + margin: EdgeInsets.zero, + padding: const EdgeInsetsDirectional.only( + top: MEDIUM_SPACE, + ), + color: context.darkTheme() ? extension.primaryUltraBlack : null, + child: child, + ), + ], + ); + } +} + +class _SmoothCardWithRoundedHeaderBackgroundPainter extends CustomPainter { + _SmoothCardWithRoundedHeaderBackgroundPainter({ + required Color color, + required this.radius, + }) : _paint = Paint()..color = color; + + final Radius radius; + final Paint _paint; + + @override + void paint(Canvas canvas, Size size) { + canvas.drawRRect( + RRect.fromRectAndCorners( + Rect.fromLTWH( + 0.0, + 0.0, + size.width, + size.height + ROUNDED_RADIUS.y, + ), + topLeft: radius, + topRight: radius, + ), + _paint, + ); + } + + @override + bool shouldRepaint( + _SmoothCardWithRoundedHeaderBackgroundPainter oldDelegate, + ) => + false; + + @override + bool shouldRebuildSemantics( + _SmoothCardWithRoundedHeaderBackgroundPainter oldDelegate, + ) => + false; +} diff --git a/packages/smooth_app/lib/helpers/product_cards_helper.dart b/packages/smooth_app/lib/helpers/product_cards_helper.dart index 496892713608..86944bc36c9c 100644 --- a/packages/smooth_app/lib/helpers/product_cards_helper.dart +++ b/packages/smooth_app/lib/helpers/product_cards_helper.dart @@ -310,6 +310,7 @@ Widget addPanelButton( final String label, { final IconData? iconData, final String? textAlign, + final EdgeInsetsGeometry? padding, required final Function() onPressed, }) => Padding( @@ -319,6 +320,7 @@ Widget addPanelButton( icon: iconData ?? Icons.add, onPressed: onPressed, textAlign: iconData == null ? TextAlign.center : null, + padding: padding, ), ); diff --git a/packages/smooth_app/lib/pages/input/smooth_autocomplete_text_field.dart b/packages/smooth_app/lib/pages/input/smooth_autocomplete_text_field.dart index 8d1456341cc7..470ca39becff 100644 --- a/packages/smooth_app/lib/pages/input/smooth_autocomplete_text_field.dart +++ b/packages/smooth_app/lib/pages/input/smooth_autocomplete_text_field.dart @@ -20,6 +20,8 @@ class SmoothAutocompleteTextField extends StatefulWidget { this.minLengthForSuggestions = 1, this.allowEmojis = true, this.suffixIcon, + this.borderRadius, + this.padding, }); final FocusNode focusNode; @@ -31,6 +33,8 @@ class SmoothAutocompleteTextField extends StatefulWidget { final AutocompleteManager? manager; final bool allowEmojis; final Widget? suffixIcon; + final BorderRadius? borderRadius; + final EdgeInsetsGeometry? padding; @override State createState() => @@ -86,14 +90,15 @@ class _SmoothAutocompleteTextFieldState decoration: InputDecoration( suffixIcon: widget.suffixIcon, filled: true, - border: const OutlineInputBorder( - borderRadius: ANGULAR_BORDER_RADIUS, + border: OutlineInputBorder( + borderRadius: widget.borderRadius ?? ANGULAR_BORDER_RADIUS, borderSide: BorderSide.none, ), - contentPadding: const EdgeInsets.symmetric( - horizontal: SMALL_SPACE, - vertical: SMALL_SPACE, - ), + contentPadding: widget.padding ?? + const EdgeInsets.symmetric( + horizontal: SMALL_SPACE, + vertical: SMALL_SPACE, + ), hintText: widget.hintText, suffix: Offstage( offstage: !_loading, diff --git a/packages/smooth_app/lib/pages/product/simple_input_page.dart b/packages/smooth_app/lib/pages/product/simple_input_page.dart index 1ed7108e5170..7286788fc004 100644 --- a/packages/smooth_app/lib/pages/product/simple_input_page.dart +++ b/packages/smooth_app/lib/pages/product/simple_input_page.dart @@ -4,7 +4,6 @@ import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:provider/provider.dart'; import 'package:smooth_app/background/background_task_details.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; -import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:smooth_app/helpers/collections_helper.dart'; import 'package:smooth_app/helpers/product_cards_helper.dart'; @@ -77,26 +76,24 @@ class _SimpleInputPageState extends State { padding: i == 0 ? EdgeInsets.zero : const EdgeInsets.only(top: LARGE_SPACE), - child: SmoothCard( - // This provider will handle the dispose() call for us - child: MultiProvider( - providers: >[ - ChangeNotifierProvider( - create: (_) { - _controllers.replace(i, TextEditingController()); - return _controllers[i]; - }, - ), - ChangeNotifierProvider( - create: (_) => widget.helpers[i], - ), - ], - child: SimpleInputWidget( - helper: widget.helpers[i], - product: widget.product, - controller: _controllers[i], - displayTitle: widget.helpers.length > 1, + // This provider will handle the dispose() call for us + child: MultiProvider( + providers: >[ + ChangeNotifierProvider( + create: (_) { + _controllers.replace(i, TextEditingController()); + return _controllers[i]; + }, + ), + ChangeNotifierProvider( + create: (_) => widget.helpers[i], ), + ], + child: SimpleInputWidget( + helper: widget.helpers[i], + product: widget.product, + controller: _controllers[i], + displayTitle: widget.helpers.length > 1, ), ), ), @@ -118,15 +115,14 @@ class _SimpleInputPageState extends State { .extension()! .primaryLight : null, - body: Padding( - padding: const EdgeInsetsDirectional.only( - top: SMALL_SPACE, - start: SMALL_SPACE, - end: SMALL_SPACE, - ), - child: Scrollbar( - child: ListView(children: simpleInputs), - ), + body: Scrollbar( + child: ListView( + padding: const EdgeInsetsDirectional.only( + top: MEDIUM_SPACE, + start: MEDIUM_SPACE, + end: MEDIUM_SPACE, + ), + children: simpleInputs), ), bottomNavigationBar: ProductBottomButtonsBar( onSave: () async => _exitPage( diff --git a/packages/smooth_app/lib/pages/product/simple_input_page_helpers.dart b/packages/smooth_app/lib/pages/product/simple_input_page_helpers.dart index 141ab0cc3eb7..8aa501821f30 100644 --- a/packages/smooth_app/lib/pages/product/simple_input_page_helpers.dart +++ b/packages/smooth_app/lib/pages/product/simple_input_page_helpers.dart @@ -122,11 +122,11 @@ abstract class AbstractSimpleInputPageHelper extends ChangeNotifier { ) => Padding( padding: const EdgeInsets.symmetric( - vertical: VERY_LARGE_SPACE, + vertical: SMALL_SPACE, horizontal: SMALL_SPACE, ), child: addPanelButton( - title.toUpperCase(), + title, onPressed: () async => confirmAndUploadNewPicture( context, imageField: ImageField.OTHER, @@ -137,6 +137,11 @@ abstract class AbstractSimpleInputPageHelper extends ChangeNotifier { isLoggedInMandatory: false, ), iconData: Icons.add_a_photo, + padding: const EdgeInsetsDirectional.only( + top: SMALL_SPACE, + bottom: SMALL_SPACE, + start: VERY_SMALL_SPACE, + ), ), ); diff --git a/packages/smooth_app/lib/pages/product/simple_input_text_field.dart b/packages/smooth_app/lib/pages/product/simple_input_text_field.dart index e2562b1bbdfe..fc4ebbe10be4 100644 --- a/packages/smooth_app/lib/pages/product/simple_input_text_field.dart +++ b/packages/smooth_app/lib/pages/product/simple_input_text_field.dart @@ -17,9 +17,11 @@ class SimpleInputTextField extends StatefulWidget { this.minLengthForSuggestions = 1, this.categories, this.shapeProvider, + this.margin, this.padding, required this.productType, this.suffixIcon, + this.borderRadius, }); final FocusNode focusNode; @@ -32,9 +34,11 @@ class SimpleInputTextField extends StatefulWidget { final int minLengthForSuggestions; final String? categories; final String? Function()? shapeProvider; + final EdgeInsetsGeometry? margin; final EdgeInsetsGeometry? padding; final ProductType? productType; final Widget? suffixIcon; + final BorderRadius? borderRadius; @override State createState() => _SimpleInputTextFieldState(); @@ -68,8 +72,8 @@ class _SimpleInputTextFieldState extends State { @override Widget build(BuildContext context) { return Padding( - padding: widget.padding ?? - const EdgeInsetsDirectional.only(start: LARGE_SPACE), + padding: + widget.margin ?? const EdgeInsetsDirectional.only(start: LARGE_SPACE), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, @@ -84,6 +88,8 @@ class _SimpleInputTextFieldState extends State { constraints: widget.constraints, manager: _manager, suffixIcon: widget.suffixIcon, + borderRadius: widget.borderRadius, + padding: widget.padding, ), ), if (widget.withClearButton) diff --git a/packages/smooth_app/lib/pages/product/simple_input_widget.dart b/packages/smooth_app/lib/pages/product/simple_input_widget.dart index 1581e9ff2370..0bc1d750ec58 100644 --- a/packages/smooth_app/lib/pages/product/simple_input_widget.dart +++ b/packages/smooth_app/lib/pages/product/simple_input_widget.dart @@ -2,12 +2,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; import 'package:smooth_app/helpers/collections_helper.dart'; import 'package:smooth_app/helpers/haptic_feedback_helper.dart'; import 'package:smooth_app/pages/product/explanation_widget.dart'; import 'package:smooth_app/pages/product/owner_field_info.dart'; import 'package:smooth_app/pages/product/simple_input_page_helpers.dart'; import 'package:smooth_app/pages/product/simple_input_text_field.dart'; +import 'package:smooth_app/themes/smooth_theme.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; +import 'package:smooth_app/themes/theme_provider.dart'; /// Simple input widget: we have a list of terms, we add, we remove. class SimpleInputWidget extends StatefulWidget { @@ -54,7 +58,8 @@ class _SimpleInputWidgetState extends State { @override Widget build(BuildContext context) { - final ThemeData themeData = Theme.of(context); + final SmoothColorsThemeExtension extension = + context.extension(); final AppLocalizations appLocalizations = AppLocalizations.of(context); final String? explanations = widget.helper.getAddExplanations(appLocalizations); @@ -64,59 +69,68 @@ class _SimpleInputWidgetState extends State { ); final bool isOwnerField = widget.helper.isOwnerField(widget.product); - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, + final Widget child = Column( + mainAxisSize: MainAxisSize.min, children: [ - if (widget.displayTitle) - ListTile( - leading: widget.helper.getIcon(), - minLeadingWidth: 0.0, - horizontalTitleGap: 12.0, - title: Text( - widget.helper.getTitle(appLocalizations), - style: themeData.textTheme.displaySmall, - ), + if (explanations != null) + Padding( + padding: + const EdgeInsetsDirectional.symmetric(horizontal: SMALL_SPACE), + child: ExplanationWidget(explanations), ), - if (explanations != null) ExplanationWidget(explanations), LayoutBuilder( builder: (_, BoxConstraints constraints) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Flexible( - flex: 1, - child: SimpleInputTextField( - autocompleteKey: _autocompleteKey, - focusNode: _focusNode, - constraints: constraints, - tagType: widget.helper.getTagType(), - hintText: widget.helper.getAddHint(appLocalizations), - controller: widget.controller, - padding: const EdgeInsetsDirectional.only( - start: 9.0, + return Padding( + padding: const EdgeInsetsDirectional.only( + start: SMALL_SPACE, + end: 4.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Flexible( + flex: 1, + child: SimpleInputTextField( + autocompleteKey: _autocompleteKey, + focusNode: _focusNode, + constraints: constraints, + tagType: widget.helper.getTagType(), + hintText: widget.helper.getAddHint(appLocalizations), + controller: widget.controller, + padding: const EdgeInsets.symmetric( + horizontal: MEDIUM_SPACE, + vertical: SMALL_SPACE, + ), + margin: const EdgeInsetsDirectional.only( + start: 3.0, + ), + productType: widget.product.productType, + suffixIcon: !isOwnerField ? null : const OwnerFieldIcon(), + borderRadius: const BorderRadius.all( + Radius.circular(20.0), + ), ), - productType: widget.product.productType, - suffixIcon: !isOwnerField ? null : const OwnerFieldIcon(), - ), - ), - Tooltip( - message: appLocalizations.edit_product_form_item_add_action( - widget.helper.getTypeLabel(appLocalizations)), - child: IconButton( - onPressed: _onAddItem, - icon: const Icon(Icons.add_circle), - splashRadius: 20, ), - ) - ], + Tooltip( + message: appLocalizations.edit_product_form_item_add_action( + widget.helper.getTypeLabel(appLocalizations)), + child: IconButton( + onPressed: _onAddItem, + icon: const Icon(Icons.add_circle), + splashRadius: 20, + ), + ) + ], + ), ); }, ), AnimatedList( key: _listKey, initialItemCount: _localTerms.length, + padding: + const EdgeInsetsDirectional.symmetric(horizontal: SMALL_SPACE), itemBuilder: ( BuildContext context, int position, @@ -156,6 +170,23 @@ class _SimpleInputWidgetState extends State { if (extraWidget != null) extraWidget, ], ); + + if (widget.displayTitle) { + return SmoothCardWithRoundedHeader( + iconTitle: widget.helper.getIcon(), + title: widget.helper.getTitle(appLocalizations), + child: child, + ); + } else { + return SmoothCard( + margin: EdgeInsets.zero, + padding: const EdgeInsetsDirectional.only( + top: MEDIUM_SPACE, + ), + color: context.darkTheme() ? extension.primaryUltraBlack : null, + child: child, + ); + } } void _onAddItem() { From fd6805fd5a83c94a2907f6aa3731be3499c7f0b8 Mon Sep 17 00:00:00 2001 From: Edouard Marquez Date: Mon, 6 Jan 2025 11:55:53 +0100 Subject: [PATCH 2/7] Better website input --- .../lib/generic_lib/widgets/smooth_card.dart | 9 ++-- .../pages/product/add_other_details_page.dart | 51 +++++++++++++------ .../lib/pages/product/simple_input_page.dart | 13 ++--- 3 files changed, 48 insertions(+), 25 deletions(-) diff --git a/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart b/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart index d0eaa976ffde..e64096dcc751 100644 --- a/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart +++ b/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart @@ -109,11 +109,13 @@ class SmoothCardWithRoundedHeader extends StatelessWidget { required this.title, required this.child, this.iconTitle, + this.contentPadding, }); final String title; final Widget? iconTitle; final Widget child; + final EdgeInsetsGeometry? contentPadding; @override Widget build(BuildContext context) { @@ -172,9 +174,10 @@ class SmoothCardWithRoundedHeader extends StatelessWidget { ), SmoothCard( margin: EdgeInsets.zero, - padding: const EdgeInsetsDirectional.only( - top: MEDIUM_SPACE, - ), + padding: contentPadding ?? + const EdgeInsetsDirectional.only( + top: MEDIUM_SPACE, + ), color: context.darkTheme() ? extension.primaryUltraBlack : null, child: child, ), diff --git a/packages/smooth_app/lib/pages/product/add_other_details_page.dart b/packages/smooth_app/lib/pages/product/add_other_details_page.dart index 1e3a2dbcaf83..ee9f11f444eb 100644 --- a/packages/smooth_app/lib/pages/product/add_other_details_page.dart +++ b/packages/smooth_app/lib/pages/product/add_other_details_page.dart @@ -3,12 +3,15 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:smooth_app/background/background_task_details.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; import 'package:smooth_app/generic_lib/widgets/smooth_text_form_field.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:smooth_app/helpers/product_cards_helper.dart'; import 'package:smooth_app/pages/product/common/product_buttons.dart'; import 'package:smooth_app/pages/product/may_exit_page_helper.dart'; import 'package:smooth_app/pages/text_field_helper.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; +import 'package:smooth_app/themes/theme_provider.dart'; import 'package:smooth_app/widgets/smooth_scaffold.dart'; import 'package:smooth_app/widgets/will_pop_scope.dart'; @@ -27,7 +30,6 @@ class AddOtherDetailsPage extends StatefulWidget { class _AddOtherDetailsPageState extends State { late final TextEditingControllerWithHistory _websiteController; - final double _heightSpace = LARGE_SPACE; final GlobalKey _formKey = GlobalKey(); late final Product _product; @@ -35,8 +37,9 @@ class _AddOtherDetailsPageState extends State { void initState() { super.initState(); _product = widget.product; - _websiteController = - TextEditingControllerWithHistory(text: _product.website ?? ''); + _websiteController = TextEditingControllerWithHistory( + text: _product.website ?? '', + ); } @override @@ -63,24 +66,40 @@ class _AddOtherDetailsPageState extends State { title: appLocalizations.edit_product_form_item_other_details_title, product: widget.product, ), + backgroundColor: context.lightTheme() + ? Theme.of(context) + .extension()! + .primaryLight + : null, body: Form( key: _formKey, child: Scrollbar( child: ListView( + padding: const EdgeInsetsDirectional.only( + top: MEDIUM_SPACE, + start: MEDIUM_SPACE, + end: MEDIUM_SPACE, + ), children: [ - SizedBox(height: _heightSpace), - Padding( - padding: EdgeInsets.symmetric(horizontal: size.width * 0.05), - child: Column( - children: [ - SizedBox(height: _heightSpace), - SmoothTextFormField( - controller: _websiteController, - type: TextFieldTypes.PLAIN_TEXT, - hintText: appLocalizations.product_field_website_title, - ), - SizedBox(height: _heightSpace), - ], + SmoothCardWithRoundedHeader( + title: appLocalizations.product_field_website_title, + iconTitle: const Icon(Icons.link), + child: Padding( + padding: EdgeInsetsDirectional.only( + bottom: MEDIUM_SPACE, + start: MEDIUM_SPACE, + end: MEDIUM_SPACE, + ), + child: Column( + children: [ + SmoothTextFormField( + controller: _websiteController, + type: TextFieldTypes.PLAIN_TEXT, + hintText: + appLocalizations.product_field_website_title, + ), + ], + ), ), ), ], diff --git a/packages/smooth_app/lib/pages/product/simple_input_page.dart b/packages/smooth_app/lib/pages/product/simple_input_page.dart index 7286788fc004..f2d4bdc9ae6f 100644 --- a/packages/smooth_app/lib/pages/product/simple_input_page.dart +++ b/packages/smooth_app/lib/pages/product/simple_input_page.dart @@ -117,12 +117,13 @@ class _SimpleInputPageState extends State { : null, body: Scrollbar( child: ListView( - padding: const EdgeInsetsDirectional.only( - top: MEDIUM_SPACE, - start: MEDIUM_SPACE, - end: MEDIUM_SPACE, - ), - children: simpleInputs), + padding: const EdgeInsetsDirectional.only( + top: MEDIUM_SPACE, + start: MEDIUM_SPACE, + end: MEDIUM_SPACE, + ), + children: simpleInputs, + ), ), bottomNavigationBar: ProductBottomButtonsBar( onSave: () async => _exitPage( From 70d8b73b6629ad1aefe4f07aacd1787c43d3ba10 Mon Sep 17 00:00:00 2001 From: Edouard Marquez Date: Mon, 6 Jan 2025 12:13:17 +0100 Subject: [PATCH 3/7] Some comments + minor fixes --- packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart | 1 + .../smooth_app/lib/pages/product/add_other_details_page.dart | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart b/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart index e64096dcc751..50834b37a734 100644 --- a/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart +++ b/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart @@ -186,6 +186,7 @@ class SmoothCardWithRoundedHeader extends StatelessWidget { } } +/// We need this [CustomPainter] to draw the background below the other card class _SmoothCardWithRoundedHeaderBackgroundPainter extends CustomPainter { _SmoothCardWithRoundedHeaderBackgroundPainter({ required Color color, diff --git a/packages/smooth_app/lib/pages/product/add_other_details_page.dart b/packages/smooth_app/lib/pages/product/add_other_details_page.dart index ee9f11f444eb..ac863e3ecc78 100644 --- a/packages/smooth_app/lib/pages/product/add_other_details_page.dart +++ b/packages/smooth_app/lib/pages/product/add_other_details_page.dart @@ -55,8 +55,8 @@ class _AddOtherDetailsPageState extends State { @override Widget build(BuildContext context) { - final Size size = MediaQuery.sizeOf(context); final AppLocalizations appLocalizations = AppLocalizations.of(context); + return WillPopScope2( onWillPop: () async => (await _mayExitPage(saving: false), null), child: SmoothScaffold( @@ -85,7 +85,7 @@ class _AddOtherDetailsPageState extends State { title: appLocalizations.product_field_website_title, iconTitle: const Icon(Icons.link), child: Padding( - padding: EdgeInsetsDirectional.only( + padding: const EdgeInsetsDirectional.only( bottom: MEDIUM_SPACE, start: MEDIUM_SPACE, end: MEDIUM_SPACE, From b0abeb19fba99d74ab938143d42cfc46fd58f94b Mon Sep 17 00:00:00 2001 From: Edouard Marquez Date: Mon, 6 Jan 2025 13:17:46 +0100 Subject: [PATCH 4/7] Add missing bottom space --- .../smooth_app/lib/pages/product/simple_input_widget.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/smooth_app/lib/pages/product/simple_input_widget.dart b/packages/smooth_app/lib/pages/product/simple_input_widget.dart index 0bc1d750ec58..c8f4bca1b2bb 100644 --- a/packages/smooth_app/lib/pages/product/simple_input_widget.dart +++ b/packages/smooth_app/lib/pages/product/simple_input_widget.dart @@ -167,7 +167,10 @@ class _SimpleInputWidgetState extends State { shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), ), - if (extraWidget != null) extraWidget, + if (extraWidget != null) + extraWidget + else + const SizedBox(height: MEDIUM_SPACE), ], ); From 1b4c60ce48913ce48334bf27ccf92a237a94bf65 Mon Sep 17 00:00:00 2001 From: Edouard Marquez Date: Mon, 6 Jan 2025 13:18:52 +0100 Subject: [PATCH 5/7] Fix page padding --- .../smooth_app/lib/pages/product/simple_input_page.dart | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/smooth_app/lib/pages/product/simple_input_page.dart b/packages/smooth_app/lib/pages/product/simple_input_page.dart index f2d4bdc9ae6f..794efdfa66ea 100644 --- a/packages/smooth_app/lib/pages/product/simple_input_page.dart +++ b/packages/smooth_app/lib/pages/product/simple_input_page.dart @@ -117,11 +117,7 @@ class _SimpleInputPageState extends State { : null, body: Scrollbar( child: ListView( - padding: const EdgeInsetsDirectional.only( - top: MEDIUM_SPACE, - start: MEDIUM_SPACE, - end: MEDIUM_SPACE, - ), + padding: const EdgeInsetsDirectional.all(MEDIUM_SPACE), children: simpleInputs, ), ), From da229ab3047cacf6873dbdd9673863bb759e4865 Mon Sep 17 00:00:00 2001 From: Edouard Marquez Date: Mon, 6 Jan 2025 19:23:01 +0100 Subject: [PATCH 6/7] Help is better embedded --- .../lib/generic_lib/widgets/smooth_card.dart | 63 ++++++++------ packages/smooth_app/lib/l10n/app_en.arb | 12 +++ packages/smooth_app/lib/l10n/app_fr.arb | 9 ++ .../pages/product/add_other_details_page.dart | 2 +- .../pages/product/simple_input_widget.dart | 82 ++++++++++++++++++- 5 files changed, 142 insertions(+), 26 deletions(-) diff --git a/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart b/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart index 50834b37a734..dca9d4ea0d8b 100644 --- a/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart +++ b/packages/smooth_app/lib/generic_lib/widgets/smooth_card.dart @@ -108,13 +108,17 @@ class SmoothCardWithRoundedHeader extends StatelessWidget { const SmoothCardWithRoundedHeader({ required this.title, required this.child, - this.iconTitle, + this.leading, + this.trailing, + this.titlePadding, this.contentPadding, }); final String title; - final Widget? iconTitle; + final Widget? leading; + final Widget? trailing; final Widget child; + final EdgeInsetsGeometry? titlePadding; final EdgeInsetsGeometry? contentPadding; @override @@ -137,36 +141,49 @@ class SmoothCardWithRoundedHeader extends StatelessWidget { radius: ROUNDED_RADIUS, ), child: Padding( - padding: const EdgeInsetsDirectional.symmetric( - vertical: MEDIUM_SPACE, - horizontal: LARGE_SPACE, - ), + padding: titlePadding ?? + const EdgeInsetsDirectional.symmetric( + vertical: MEDIUM_SPACE, + horizontal: LARGE_SPACE, + ), child: Row( children: [ - if (iconTitle != null) - DecoratedBox( - decoration: const BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, + if (leading != null) + IconTheme( + data: IconThemeData( + color: color, + size: 18.0, ), - child: Padding( - padding: const EdgeInsetsDirectional.all(SMALL_SPACE), - child: IconTheme( - data: IconThemeData( - color: color, - size: 18.0, - ), - child: iconTitle!, + child: DecoratedBox( + decoration: const BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + child: Padding( + padding: const EdgeInsetsDirectional.all(SMALL_SPACE), + child: leading, ), ), ), const SizedBox(width: MEDIUM_SPACE), - Text( - title, - style: themeData.textTheme.displaySmall?.copyWith( - color: Colors.white, + Expanded( + child: Text( + title, + style: themeData.textTheme.displaySmall?.copyWith( + color: Colors.white, + ), ), ), + if (trailing != null) ...[ + const SizedBox(width: MEDIUM_SPACE), + IconTheme( + data: const IconThemeData( + color: Colors.white, + size: 20.0, + ), + child: trailing!, + ), + ], ], ), ), diff --git a/packages/smooth_app/lib/l10n/app_en.arb b/packages/smooth_app/lib/l10n/app_en.arb index 3c62951092cc..1b066ee60525 100644 --- a/packages/smooth_app/lib/l10n/app_en.arb +++ b/packages/smooth_app/lib/l10n/app_en.arb @@ -1408,6 +1408,18 @@ "@edit_product_label_short": { "description": "Edit product button short label (only the verb)" }, + "edit_product_form_item_help": "How to enter \"{value}\"?", + "@edit_product_form_item_help": { + "description": "Help text for the input field. Eg 'How to enter stores?'", + "placeholders": { + "value": { + "type": "String" + } + } + }, + "@edit_product_label_short": { + "description": "Edit product button short label (only the verb)" + }, "edit_product_form_item_add_action": "Add a new {itemType}", "description": "Tooltip to show when the user long presses the (+) button", "@edit_product_form_item_add_action": { diff --git a/packages/smooth_app/lib/l10n/app_fr.arb b/packages/smooth_app/lib/l10n/app_fr.arb index 9157d858c3e2..5c326e68804f 100644 --- a/packages/smooth_app/lib/l10n/app_fr.arb +++ b/packages/smooth_app/lib/l10n/app_fr.arb @@ -1408,6 +1408,15 @@ "@edit_product_label_short": { "description": "Edit product button short label (only the verb)" }, + "edit_product_form_item_help": "Comment saisir : \"{value}\" ?", + "@edit_product_form_item_help": { + "description": "Help text for the input field. Eg 'How to enter stores?'", + "placeholders": { + "value": { + "type": "String" + } + } + }, "edit_product_form_item_add_action": "Ajouter un nouveau {itemType}", "description": "Infobulle à afficher lorsque l'utilisateur appuie longuement sur le bouton (+)", "@edit_product_form_item_add_action": { diff --git a/packages/smooth_app/lib/pages/product/add_other_details_page.dart b/packages/smooth_app/lib/pages/product/add_other_details_page.dart index ac863e3ecc78..9a5f54934896 100644 --- a/packages/smooth_app/lib/pages/product/add_other_details_page.dart +++ b/packages/smooth_app/lib/pages/product/add_other_details_page.dart @@ -83,7 +83,7 @@ class _AddOtherDetailsPageState extends State { children: [ SmoothCardWithRoundedHeader( title: appLocalizations.product_field_website_title, - iconTitle: const Icon(Icons.link), + leading: const Icon(Icons.link), child: Padding( padding: const EdgeInsetsDirectional.only( bottom: MEDIUM_SPACE, diff --git a/packages/smooth_app/lib/pages/product/simple_input_widget.dart b/packages/smooth_app/lib/pages/product/simple_input_widget.dart index c8f4bca1b2bb..20e73759d4f9 100644 --- a/packages/smooth_app/lib/pages/product/simple_input_widget.dart +++ b/packages/smooth_app/lib/pages/product/simple_input_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/generic_lib/bottom_sheets/smooth_bottom_sheet.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; import 'package:smooth_app/helpers/collections_helper.dart'; @@ -9,6 +10,7 @@ import 'package:smooth_app/pages/product/explanation_widget.dart'; import 'package:smooth_app/pages/product/owner_field_info.dart'; import 'package:smooth_app/pages/product/simple_input_page_helpers.dart'; import 'package:smooth_app/pages/product/simple_input_text_field.dart'; +import 'package:smooth_app/resources/app_icons.dart' as icons; import 'package:smooth_app/themes/smooth_theme.dart'; import 'package:smooth_app/themes/smooth_theme_colors.dart'; import 'package:smooth_app/themes/theme_provider.dart'; @@ -72,7 +74,7 @@ class _SimpleInputWidgetState extends State { final Widget child = Column( mainAxisSize: MainAxisSize.min, children: [ - if (explanations != null) + if (explanations != null && !widget.displayTitle) Padding( padding: const EdgeInsetsDirectional.symmetric(horizontal: SMALL_SPACE), @@ -176,8 +178,22 @@ class _SimpleInputWidgetState extends State { if (widget.displayTitle) { return SmoothCardWithRoundedHeader( - iconTitle: widget.helper.getIcon(), + leading: widget.helper.getIcon(), title: widget.helper.getTitle(appLocalizations), + trailing: explanations != null + ? _ExplanationTitleIcon( + text: explanations, + type: widget.helper.getTitle(appLocalizations), + ) + : null, + titlePadding: explanations != null + ? const EdgeInsetsDirectional.only( + top: SMALL_SPACE, + bottom: SMALL_SPACE, + start: LARGE_SPACE, + end: SMALL_SPACE, + ) + : null, child: child, ); } else { @@ -225,3 +241,65 @@ class _SimpleInputWidgetState extends State { } } } + +class _ExplanationTitleIcon extends StatelessWidget { + const _ExplanationTitleIcon({ + required this.type, + required this.text, + }); + + final String type; + final String text; + + @override + Widget build(BuildContext context) { + final String title = + AppLocalizations.of(context).edit_product_form_item_help(type); + + return Material( + type: MaterialType.transparency, + child: Semantics( + label: title, + button: true, + excludeSemantics: true, + child: Tooltip( + message: title, + child: InkWell( + customBorder: const CircleBorder(), + onTap: () { + showSmoothModalSheet( + context: context, + builder: (BuildContext context) { + return SmoothModalSheet( + title: title, + prefixIndicator: true, + body: Padding( + padding: EdgeInsetsDirectional.only( + start: MEDIUM_SPACE, + end: MEDIUM_SPACE, + top: VERY_SMALL_SPACE, + bottom: VERY_SMALL_SPACE + + MediaQuery.viewPaddingOf(context).bottom, + ), + child: Text( + text, + style: const TextStyle( + fontSize: 15.0, + height: 1.7, + ), + ), + ), + ); + }, + ); + }, + child: const Padding( + padding: EdgeInsetsDirectional.all(MEDIUM_SPACE), + child: icons.Help(), + ), + ), + ), + ), + ); + } +} From 0ca1d44a19c2a6270800898ddb47c341bd56d0c0 Mon Sep 17 00:00:00 2001 From: Edouard Marquez Date: Mon, 6 Jan 2025 19:29:28 +0100 Subject: [PATCH 7/7] Change ingredients instructions --- packages/smooth_app/lib/l10n/app_en.arb | 2 +- packages/smooth_app/lib/l10n/app_fr.arb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/smooth_app/lib/l10n/app_en.arb b/packages/smooth_app/lib/l10n/app_en.arb index 1b066ee60525..0cc1a6d2605f 100644 --- a/packages/smooth_app/lib/l10n/app_en.arb +++ b/packages/smooth_app/lib/l10n/app_en.arb @@ -528,7 +528,7 @@ }, "ingredients": "Ingredients", "@ingredients": {}, - "ingredients_editing_instructions": "Keep the original order. Indicate the percentage when specified. Separate with a comma or hyphen, use parentheses for ingredients of an ingredient, and indicate allergens between underscores.", + "ingredients_editing_instructions": "Keep the original order. Indicate the percentage when specified. Separate with a comma or hyphen and use parentheses for ingredients of an ingredient.", "ingredients_editing_error": "Failed to save the ingredients.", "ingredients_editing_image_error": "Failed to get a new ingredients image.", "ingredients_editing_title": "Edit Ingredients", diff --git a/packages/smooth_app/lib/l10n/app_fr.arb b/packages/smooth_app/lib/l10n/app_fr.arb index 5c326e68804f..38a8a5ddbaf5 100644 --- a/packages/smooth_app/lib/l10n/app_fr.arb +++ b/packages/smooth_app/lib/l10n/app_fr.arb @@ -528,7 +528,7 @@ }, "ingredients": "Ingrédients", "@ingredients": {}, - "ingredients_editing_instructions": "Conservez l'ordre original. Indiquer le pourcentage lorsque spécifié. Séparez par une virgule ou un trait d'union, utilisez des parenthèses pour les ingrédients d'un ingrédient et indiquez les allergènes entre tirets du bas.", + "ingredients_editing_instructions": "Conservez l'ordre original. Indiquer le pourcentage lorsque spécifié. Séparez par une virgule ou un trait d'union, utilisez des parenthèses pour les ingrédients d'un ingrédient.", "ingredients_editing_error": "Échec de l’enregistrement des ingrédients.", "ingredients_editing_image_error": "Échec de l'obtention d'une nouvelle image d'ingrédients.", "ingredients_editing_title": "Modifier les ingrédients",