From 78ca8832cd9a7821dacbfa8fa728f9e8eb6f3d21 Mon Sep 17 00:00:00 2001 From: Ava Shilling <165050625+avashilling@users.noreply.github.com> Date: Fri, 16 Jan 2026 16:35:26 -0500 Subject: [PATCH] grammar practice UI updates - add morph icon to card - track last selected answer and display hint/description at the bottom after each one (like chat practice) --- .../analytics_practice_page.dart | 25 ++++++++ .../analytics_practice_view.dart | 63 ++++++++++++------- .../choice_cards/grammar_choice_card.dart | 33 +++++++++- 3 files changed, 98 insertions(+), 23 deletions(-) diff --git a/lib/pangea/analytics_practice/analytics_practice_page.dart b/lib/pangea/analytics_practice/analytics_practice_page.dart index 6fb2da694..f6f22e224 100644 --- a/lib/pangea/analytics_practice/analytics_practice_page.dart +++ b/lib/pangea/analytics_practice/analytics_practice_page.dart @@ -17,6 +17,7 @@ import 'package:fluffychat/pangea/analytics_practice/analytics_practice_view.dar import 'package:fluffychat/pangea/common/utils/async_state.dart'; import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; import 'package:fluffychat/pangea/lemmas/lemma_info_repo.dart'; +import 'package:fluffychat/pangea/morphs/morph_features_enum.dart'; import 'package:fluffychat/pangea/practice_activities/message_activity_request.dart'; import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart'; import 'package:fluffychat/pangea/practice_activities/practice_generation_repo.dart'; @@ -26,6 +27,16 @@ import 'package:fluffychat/pangea/toolbar/message_practice/practice_record_contr import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; +class SelectedMorphChoice { + final MorphFeaturesEnum feature; + final String tag; + + const SelectedMorphChoice({ + required this.feature, + required this.tag, + }); +} + class PracticeChoice { final String choiceId; final String choiceText; @@ -74,6 +85,9 @@ class AnalyticsPracticeState extends State final ValueNotifier progressNotifier = ValueNotifier(0.0); + final ValueNotifier selectedMorphChoice = + ValueNotifier(null); + final Map> _choiceTexts = {}; final Map> _choiceEmojis = {}; @@ -101,6 +115,7 @@ class AnalyticsPracticeState extends State activityState.dispose(); activityTarget.dispose(); progressNotifier.dispose(); + selectedMorphChoice.dispose(); super.dispose(); } @@ -185,6 +200,7 @@ class AnalyticsPracticeState extends State void _resetActivityState() { activityState.value = const AsyncState.loading(); activityTarget.value = null; + selectedMorphChoice.value = null; } void _resetSessionState() { @@ -285,6 +301,7 @@ class AnalyticsPracticeState extends State await _completeSession(); } else { activityState.value = const AsyncState.loading(); + selectedMorphChoice.value = null; final nextActivityCompleter = _queue.removeFirst(); activityTarget.value = nextActivityCompleter.key; @@ -401,6 +418,14 @@ class AnalyticsPracticeState extends State if (_currentActivity == null) return; final activity = _currentActivity!; + // Track the selection for display + if (activity is MorphPracticeActivityModel) { + selectedMorphChoice.value = SelectedMorphChoice( + feature: activity.morphFeature, + tag: choiceContent, + ); + } + // Update activity record PracticeRecordController.onSelectChoice( choiceContent, diff --git a/lib/pangea/analytics_practice/analytics_practice_view.dart b/lib/pangea/analytics_practice/analytics_practice_view.dart index a41f0ec3e..562eedefa 100644 --- a/lib/pangea/analytics_practice/analytics_practice_view.dart +++ b/lib/pangea/analytics_practice/analytics_practice_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pangea/analytics_details_popup/morph_meaning_widget.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_page.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_model.dart'; @@ -263,28 +264,46 @@ class _ActivityChoicesWidget extends StatelessWidget { final cardHeight = (constrainedHeight / (choices.length + 1)) .clamp(50.0, 80.0); - return Container( - constraints: const BoxConstraints(maxHeight: 400.0), - child: Column( - spacing: 4.0, - mainAxisSize: MainAxisSize.min, - children: choices - .map( - (choice) => _ChoiceCard( - activity: value, - targetId: - controller.choiceTargetId(choice.choiceId), - choiceId: choice.choiceId, - onPressed: () => controller.onSelectChoice( - choice.choiceId, - ), - cardHeight: cardHeight, - choiceText: choice.choiceText, - choiceEmoji: choice.choiceEmoji, - ), - ) - .toList(), - ), + return Column( + children: [ + Container( + constraints: const BoxConstraints(maxHeight: 400.0), + child: Column( + spacing: 4.0, + mainAxisSize: MainAxisSize.min, + children: choices + .map( + (choice) => _ChoiceCard( + activity: value, + targetId: + controller.choiceTargetId(choice.choiceId), + choiceId: choice.choiceId, + onPressed: () => controller.onSelectChoice( + choice.choiceId, + ), + cardHeight: cardHeight, + choiceText: choice.choiceText, + choiceEmoji: choice.choiceEmoji, + ), + ) + .toList(), + ), + ), + if (value.activityType == ActivityTypeEnum.grammarCategory) + ValueListenableBuilder( + valueListenable: controller.selectedMorphChoice, + builder: (context, selectedChoice, __) { + if (selectedChoice == null) { + return const SizedBox.shrink(); + } + + return MorphMeaningWidget( + feature: selectedChoice.feature, + tag: selectedChoice.tag, + ); + }, + ), + ], ); }, ), diff --git a/lib/pangea/analytics_practice/choice_cards/grammar_choice_card.dart b/lib/pangea/analytics_practice/choice_cards/grammar_choice_card.dart index c1299b611..ffc4464db 100644 --- a/lib/pangea/analytics_practice/choice_cards/grammar_choice_card.dart +++ b/lib/pangea/analytics_practice/choice_cards/grammar_choice_card.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/pangea/analytics_practice/choice_cards/game_choice_card.dart'; import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart'; import 'package:fluffychat/pangea/morphs/morph_features_enum.dart'; +import 'package:fluffychat/pangea/morphs/morph_icon.dart'; /// Choice card for meaning activity with emoji, and alt text on flip class GrammarChoiceCard extends StatelessWidget { @@ -29,6 +30,10 @@ class GrammarChoiceCard extends StatelessWidget { @override Widget build(BuildContext context) { + final baseTextSize = + (Theme.of(context).textTheme.titleMedium?.fontSize ?? 16) * + (height / 72.0).clamp(1.0, 1.4); + final emojiSize = baseTextSize * 1.2; final copy = getGrammarCopy( category: feature.name, lemma: tag, @@ -42,7 +47,33 @@ class GrammarChoiceCard extends StatelessWidget { onPressed: onPressed, isCorrect: isCorrect, height: height, - child: Text(copy), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: height * .7, + height: height, + child: Center( + child: MorphIcon( + morphFeature: feature, + morphTag: tag, + size: Size(emojiSize, emojiSize), + ), + ), + ), + Expanded( + child: Text( + copy, + overflow: TextOverflow.ellipsis, + maxLines: 2, + textAlign: TextAlign.left, + style: TextStyle( + fontSize: baseTextSize, + ), + ), + ), + ], + ), ); } }