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)
This commit is contained in:
parent
af26fd3bd9
commit
78ca8832cd
3 changed files with 98 additions and 23 deletions
|
|
@ -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<AnalyticsPractice>
|
|||
|
||||
final ValueNotifier<double> progressNotifier = ValueNotifier<double>(0.0);
|
||||
|
||||
final ValueNotifier<SelectedMorphChoice?> selectedMorphChoice =
|
||||
ValueNotifier<SelectedMorphChoice?>(null);
|
||||
|
||||
final Map<String, Map<String, String>> _choiceTexts = {};
|
||||
final Map<String, Map<String, String?>> _choiceEmojis = {};
|
||||
|
||||
|
|
@ -101,6 +115,7 @@ class AnalyticsPracticeState extends State<AnalyticsPractice>
|
|||
activityState.dispose();
|
||||
activityTarget.dispose();
|
||||
progressNotifier.dispose();
|
||||
selectedMorphChoice.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -185,6 +200,7 @@ class AnalyticsPracticeState extends State<AnalyticsPractice>
|
|||
void _resetActivityState() {
|
||||
activityState.value = const AsyncState.loading();
|
||||
activityTarget.value = null;
|
||||
selectedMorphChoice.value = null;
|
||||
}
|
||||
|
||||
void _resetSessionState() {
|
||||
|
|
@ -285,6 +301,7 @@ class AnalyticsPracticeState extends State<AnalyticsPractice>
|
|||
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<AnalyticsPractice>
|
|||
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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue