diff --git a/packages/smooth_app/lib/pages/product/add_basic_details_page.dart b/packages/smooth_app/lib/pages/product/add_basic_details_page.dart index 96d2bf307b0..008d12e3d3a 100644 --- a/packages/smooth_app/lib/pages/product/add_basic_details_page.dart +++ b/packages/smooth_app/lib/pages/product/add_basic_details_page.dart @@ -90,8 +90,18 @@ class _AddBasicDetailsPageState extends State { @override Widget build(BuildContext context) { - final Size size = MediaQuery.sizeOf(context); final AppLocalizations appLocalizations = AppLocalizations.of(context); + + Widget child = _buildForm(appLocalizations, context); + if (_hasOwnerField()) { + child = Column( + children: [ + Expanded(child: child), + const OwnerFieldBanner(), + ], + ); + } + return WillPopScope2( onWillPop: () async => (await _mayExitPage(saving: false), null), child: UnfocusFieldWhenTapOutside( @@ -102,161 +112,158 @@ class _AddBasicDetailsPageState extends State { title: appLocalizations.basic_details, product: _product, ), - body: Form( - key: _formKey, - child: Scrollbar( - child: ListView( + body: child, + bottomNavigationBar: ProductBottomButtonsBar( + onSave: () async => _exitPage( + await _mayExitPage(saving: true), + ), + onCancel: () async => _exitPage( + await _mayExitPage(saving: false), + ), + ), + ), + ), + ); + } + + Form _buildForm(AppLocalizations appLocalizations, BuildContext context) { + final Size size = MediaQuery.sizeOf(context); + + return Form( + key: _formKey, + child: Scrollbar( + child: ListView( + children: [ + Align( + alignment: AlignmentDirectional.topStart, + child: ProductImageCarousel( + _product, + height: size.height * 0.20, + ), + ), + SizedBox(height: _heightSpace), + Padding( + padding: EdgeInsets.symmetric( + horizontal: size.width * 0.05, + ), + child: Column( children: [ - Align( - alignment: AlignmentDirectional.topStart, - child: ProductImageCarousel( - _product, - height: size.height * 0.20, - ), + Text( + appLocalizations.barcode_barcode(_product.barcode!), + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.bold, + ), ), SizedBox(height: _heightSpace), - Padding( - padding: EdgeInsets.symmetric( - horizontal: size.width * 0.05, - ), - child: Column( - children: [ - Text( - appLocalizations.barcode_barcode(_product.barcode!), - style: - Theme.of(context).textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.bold, + ConsumerFilter( + buildWhen: ( + UserPreferences? previousValue, + UserPreferences currentValue, + ) { + return previousValue?.getFlag(UserPreferencesDevMode + .userPreferencesFlagSpellCheckerOnOcr) != + currentValue.getFlag(UserPreferencesDevMode + .userPreferencesFlagSpellCheckerOnOcr); + }, + builder: (BuildContext context, UserPreferences prefs, + Widget? child) { + if (_multilingualHelper.isMonolingual()) { + return SmoothTextFormField( + suffixIcon: _getOwnerFieldIcon( + ProductField.NAME, + ), + controller: _productNameController, + type: TextFieldTypes.PLAIN_TEXT, + hintText: appLocalizations.product_name, + spellCheckConfiguration: (prefs.getFlag( + UserPreferencesDevMode + .userPreferencesFlagSpellCheckerOnOcr) ?? + false) && + (Platform.isAndroid || Platform.isIOS) + ? const SpellCheckConfiguration() + : const SpellCheckConfiguration.disabled(), + ); + } else { + return Card( + child: Column( + children: [ + _multilingualHelper.getLanguageSelector( + setState: setState, + product: _product, + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: SmoothTextFormField( + suffixIcon: _getOwnerFieldIcon( + ProductField.NAME_IN_LANGUAGES, + language: _multilingualHelper + .getCurrentLanguage(), ), - ), - SizedBox(height: _heightSpace), - ConsumerFilter( - buildWhen: ( - UserPreferences? previousValue, - UserPreferences currentValue, - ) { - return previousValue?.getFlag(UserPreferencesDevMode - .userPreferencesFlagSpellCheckerOnOcr) != - currentValue.getFlag(UserPreferencesDevMode - .userPreferencesFlagSpellCheckerOnOcr); - }, - builder: (BuildContext context, UserPreferences prefs, - Widget? child) { - if (_multilingualHelper.isMonolingual()) { - return SmoothTextFormField( - suffixIcon: _getOwnerFieldIcon( - ProductField.NAME, - ), - controller: _productNameController, - type: TextFieldTypes.PLAIN_TEXT, - hintText: appLocalizations.product_name, - spellCheckConfiguration: (prefs.getFlag( - UserPreferencesDevMode - .userPreferencesFlagSpellCheckerOnOcr) ?? - false) && - (Platform.isAndroid || Platform.isIOS) - ? const SpellCheckConfiguration() - : const SpellCheckConfiguration.disabled(), - ); - } else { - return Card( - child: Column( - children: [ - _multilingualHelper.getLanguageSelector( - setState: setState, - product: _product, - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: SmoothTextFormField( - suffixIcon: _getOwnerFieldIcon( - ProductField.NAME_IN_LANGUAGES, - language: _multilingualHelper - .getCurrentLanguage(), - ), - controller: _productNameController, - type: TextFieldTypes.PLAIN_TEXT, - hintText: appLocalizations.product_name, - spellCheckConfiguration: (prefs.getFlag( - UserPreferencesDevMode - .userPreferencesFlagSpellCheckerOnOcr) ?? - false) && - (Platform.isAndroid || - Platform.isIOS) - ? const SpellCheckConfiguration() - : const SpellCheckConfiguration - .disabled(), - ), - ), - ], - ), - ); - } - }, - ), - SizedBox(height: _heightSpace), - LayoutBuilder( - builder: ( - final BuildContext context, - final BoxConstraints constraints, - ) => - SmoothAutocompleteTextField( - focusNode: _focusNode, - controller: _brandNameController, - autocompleteKey: _autocompleteKey, - allowEmojis: false, - hintText: appLocalizations.brand_name, - constraints: constraints, - suffixIcon: _getOwnerFieldIcon( - ProductField.BRANDS, - ), - manager: AutocompleteManager( - TaxonomyNameAutocompleter( - taxonomyNames: [ - TaxonomyName.brand - ], - // for brands, language must be English - language: OpenFoodFactsLanguage.ENGLISH, - user: ProductQuery.getReadUser(), - limit: 25, - fuzziness: Fuzziness.none, - uriHelper: ProductQuery.getUriProductHelper( - productType: _product.productType, + controller: _productNameController, + type: TextFieldTypes.PLAIN_TEXT, + hintText: appLocalizations.product_name, + spellCheckConfiguration: (prefs.getFlag( + UserPreferencesDevMode + .userPreferencesFlagSpellCheckerOnOcr) ?? + false) && + (Platform.isAndroid || Platform.isIOS) + ? const SpellCheckConfiguration() + : const SpellCheckConfiguration + .disabled(), ), ), - ), + ], ), - ), - SizedBox(height: _heightSpace), - SmoothTextFormField( - suffixIcon: _getOwnerFieldIcon( - ProductField.QUANTITY, + ); + } + }, + ), + SizedBox(height: _heightSpace), + LayoutBuilder( + builder: ( + final BuildContext context, + final BoxConstraints constraints, + ) => + SmoothAutocompleteTextField( + focusNode: _focusNode, + controller: _brandNameController, + autocompleteKey: _autocompleteKey, + allowEmojis: false, + hintText: appLocalizations.brand_name, + constraints: constraints, + suffixIcon: _getOwnerFieldIcon( + ProductField.BRANDS, + ), + manager: AutocompleteManager( + TaxonomyNameAutocompleter( + taxonomyNames: [TaxonomyName.brand], + // for brands, language must be English + language: OpenFoodFactsLanguage.ENGLISH, + user: ProductQuery.getReadUser(), + limit: 25, + fuzziness: Fuzziness.none, + uriHelper: ProductQuery.getUriProductHelper( + productType: _product.productType, ), - controller: _weightController, - type: TextFieldTypes.PLAIN_TEXT, - hintText: appLocalizations.quantity, ), - if (_hasOwnerField()) - const Padding( - padding: EdgeInsets.only(top: LARGE_SPACE), - child: Card(child: OwnerFieldInfo()), - ), - // in order to be able to scroll suggestions - const SizedBox(height: 150), - ], + ), + ), + ), + SizedBox(height: _heightSpace), + SmoothTextFormField( + suffixIcon: _getOwnerFieldIcon( + ProductField.QUANTITY, ), + controller: _weightController, + type: TextFieldTypes.PLAIN_TEXT, + hintText: appLocalizations.quantity, ), + // in order to be able to scroll suggestions + const SizedBox(height: 150), ], ), ), - ), - bottomNavigationBar: ProductBottomButtonsBar( - onSave: () async => _exitPage( - await _mayExitPage(saving: true), - ), - onCancel: () async => _exitPage( - await _mayExitPage(saving: false), - ), - ), + ], ), ), ); diff --git a/packages/smooth_app/lib/pages/product/owner_field_info.dart b/packages/smooth_app/lib/pages/product/owner_field_info.dart index 66329284b0c..2912545a758 100644 --- a/packages/smooth_app/lib/pages/product/owner_field_info.dart +++ b/packages/smooth_app/lib/pages/product/owner_field_info.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:smooth_app/widgets/smooth_banner.dart'; /// Icon to display when the product field value is "producer provided". const IconData _ownerFieldIconData = Icons.factory; @@ -23,6 +24,21 @@ class OwnerFieldInfo extends StatelessWidget { } } +class OwnerFieldBanner extends StatelessWidget { + const OwnerFieldBanner({super.key}); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + + return SmoothBanner( + icon: const OwnerFieldIcon(), + title: appLocalizations.owner_field_info_title, + content: appLocalizations.owner_field_info_message, + ); + } +} + /// Standard icon about "owner fields". class OwnerFieldIcon extends StatelessWidget { const OwnerFieldIcon(); diff --git a/packages/smooth_app/lib/themes/smooth_theme_colors.dart b/packages/smooth_app/lib/themes/smooth_theme_colors.dart index d65d73ec0d7..81ba32ec85a 100644 --- a/packages/smooth_app/lib/themes/smooth_theme_colors.dart +++ b/packages/smooth_app/lib/themes/smooth_theme_colors.dart @@ -21,7 +21,7 @@ class SmoothColorsThemeExtension }); SmoothColorsThemeExtension.defaultValues() - : primaryUltraBlack = const Color(0xFF52443D), + : primaryUltraBlack = const Color(0xFF201A17), primaryBlack = const Color(0xFF341100), primaryDark = const Color(0xFF483527), primarySemiDark = const Color(0xFF52443D), diff --git a/packages/smooth_app/lib/widgets/smooth_banner.dart b/packages/smooth_app/lib/widgets/smooth_banner.dart new file mode 100644 index 00000000000..62e09fba758 --- /dev/null +++ b/packages/smooth_app/lib/widgets/smooth_banner.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; + +class SmoothBanner extends StatelessWidget { + const SmoothBanner({ + required this.icon, + required this.title, + required this.content, + super.key, + }); + + final Widget icon; + final String title; + final String content; + + @override + Widget build(BuildContext context) { + return IntrinsicHeight( + child: Row( + children: [ + Expanded( + flex: 15, + child: ExcludeSemantics( + child: Container( + width: double.infinity, + height: double.infinity, + color: const Color(0xFFE4E4E4), + padding: const EdgeInsetsDirectional.symmetric( + horizontal: LARGE_SPACE, + vertical: MEDIUM_SPACE, + ), + alignment: AlignmentDirectional.topCenter, + child: IconTheme( + data: const IconThemeData( + color: Color(0xFF373737), + ), + child: icon, + ), + ), + ), + ), + Expanded( + flex: 85, + child: Container( + width: double.infinity, + color: const Color(0xFFECECEC), + padding: const EdgeInsetsDirectional.only( + start: MEDIUM_SPACE, + end: MEDIUM_SPACE, + top: BALANCED_SPACE, + bottom: MEDIUM_SPACE, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: const TextStyle( + fontSize: 16.0, + fontWeight: FontWeight.bold, + color: Color(0xFF373737), + ), + ), + const SizedBox(height: VERY_SMALL_SPACE), + Text( + content, + style: const TextStyle( + fontSize: 14.0, + color: Color(0xFF373737), + ), + ), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/packages/smooth_app/lib/widgets/v2/smooth_buttons_bar.dart b/packages/smooth_app/lib/widgets/v2/smooth_buttons_bar.dart index 6539da51ca9..07d42d48f47 100644 --- a/packages/smooth_app/lib/widgets/v2/smooth_buttons_bar.dart +++ b/packages/smooth_app/lib/widgets/v2/smooth_buttons_bar.dart @@ -91,7 +91,7 @@ class _SmoothButtonsBar2State extends State width: double.infinity, decoration: BoxDecoration( color: widget.backgroundColor ?? - (context.lightTheme() ? Colors.white : colors!.primaryDark), + (context.lightTheme() ? Colors.white : colors!.primaryUltraBlack), boxShadow: const [ BoxShadow( color: Colors.black12, @@ -152,7 +152,8 @@ class _SmoothPositiveButton2 extends StatelessWidget { return _SmoothBaseButton2( data: data, - backgroundColor: colors.primaryBlack, + backgroundColor: + context.lightTheme() ? colors.primaryBlack : colors.primaryDark, foregroundColor: Colors.white, ); }