fix(learn_practice): Adjust learn practice user flow
This commit is contained in:
parent
5cfe6897c6
commit
108643cdab
|
|
@ -1,3 +1,8 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:waveform_recorder/waveform_recorder.dart';
|
import 'package:waveform_recorder/waveform_recorder.dart';
|
||||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||||
|
|
@ -37,6 +42,22 @@ class VoiceRecorderService with ListenableServiceMixin {
|
||||||
final file = _waveController.file;
|
final file = _waveController.file;
|
||||||
print('RECORDED $file');
|
print('RECORDED $file');
|
||||||
if (file == null) return null;
|
if (file == null) return null;
|
||||||
return file.path;
|
|
||||||
|
await _saveRecordedAudio(file);
|
||||||
|
|
||||||
|
// return file.path;
|
||||||
|
String? voice = await _saveRecordedAudio(file);
|
||||||
|
return voice;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> _saveRecordedAudio(XFile? file) async {
|
||||||
|
late File voice;
|
||||||
|
final voiceName = basename(file?.name ?? '');
|
||||||
|
final Directory appDir = await getApplicationDocumentsDirectory();
|
||||||
|
|
||||||
|
final localImagePath = join(appDir.path, voiceName);
|
||||||
|
voice = File(localImagePath);
|
||||||
|
//voice.writeAsBytes(await file?.);
|
||||||
|
return voice.path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,4 +62,5 @@ enum StateObjects {
|
||||||
coursePracticeQuestion,
|
coursePracticeQuestion,
|
||||||
coursePracticeQuestions,
|
coursePracticeQuestions,
|
||||||
recordLearnPracticeAnswer,
|
recordLearnPracticeAnswer,
|
||||||
|
finishLearnPracticeQuestion
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,7 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
||||||
Future<void> _playAudio(
|
Future<void> _playAudio(
|
||||||
{required Map<String, dynamic> answer, required Voice voice}) async {
|
{required Map<String, dynamic> answer, required Voice voice}) async {
|
||||||
if (voice == Voice.recorded) {
|
if (voice == Voice.recorded) {
|
||||||
|
print(answer['recorded_voice_answer']);
|
||||||
await _audioPlayerService.playLocal(answer['recorded_voice_answer']);
|
await _audioPlayerService.playLocal(answer['recorded_voice_answer']);
|
||||||
} else {
|
} else {
|
||||||
await _audioPlayerService.playUrl(answer['sample_voice_answer']);
|
await _audioPlayerService.playUrl(answer['sample_voice_answer']);
|
||||||
|
|
@ -214,18 +215,24 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> nextQuestion(
|
Future<void> nextQuestion(
|
||||||
|
{required int index, required LearnQuestion question}) async =>
|
||||||
|
await runBusyFuture(_nextQuestion(index: index, question: question),
|
||||||
|
busyObject: StateObjects.finishLearnPracticeQuestion);
|
||||||
|
|
||||||
|
Future<void> _nextQuestion(
|
||||||
{required int index, required LearnQuestion question}) async {
|
{required int index, required LearnQuestion question}) async {
|
||||||
await stopRecording();
|
await stopRecording();
|
||||||
_answers.add({
|
_answers.add({
|
||||||
'busy_object': question.id.toString(),
|
'busy_object': question.id.toString(),
|
||||||
'sample_text_answer': question.audioCorrectAnswerText,
|
'sample_text_answer': question.audioCorrectAnswerText,
|
||||||
'sample_voice_answer': question.sampleAnswerVoicePrompt,
|
'sample_voice_answer': question.sampleAnswerVoicePrompt,
|
||||||
'recorded_voice_answer': _voiceRecorderService.getRecordedAudio(),
|
'recorded_voice_answer': await _voiceRecorderService.getRecordedAudio(),
|
||||||
});
|
});
|
||||||
if (index != _questions.length) {
|
if (index != _questions.length) {
|
||||||
_questionSetController.nextPage(
|
_questionSetController.nextPage(
|
||||||
duration: const Duration(milliseconds: 350),
|
curve: Curves.easeInOutCubic,
|
||||||
curve: Curves.easeInOutCubic);
|
duration: const Duration(milliseconds: 350),
|
||||||
|
);
|
||||||
await playVoicePrompt(_questions[index]);
|
await playVoicePrompt(_questions[index]);
|
||||||
} else {
|
} else {
|
||||||
goTo(3);
|
goTo(3);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import 'package:stacked/stacked.dart';
|
||||||
import 'package:waveform_recorder/waveform_recorder.dart';
|
import 'package:waveform_recorder/waveform_recorder.dart';
|
||||||
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/cancel_learn_practice_sheet.dart';
|
import 'package:yimaru_app/ui/widgets/cancel_learn_practice_sheet.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_circular_progress_indicator.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart';
|
import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
|
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/wave_wrapper.dart';
|
import 'package:yimaru_app/ui/widgets/wave_wrapper.dart';
|
||||||
|
|
@ -70,7 +71,6 @@ class InteractLearnPracticeScreen
|
||||||
children: [
|
children: [
|
||||||
_buildBodyColumnWrapper(context: context, viewModel: viewModel),
|
_buildBodyColumnWrapper(context: context, viewModel: viewModel),
|
||||||
_buildProgressIndicatorState(viewModel),
|
_buildProgressIndicatorState(viewModel),
|
||||||
_buildPageLoadingIndicatorState(viewModel)
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -139,7 +139,9 @@ class InteractLearnPracticeScreen
|
||||||
viewModel.player.state == PlayerState.playing
|
viewModel.player.state == PlayerState.playing
|
||||||
? _buildListeningLabel()
|
? _buildListeningLabel()
|
||||||
: VoiceRecordingState.recording == viewModel.recordingState
|
: VoiceRecordingState.recording == viewModel.recordingState
|
||||||
? _buildSpeakingLabel()
|
? viewModel.busy(StateObjects.finishLearnPracticeQuestion)
|
||||||
|
? const SizedBox(height: 20)
|
||||||
|
: _buildSpeakingLabel()
|
||||||
: const SizedBox(height: 20);
|
: const SizedBox(height: 20);
|
||||||
|
|
||||||
Widget _buildListeningLabel() => Text(
|
Widget _buildListeningLabel() => Text(
|
||||||
|
|
@ -149,7 +151,7 @@ class InteractLearnPracticeScreen
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSpeakingLabel() => Text(
|
Widget _buildSpeakingLabel() => Text(
|
||||||
'You\'re is speaking...',
|
'You\'re speaking...',
|
||||||
style: style14P400,
|
style: style14P400,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
@ -158,8 +160,11 @@ class InteractLearnPracticeScreen
|
||||||
WaveWrapper(height: 200, child: _buildSpinnerState(viewModel));
|
WaveWrapper(height: 200, child: _buildSpinnerState(viewModel));
|
||||||
|
|
||||||
Widget _buildSpinnerState(LearnPracticeViewModel viewModel) =>
|
Widget _buildSpinnerState(LearnPracticeViewModel viewModel) =>
|
||||||
viewModel.player.state == PlayerState.playing
|
viewModel.busy(StateObjects.recordLearnPracticeAnswer) ||
|
||||||
? _buildSpinner()
|
viewModel.busy(StateObjects.finishLearnPracticeQuestion)
|
||||||
|
? Container()
|
||||||
|
: viewModel.player.state == PlayerState.playing
|
||||||
|
? _buildSpinner()
|
||||||
: VoiceRecordingState.recording == viewModel.recordingState &&
|
: VoiceRecordingState.recording == viewModel.recordingState &&
|
||||||
viewModel.waveController.isRecording
|
viewModel.waveController.isRecording
|
||||||
? _buildSpeakingSpinnerColumn(viewModel)
|
? _buildSpeakingSpinnerColumn(viewModel)
|
||||||
|
|
@ -184,7 +189,6 @@ class InteractLearnPracticeScreen
|
||||||
Widget _buildSpeakingSpinner(LearnPracticeViewModel viewModel) =>
|
Widget _buildSpeakingSpinner(LearnPracticeViewModel viewModel) =>
|
||||||
WaveformRecorder(
|
WaveformRecorder(
|
||||||
height: 35,
|
height: 35,
|
||||||
onRecordingStopped: () {},
|
|
||||||
waveColor: kcPrimaryColor,
|
waveColor: kcPrimaryColor,
|
||||||
durationTextStyle: style14P600,
|
durationTextStyle: style14P600,
|
||||||
controller: viewModel.waveController,
|
controller: viewModel.waveController,
|
||||||
|
|
@ -221,7 +225,7 @@ class InteractLearnPracticeScreen
|
||||||
[
|
[
|
||||||
_buildActionLabel(),
|
_buildActionLabel(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildButtonsRowWrapper(context: context, viewModel: viewModel),
|
_buildButtonRowContainer(context: context, viewModel: viewModel),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -231,6 +235,27 @@ class InteractLearnPracticeScreen
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Widget _buildButtonRowContainer(
|
||||||
|
{required BuildContext context,
|
||||||
|
required LearnPracticeViewModel viewModel}) =>
|
||||||
|
SizedBox(
|
||||||
|
height: 75,
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: _buildButtonRowState(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildButtonRowState(
|
||||||
|
{required BuildContext context,
|
||||||
|
required LearnPracticeViewModel viewModel}) =>
|
||||||
|
viewModel.busy(StateObjects.learnPracticeQuestion) ||
|
||||||
|
viewModel.busy(StateObjects.finishLearnPracticeQuestion)
|
||||||
|
? _buildProgressIndicator(kcPrimaryColor)
|
||||||
|
: _buildButtonsRowWrapper(context: context, viewModel: viewModel);
|
||||||
|
|
||||||
|
Widget _buildProgressIndicator(Color color) => Center(
|
||||||
|
child: CustomCircularProgressIndicator(color: color),
|
||||||
|
);
|
||||||
|
|
||||||
Widget _buildButtonsRowWrapper(
|
Widget _buildButtonsRowWrapper(
|
||||||
{required BuildContext context,
|
{required BuildContext context,
|
||||||
required LearnPracticeViewModel viewModel}) =>
|
required LearnPracticeViewModel viewModel}) =>
|
||||||
|
|
@ -268,7 +293,12 @@ class InteractLearnPracticeScreen
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildMicButtonWrapper(LearnPracticeViewModel viewModel) =>
|
Widget _buildMicButtonWrapper(LearnPracticeViewModel viewModel) =>
|
||||||
Expanded(child: _buildMicButton(viewModel));
|
Expanded(child: _buildMicButtonState(viewModel));
|
||||||
|
|
||||||
|
Widget _buildMicButtonState(LearnPracticeViewModel viewModel) =>
|
||||||
|
viewModel.busy(StateObjects.recordLearnPracticeAnswer)
|
||||||
|
? _buildProgressIndicator(kcPrimaryColor)
|
||||||
|
: _buildMicButton(viewModel);
|
||||||
|
|
||||||
Widget _buildMicButton(LearnPracticeViewModel viewModel) => ElevatedButton(
|
Widget _buildMicButton(LearnPracticeViewModel viewModel) => ElevatedButton(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
|
|
@ -335,7 +365,11 @@ class InteractLearnPracticeScreen
|
||||||
|
|
||||||
Widget _buildProgressIndicatorState(LearnPracticeViewModel viewModel) =>
|
Widget _buildProgressIndicatorState(LearnPracticeViewModel viewModel) =>
|
||||||
viewModel.recordingState == VoiceRecordingState.pending
|
viewModel.recordingState == VoiceRecordingState.pending
|
||||||
? _buildProgressIndicatorWrapper(viewModel)
|
? viewModel.busy(StateObjects.finishLearnPracticeQuestion) ||
|
||||||
|
viewModel.busy(StateObjects.learnPracticeQuestion) ||
|
||||||
|
viewModel.busy(StateObjects.recordLearnPracticeAnswer)
|
||||||
|
? Container()
|
||||||
|
: _buildProgressIndicatorWrapper(viewModel)
|
||||||
: Container();
|
: Container();
|
||||||
|
|
||||||
Widget _buildProgressIndicatorWrapper(LearnPracticeViewModel viewModel) =>
|
Widget _buildProgressIndicatorWrapper(LearnPracticeViewModel viewModel) =>
|
||||||
|
|
@ -349,17 +383,12 @@ class InteractLearnPracticeScreen
|
||||||
Widget _buildProgressIndicatorSpacer(LearnPracticeViewModel viewModel) =>
|
Widget _buildProgressIndicatorSpacer(LearnPracticeViewModel viewModel) =>
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildProgressIndicator(viewModel),
|
child: _buildLinearProgressIndicator(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildProgressIndicator(LearnPracticeViewModel viewModel) =>
|
Widget _buildLinearProgressIndicator(LearnPracticeViewModel viewModel) =>
|
||||||
CustomLinearProgressIndicator(
|
CustomLinearProgressIndicator(
|
||||||
activeColor: kcPrimaryColor,
|
activeColor: kcPrimaryColor,
|
||||||
progress: viewModel.progress,
|
progress: viewModel.progress,
|
||||||
backgroundColor: kcVeryLightGrey);
|
backgroundColor: kcVeryLightGrey);
|
||||||
|
|
||||||
Widget _buildPageLoadingIndicatorState(LearnPracticeViewModel viewModel) =>
|
|
||||||
viewModel.busy(StateObjects.learnPracticeQuestion)
|
|
||||||
? const PageLoadingIndicator()
|
|
||||||
: Container();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,5 @@ class LearnPracticeResultsWrapper
|
||||||
itemBuilder: (context, index) => _buildResult(viewModel.answers[index]),
|
itemBuilder: (context, index) => _buildResult(viewModel.answers[index]),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildResult(Map<String, dynamic> answer) => LearnPracticeResultCard(
|
Widget _buildResult(Map<String, dynamic> answer) => LearnPracticeResultCard(answer: answer);
|
||||||
answer: answer,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
name: yimaru_app
|
name: yimaru_app
|
||||||
version: 0.1.9+11
|
version: 0.1.10+12
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
description: A new Flutter project.
|
description: A new Flutter project.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user