chore: show correct answer hint button
and don't show answer description on selection of correct answer
This commit is contained in:
parent
aa597b8698
commit
0cb3d472c7
2 changed files with 166 additions and 50 deletions
|
|
@ -101,6 +101,8 @@ class AnalyticsPracticeState extends State<AnalyticsPractice>
|
|||
final ValueNotifier<SelectedMorphChoice?> selectedMorphChoice =
|
||||
ValueNotifier<SelectedMorphChoice?>(null);
|
||||
|
||||
final ValueNotifier<bool> hintPressedNotifier = ValueNotifier<bool>(false);
|
||||
|
||||
final Map<String, Map<String, String>> _choiceTexts = {};
|
||||
final Map<String, Map<String, String?>> _choiceEmojis = {};
|
||||
|
||||
|
|
@ -125,6 +127,7 @@ class AnalyticsPracticeState extends State<AnalyticsPractice>
|
|||
progressNotifier.dispose();
|
||||
enableChoicesNotifier.dispose();
|
||||
selectedMorphChoice.dispose();
|
||||
hintPressedNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -210,6 +213,7 @@ class AnalyticsPracticeState extends State<AnalyticsPractice>
|
|||
activityState.value = const AsyncState.loading();
|
||||
activityTarget.value = null;
|
||||
selectedMorphChoice.value = null;
|
||||
hintPressedNotifier.value = false;
|
||||
enableChoicesNotifier.value = true;
|
||||
progressNotifier.value = 0.0;
|
||||
_queue.clear();
|
||||
|
|
@ -282,6 +286,7 @@ class AnalyticsPracticeState extends State<AnalyticsPractice>
|
|||
try {
|
||||
activityState.value = const AsyncState.loading();
|
||||
selectedMorphChoice.value = null;
|
||||
hintPressedNotifier.value = false;
|
||||
|
||||
final req = activityTarget.value!;
|
||||
final res = await _fetchActivity(req);
|
||||
|
|
@ -324,6 +329,7 @@ class AnalyticsPracticeState extends State<AnalyticsPractice>
|
|||
while (_queue.isNotEmpty) {
|
||||
activityState.value = const AsyncState.loading();
|
||||
selectedMorphChoice.value = null;
|
||||
hintPressedNotifier.value = false;
|
||||
final nextActivityCompleter = _queue.removeFirst();
|
||||
|
||||
try {
|
||||
|
|
@ -477,6 +483,10 @@ class AnalyticsPracticeState extends State<AnalyticsPractice>
|
|||
await _analyticsService.updateService.addAnalytics(null, [use]);
|
||||
}
|
||||
|
||||
void onHintPressed() {
|
||||
hintPressedNotifier.value = !hintPressedNotifier.value;
|
||||
}
|
||||
|
||||
Future<void> onSelectChoice(
|
||||
String choiceContent,
|
||||
) async {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
|
|
@ -23,7 +25,6 @@ import 'package:fluffychat/pangea/practice_activities/practice_activity_model.da
|
|||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AnalyticsPracticeView extends StatelessWidget {
|
||||
final AnalyticsPracticeState controller;
|
||||
|
|
@ -162,28 +163,7 @@ class _AnalyticsActivityView extends StatelessWidget {
|
|||
const SizedBox(height: 16.0),
|
||||
_ActivityChoicesWidget(controller),
|
||||
const SizedBox(height: 16.0),
|
||||
ListenableBuilder(
|
||||
listenable: Listenable.merge([
|
||||
controller.activityState,
|
||||
controller.selectedMorphChoice,
|
||||
]),
|
||||
builder: (context, _) {
|
||||
final activityState = controller.activityState.value;
|
||||
final selectedChoice = controller.selectedMorphChoice.value;
|
||||
|
||||
if (activityState
|
||||
is! AsyncLoaded<MultipleChoicePracticeActivityModel> ||
|
||||
selectedChoice == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return MorphMeaningWidget(
|
||||
feature: selectedChoice.feature,
|
||||
tag: selectedChoice.tag,
|
||||
blankErrorFeedback: true,
|
||||
);
|
||||
},
|
||||
),
|
||||
_WrongAnswerFeedback(controller: controller),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
@ -228,6 +208,26 @@ class _AnalyticsPracticeCenterContent extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
ActivityTypeEnum.grammarCategory => Center(
|
||||
child: Column(
|
||||
children: [
|
||||
_CorrectAnswerHint(controller: controller),
|
||||
_ExampleMessageWidget(
|
||||
controller.getExampleMessage(target!.target),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: controller.hintPressedNotifier,
|
||||
builder: (context, hintPressed, __) {
|
||||
return HintButton(
|
||||
depressed: hintPressed,
|
||||
onPressed: controller.onHintPressed,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_ => SizedBox(
|
||||
height: 100.0,
|
||||
child: Center(
|
||||
|
|
@ -282,6 +282,96 @@ class _ExampleMessageWidget extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class _CorrectAnswerHint extends StatelessWidget {
|
||||
final AnalyticsPracticeState controller;
|
||||
|
||||
const _CorrectAnswerHint({
|
||||
required this.controller,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: controller.hintPressedNotifier,
|
||||
builder: (context, hintPressed, __) {
|
||||
if (!hintPressed) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: controller.activityState,
|
||||
builder: (context, state, __) {
|
||||
if (state is! AsyncLoaded<MultipleChoicePracticeActivityModel>) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final activity = state.value;
|
||||
if (activity is! MorphPracticeActivityModel) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final correctAnswerTag =
|
||||
activity.multipleChoiceContent.answers.first;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: MorphMeaningWidget(
|
||||
feature: activity.morphFeature,
|
||||
tag: correctAnswerTag,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _WrongAnswerFeedback extends StatelessWidget {
|
||||
final AnalyticsPracticeState controller;
|
||||
|
||||
const _WrongAnswerFeedback({
|
||||
required this.controller,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListenableBuilder(
|
||||
listenable: Listenable.merge([
|
||||
controller.activityState,
|
||||
controller.selectedMorphChoice,
|
||||
]),
|
||||
builder: (context, _) {
|
||||
final activityState = controller.activityState.value;
|
||||
final selectedChoice = controller.selectedMorphChoice.value;
|
||||
|
||||
if (activityState
|
||||
is! AsyncLoaded<MultipleChoicePracticeActivityModel> ||
|
||||
selectedChoice == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final activity = activityState.value;
|
||||
final isWrongAnswer =
|
||||
!activity.multipleChoiceContent.isCorrect(selectedChoice.tag);
|
||||
|
||||
if (!isWrongAnswer) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: MorphMeaningWidget(
|
||||
feature: selectedChoice.feature,
|
||||
tag: selectedChoice.tag,
|
||||
blankErrorFeedback: true,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ErrorBlankWidget extends StatefulWidget {
|
||||
final GrammarErrorPracticeActivityModel activity;
|
||||
|
||||
|
|
@ -413,38 +503,54 @@ class _ErrorBlankWidgetState extends State<_ErrorBlankWidget> {
|
|||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
PressableButton(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
depressed: _showTranslation,
|
||||
onPressed: _toggleTranslation,
|
||||
playSound: true,
|
||||
colorFactor: 0.3,
|
||||
builder: (context, depressed, shadowColor) => Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
height: 40.0,
|
||||
width: 40.0,
|
||||
decoration: BoxDecoration(
|
||||
color: depressed
|
||||
? shadowColor
|
||||
: Theme.of(context).colorScheme.primaryContainer,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.translate,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
HintButton(depressed: _showTranslation, onPressed: _toggleTranslation),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HintButton extends StatelessWidget {
|
||||
final VoidCallback onPressed;
|
||||
final bool depressed;
|
||||
|
||||
const HintButton({
|
||||
required this.onPressed,
|
||||
required this.depressed,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PressableButton(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
onPressed: onPressed,
|
||||
depressed: depressed,
|
||||
playSound: true,
|
||||
colorFactor: 0.3,
|
||||
builder: (context, depressed, shadowColor) => Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
height: 40.0,
|
||||
width: 40.0,
|
||||
decoration: BoxDecoration(
|
||||
color: depressed
|
||||
? shadowColor
|
||||
: Theme.of(context).colorScheme.primaryContainer,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.lightbulb_outline,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ActivityChoicesWidget extends StatelessWidget {
|
||||
final AnalyticsPracticeState controller;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue