From af923d67bf8f775384321c6e989fb42c375975d9 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 19 Mar 2025 15:34:38 -0400 Subject: [PATCH] chore: highlight selected item in dropdowns (#2181) --- .../activity_planner_page.dart | 28 +++++++-- .../conversation_bot_mode_dynamic_zone.dart | 3 - .../conversation_bot_mode_select.dart | 22 +++++-- .../conversation_bot_settings_form.dart | 50 ++++----------- .../widgets/language_level_dropdown.dart | 45 +++++++------ .../common/widgets/dropdown_text_button.dart | 63 +++++++++++++++++++ .../widgets/country_picker_tile.dart | 16 ++++- .../widgets/p_language_dropdown.dart | 27 +++++--- 8 files changed, 174 insertions(+), 80 deletions(-) create mode 100644 lib/pangea/common/widgets/dropdown_text_button.dart diff --git a/lib/pangea/activity_planner/activity_planner_page.dart b/lib/pangea/activity_planner/activity_planner_page.dart index a136156c8..2abe06419 100644 --- a/lib/pangea/activity_planner/activity_planner_page.dart +++ b/lib/pangea/activity_planner/activity_planner_page.dart @@ -15,6 +15,7 @@ import 'package:fluffychat/pangea/activity_planner/media_enum.dart'; import 'package:fluffychat/pangea/activity_planner/suggestion_form_field.dart'; import 'package:fluffychat/pangea/activity_planner/topic_list_repo.dart'; import 'package:fluffychat/pangea/chat_settings/widgets/language_level_dropdown.dart'; +import 'package:fluffychat/pangea/common/widgets/dropdown_text_button.dart'; import 'package:fluffychat/pangea/instructions/instructions_enum.dart'; import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart'; import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; @@ -256,23 +257,42 @@ class ActivityPlannerPageState extends State { ), const SizedBox(height: 24), DropdownButtonFormField2( + customButton: CustomDropdownTextButton( + text: _selectedMedia.toDisplayCopyUsingL10n(context), + ), + menuItemStyleData: const MenuItemStyleData( + padding: EdgeInsets.zero, // Remove default padding + ), decoration: InputDecoration(labelText: l10n.mediaLabel), + isExpanded: true, + dropdownStyleData: DropdownStyleData( + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .surfaceContainerHigh, + ), + ), items: MediaEnum.values .map( (e) => DropdownMenuItem( value: e, - child: Text(e.toDisplayCopyUsingL10n(context)), + child: DropdownTextButton( + text: e.toDisplayCopyUsingL10n(context), + isSelected: _selectedMedia == e, + ), ), ) .toList(), - onChanged: (val) => - _selectedMedia = val ?? MediaEnum.nan, + onChanged: (val) { + setState(() => _selectedMedia = val ?? MediaEnum.nan); + }, value: _selectedMedia, ), const SizedBox(height: 24), LanguageLevelDropdown( initialLevel: _selectedCefrLevel, - onChanged: (val) => _selectedCefrLevel = val, + onChanged: (val) => + setState(() => _selectedCefrLevel = val), ), const SizedBox(height: 24), PLanguageDropdown( diff --git a/lib/pangea/chat_settings/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart b/lib/pangea/chat_settings/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart index f7906baf4..f3a0f918f 100644 --- a/lib/pangea/chat_settings/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart +++ b/lib/pangea/chat_settings/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart @@ -34,8 +34,6 @@ class ConversationBotModeDynamicZone extends StatelessWidget { decoration: InputDecoration( hintText: L10n.of(context) .conversationBotDiscussionZone_discussionTopicPlaceholder, - contentPadding: - const EdgeInsets.symmetric(horizontal: 28.0, vertical: 12.0), ), controller: discussionTopicController, validator: (value) => enabled && @@ -57,7 +55,6 @@ class ConversationBotModeDynamicZone extends StatelessWidget { decoration: InputDecoration( hintText: L10n.of(context) .conversationBotDiscussionZone_discussionKeywordsPlaceholder, - contentPadding: const EdgeInsets.symmetric(horizontal: 28.0), ), controller: discussionKeywordsController, enabled: hasPermission && enabled, diff --git a/lib/pangea/chat_settings/widgets/conversation_bot/conversation_bot_mode_select.dart b/lib/pangea/chat_settings/widgets/conversation_bot/conversation_bot_mode_select.dart index 48290379b..7362c265d 100644 --- a/lib/pangea/chat_settings/widgets/conversation_bot/conversation_bot_mode_select.dart +++ b/lib/pangea/chat_settings/widgets/conversation_bot/conversation_bot_mode_select.dart @@ -4,6 +4,7 @@ import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:fluffychat/pangea/chat_settings/constants/bot_mode.dart'; +import 'package:fluffychat/pangea/common/widgets/dropdown_text_button.dart'; class ConversationBotModeSelect extends StatelessWidget { final String? initialMode; @@ -32,15 +33,28 @@ class ConversationBotModeSelect extends StatelessWidget { }; return DropdownButtonFormField2( + customButton: initialMode != null && options.containsKey(initialMode) + ? CustomDropdownTextButton( + text: options[initialMode]!, + ) + : null, + menuItemStyleData: const MenuItemStyleData( + padding: EdgeInsets.zero, // Remove default padding + ), + isExpanded: true, + dropdownStyleData: DropdownStyleData( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainerHigh, + ), + ), hint: Text(L10n.of(context).selectBotChatMode), items: [ for (final entry in options.entries) DropdownMenuItem( value: entry.key, - child: Text( - entry.value, - overflow: TextOverflow.clip, - textAlign: TextAlign.center, + child: DropdownTextButton( + text: entry.value, + isSelected: initialMode == entry.key, ), ), ], diff --git a/lib/pangea/chat_settings/widgets/conversation_bot/conversation_bot_settings_form.dart b/lib/pangea/chat_settings/widgets/conversation_bot/conversation_bot_settings_form.dart index 441504b67..81abe7be7 100644 --- a/lib/pangea/chat_settings/widgets/conversation_bot/conversation_bot_settings_form.dart +++ b/lib/pangea/chat_settings/widgets/conversation_bot/conversation_bot_settings_form.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart'; @@ -9,6 +8,8 @@ import 'package:fluffychat/pangea/chat_settings/widgets/conversation_bot/convers import 'package:fluffychat/pangea/chat_settings/widgets/conversation_bot/conversation_bot_no_permission_dialog.dart'; import 'package:fluffychat/pangea/chat_settings/widgets/language_level_dropdown.dart'; import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; +import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/learning_settings/widgets/p_language_dropdown.dart'; import 'package:fluffychat/widgets/matrix.dart'; class ConversationBotSettingsForm extends StatelessWidget { @@ -48,44 +49,15 @@ class ConversationBotSettingsForm extends StatelessWidget { children: [ InkWell( onTap: hasPermission ? null : () => showNoPermissionDialog(context), - child: DropdownButtonFormField2( - dropdownStyleData: const DropdownStyleData( - padding: EdgeInsets.zero, - ), - hint: Text( - L10n.of(context).selectBotLanguage, - overflow: TextOverflow.clip, - textAlign: TextAlign.center, - ), - value: botOptions.targetLanguage, - isExpanded: true, - items: MatrixState.pangeaController.pLanguageStore.targetOptions - .map((language) { - return DropdownMenuItem( - value: language.langCode, - child: Text( - language.getDisplayName(context) ?? language.langCode, - overflow: TextOverflow.clip, - textAlign: TextAlign.center, - ), - ); - }).toList(), - onChanged: hasPermission && enabled ? onUpdateBotLanguage : null, - ), - ), - const SizedBox(height: 12), - InkWell( - onTap: hasPermission ? null : () => showNoPermissionDialog(context), - child: DropdownButtonFormField2( - hint: Text( - L10n.of(context).chooseVoice, - overflow: TextOverflow.clip, - textAlign: TextAlign.center, - ), - value: botOptions.targetVoice, - isExpanded: true, - items: const [], - onChanged: hasPermission && enabled ? onUpdateBotVoice : null, + child: PLanguageDropdown( + languages: + MatrixState.pangeaController.pLanguageStore.targetOptions, + onChange: (lang) => hasPermission && enabled + ? onUpdateBotLanguage(lang.langCode) + : null, + initialLanguage: botOptions.targetLanguage != null + ? PLanguageStore.byLangCode(botOptions.targetLanguage!) + : null, ), ), const SizedBox(height: 12), diff --git a/lib/pangea/chat_settings/widgets/language_level_dropdown.dart b/lib/pangea/chat_settings/widgets/language_level_dropdown.dart index bf9231e0b..b3408eaae 100644 --- a/lib/pangea/chat_settings/widgets/language_level_dropdown.dart +++ b/lib/pangea/chat_settings/widgets/language_level_dropdown.dart @@ -5,6 +5,7 @@ import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:fluffychat/pangea/chat_settings/utils/language_level_copy.dart'; +import 'package:fluffychat/pangea/common/widgets/dropdown_text_button.dart'; import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; class LanguageLevelDropdown extends StatelessWidget { @@ -26,39 +27,47 @@ class LanguageLevelDropdown extends StatelessWidget { @override Widget build(BuildContext context) { return DropdownButtonFormField2( - decoration: InputDecoration(labelText: L10n.of(context).cefrLevelLabel), - hint: Text( - L10n.of(context).selectLanguageLevel, - overflow: TextOverflow.clip, - textAlign: TextAlign.center, + customButton: initialLevel != null && + LanguageLevelTypeEnum.values.contains(initialLevel) + ? CustomDropdownTextButton( + text: LanguageLevelTextPicker.languageLevelText( + context, + initialLevel!, + ), + ) + : null, + menuItemStyleData: const MenuItemStyleData( + padding: EdgeInsets.zero, // Remove default padding + ), + decoration: InputDecoration( + labelText: L10n.of(context).cefrLevelLabel, + ), + isExpanded: true, + dropdownStyleData: DropdownStyleData( + maxHeight: kIsWeb ? 500 : null, + decoration: BoxDecoration( + color: backgroundColor ?? + Theme.of(context).colorScheme.surfaceContainerHigh, + ), ), - value: initialLevel, items: LanguageLevelTypeEnum.values.map((LanguageLevelTypeEnum levelOption) { return DropdownMenuItem( value: levelOption, - child: Text( - LanguageLevelTextPicker.languageLevelText( + child: DropdownTextButton( + text: LanguageLevelTextPicker.languageLevelText( context, levelOption, ), - overflow: TextOverflow.clip, - textAlign: TextAlign.center, + isSelected: initialLevel == levelOption, ), ); }).toList(), onChanged: enabled ? (value) => onChanged?.call(value as LanguageLevelTypeEnum) : null, + value: initialLevel, validator: validator, - dropdownStyleData: DropdownStyleData( - maxHeight: kIsWeb ? 500 : null, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(14), - color: backgroundColor ?? - Theme.of(context).colorScheme.surfaceContainerHigh, - ), - ), ); } } diff --git a/lib/pangea/common/widgets/dropdown_text_button.dart b/lib/pangea/common/widgets/dropdown_text_button.dart new file mode 100644 index 000000000..ce3f5e6f4 --- /dev/null +++ b/lib/pangea/common/widgets/dropdown_text_button.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; + +class DropdownTextButton extends StatelessWidget { + final bool isSelected; + final String text; + + const DropdownTextButton({ + required this.text, + this.isSelected = false, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Container( + color: isSelected + ? Theme.of(context) + .colorScheme + .primary + .withAlpha(20) // Highlight selected + : Colors.transparent, + padding: const EdgeInsets.symmetric( + vertical: 12, + horizontal: 12, + ), + child: Row( + children: [ + Text( + text, + overflow: TextOverflow.clip, + ), + ], + ), + ); + } +} + +class CustomDropdownTextButton extends StatelessWidget { + final String text; + + const CustomDropdownTextButton({ + required this.text, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: Text( + text, + overflow: TextOverflow.clip, + ), + ), + Icon( + Icons.arrow_drop_down, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ], + ); + } +} diff --git a/lib/pangea/learning_settings/widgets/country_picker_tile.dart b/lib/pangea/learning_settings/widgets/country_picker_tile.dart index 69d3f215a..9a4fee60d 100644 --- a/lib/pangea/learning_settings/widgets/country_picker_tile.dart +++ b/lib/pangea/learning_settings/widgets/country_picker_tile.dart @@ -43,6 +43,9 @@ class CountryPickerDropdownState extends State { isDropdown: true, ) : null, + menuItemStyleData: const MenuItemStyleData( + padding: EdgeInsets.zero, + ), isExpanded: true, decoration: InputDecoration( labelText: L10n.of(context).countryInformation, @@ -50,7 +53,7 @@ class CountryPickerDropdownState extends State { dropdownStyleData: DropdownStyleData( maxHeight: kIsWeb ? 500 : null, decoration: BoxDecoration( - borderRadius: BorderRadius.circular(14), + // borderRadius: BorderRadius.circular(14), color: Theme.of(context).colorScheme.surfaceContainerHigh, ), ), @@ -58,7 +61,16 @@ class CountryPickerDropdownState extends State { ...countries.map( (country) => DropdownMenuItem( value: country, - child: CountryPickerTile(country), + child: Container( + color: widget.learningController.country == country + ? Theme.of(context).colorScheme.primary.withAlpha(20) + : Colors.transparent, + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 12, + ), + child: CountryPickerTile(country), + ), ), ), ], diff --git a/lib/pangea/learning_settings/widgets/p_language_dropdown.dart b/lib/pangea/learning_settings/widgets/p_language_dropdown.dart index 597e04ea4..afbcee914 100644 --- a/lib/pangea/learning_settings/widgets/p_language_dropdown.dart +++ b/lib/pangea/learning_settings/widgets/p_language_dropdown.dart @@ -16,7 +16,7 @@ class PLanguageDropdown extends StatefulWidget { final Function(LanguageModel) onChange; final bool showMultilingual; final bool isL2List; - final String decorationText; + final String? decorationText; final String? error; final String? Function(LanguageModel?)? validator; final Color? backgroundColor; @@ -28,7 +28,7 @@ class PLanguageDropdown extends StatefulWidget { required this.onChange, required this.initialLanguage, this.showMultilingual = false, - required this.decorationText, + this.decorationText, this.isL2List = false, this.error, this.validator, @@ -95,21 +95,19 @@ class PLanguageDropdownState extends State { isDropdown: true, ) : null, + menuItemStyleData: const MenuItemStyleData( + padding: EdgeInsets.zero, + ), decoration: InputDecoration( labelText: widget.decorationText, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(36.0), - ), enabledBorder: hasError ? OutlineInputBorder( - borderRadius: BorderRadius.circular(36.0), borderSide: BorderSide(color: Theme.of(context).colorScheme.error), ) : null, focusedBorder: hasError ? OutlineInputBorder( - borderRadius: BorderRadius.circular(36.0), borderSide: BorderSide( color: Theme.of(context).colorScheme.error, width: 2, @@ -138,9 +136,18 @@ class PLanguageDropdownState extends State { ...sortedLanguages.map( (languageModel) => DropdownMenuItem( value: languageModel, - child: LanguageDropDownEntry( - languageModel: languageModel, - isL2List: widget.isL2List, + child: Container( + color: widget.initialLanguage == languageModel + ? Theme.of(context).colorScheme.primary.withAlpha(20) + : Colors.transparent, + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 12, + ), + child: LanguageDropDownEntry( + languageModel: languageModel, + isL2List: widget.isL2List, + ), ), ), ),