fix(learn): Fix user recorded voice playing issue
This commit is contained in:
parent
c2fc40fc3b
commit
539d8bf6c2
|
|
@ -506,10 +506,11 @@ class ApiService {
|
||||||
|
|
||||||
final Response response = await _service.dio
|
final Response response = await _service.dio
|
||||||
.get('$kBaseUrl/api/$kApiVersionUrl/$kModulesUrl/$id/$kPracticesUrl');
|
.get('$kBaseUrl/api/$kApiVersionUrl/$kModulesUrl/$id/$kPracticesUrl');
|
||||||
|
print('MODULE PRACTICES: ${response.data}');
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
var data = response.data;
|
var data = response.data;
|
||||||
var decodedData = data['data'] as List;
|
var decodedData = data['data']['practices'] as List;
|
||||||
practices = decodedData.map(
|
practices = decodedData.map(
|
||||||
(e) {
|
(e) {
|
||||||
return LearnPractice.fromJson(e);
|
return LearnPractice.fromJson(e);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:async/async.dart';
|
||||||
import 'package:audioplayers/audioplayers.dart';
|
import 'package:audioplayers/audioplayers.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
|
|
@ -9,13 +12,19 @@ class AudioPlayerService with ListenableServiceMixin {
|
||||||
|
|
||||||
AudioPlayer get player => _player;
|
AudioPlayer get player => _player;
|
||||||
|
|
||||||
|
final _durationController = StreamController<Duration>.broadcast();
|
||||||
|
|
||||||
AudioPlayerService() {
|
AudioPlayerService() {
|
||||||
_player.setReleaseMode(ReleaseMode.stop);
|
_player.setReleaseMode(ReleaseMode.stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Streams
|
// Streams
|
||||||
|
Stream<Duration> get durationStream => StreamGroup.merge([
|
||||||
|
_player.onDurationChanged,
|
||||||
|
_durationController.stream,
|
||||||
|
]);
|
||||||
|
|
||||||
Stream<Duration> get positionStream => _player.onPositionChanged;
|
Stream<Duration> get positionStream => _player.onPositionChanged;
|
||||||
Stream<Duration> get durationStream => _player.onDurationChanged;
|
|
||||||
|
|
||||||
// Optional: player state
|
// Optional: player state
|
||||||
Stream<PlayerState> get stateStream => _player.onPlayerStateChanged;
|
Stream<PlayerState> get stateStream => _player.onPlayerStateChanged;
|
||||||
|
|
@ -28,10 +37,16 @@ class AudioPlayerService with ListenableServiceMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
await _player.play(UrlSource(playableUrl));
|
await _player.play(UrlSource(playableUrl));
|
||||||
|
|
||||||
|
// 👇 Force duration fetch
|
||||||
|
final dur = await _player.getDuration();
|
||||||
|
if (dur != null) {
|
||||||
|
_durationController.add(dur);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> playLocal(String url) async {
|
Future<void> playLocal(String url) async {
|
||||||
await _player.play(UrlSource(url));
|
await _player.play(DeviceFileSource(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> pause() async => await _player.pause();
|
Future<void> pause() async => await _player.pause();
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,8 @@ class AuthenticationService with ListenableServiceMixin {
|
||||||
|
|
||||||
// Get user data
|
// Get user data
|
||||||
Future<User?> getUser() async {
|
Future<User?> getUser() async {
|
||||||
|
print('GENDER:');
|
||||||
|
print(await _secureService.getString('gender'));
|
||||||
_user = User(
|
_user = User(
|
||||||
userId: await _secureService.getInt('userId'),
|
userId: await _secureService.getInt('userId'),
|
||||||
email: await _secureService.getString('email'),
|
email: await _secureService.getString('email'),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
import 'package:image_picker/image_picker.dart';
|
|
||||||
import 'package:path/path.dart';
|
|
||||||
import 'package:path_provider/path_provider.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';
|
||||||
|
|
@ -39,25 +37,19 @@ class VoiceRecorderService with ListenableServiceMixin {
|
||||||
|
|
||||||
// Get recorded audio
|
// Get recorded audio
|
||||||
Future<String?> getRecordedAudio() async {
|
Future<String?> getRecordedAudio() async {
|
||||||
final file = _waveController.file;
|
final recorded = _waveController.file;
|
||||||
print('RECORDED $file');
|
if (recorded == null) return null;
|
||||||
if (file == null) return null;
|
|
||||||
|
|
||||||
await _saveRecordedAudio(file);
|
final generator = Random();
|
||||||
|
|
||||||
// return file.path;
|
int random = generator.nextInt(100);
|
||||||
String? voice = await _saveRecordedAudio(file);
|
|
||||||
return voice;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String?> _saveRecordedAudio(XFile? file) async {
|
final dir = await getTemporaryDirectory();
|
||||||
late File voice;
|
|
||||||
final voiceName = basename(file?.name ?? '');
|
|
||||||
final Directory appDir = await getApplicationDocumentsDirectory();
|
|
||||||
|
|
||||||
final localImagePath = join(appDir.path, voiceName);
|
final playable = File('${dir.path}/temp_audio_$random.aac');
|
||||||
voice = File(localImagePath);
|
|
||||||
//voice.writeAsBytes(await file?.);
|
await playable.writeAsBytes(await recorded.readAsBytes(), flush: true);
|
||||||
return voice.path;
|
|
||||||
|
return playable.path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,10 +59,12 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
||||||
Duration get duration => _duration;
|
Duration get duration => _duration;
|
||||||
|
|
||||||
double get progress {
|
double get progress {
|
||||||
|
print('DURATION: ${_duration.inMilliseconds}');
|
||||||
if (_duration.inMilliseconds == 0) return 0;
|
if (_duration.inMilliseconds == 0) return 0;
|
||||||
return _position.inMilliseconds / _duration.inMilliseconds;
|
return _position.inMilliseconds / _duration.inMilliseconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Voice recorder
|
// Voice recorder
|
||||||
WaveformRecorderController get _waveController =>
|
WaveformRecorderController get _waveController =>
|
||||||
_voiceRecorderService.waveController;
|
_voiceRecorderService.waveController;
|
||||||
|
|
@ -127,15 +129,15 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
||||||
|
|
||||||
// Play practice audio
|
// Play practice audio
|
||||||
void _listenToAudio() {
|
void _listenToAudio() {
|
||||||
_audioPlayerService.durationStream.listen((dur) {
|
_audioPlayerService.durationStream.listen((dur) {
|
||||||
|
if (dur.inMilliseconds > 0) {
|
||||||
_duration = dur;
|
_duration = dur;
|
||||||
print('DURATION: $_duration');
|
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
_audioPlayerService.positionStream.listen((pos) {
|
_audioPlayerService.positionStream.listen((pos) {
|
||||||
_position = pos;
|
_position = pos;
|
||||||
print('POSITION: $_position');
|
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -255,13 +257,14 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
||||||
if (await _statusChecker.checkConnection()) {
|
if (await _statusChecker.checkConnection()) {
|
||||||
if (practice == LearnPractices.course) {
|
if (practice == LearnPractices.course) {
|
||||||
_practices = await _apiService.getLearnCoursePractices(id);
|
_practices = await _apiService.getLearnCoursePractices(id);
|
||||||
|
|
||||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||||
} else if (practice == LearnPractices.module) {
|
} else if (practice == LearnPractices.module) {
|
||||||
_practices = await _apiService.getLearnModulePractices(id);
|
_practices = await _apiService.getLearnModulePractices(id);
|
||||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||||
} else {
|
} else {
|
||||||
_practices = await _apiService.getLearnLessonPractices(id);
|
_practices = await _apiService.getLearnLessonPractices(id);
|
||||||
print('PRACTICE LENGTH: ${_practices.length}');
|
|
||||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -370,7 +370,7 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
|
|
||||||
Widget _buildMaleRadioTile(ProfileDetailViewModel viewModel) =>
|
Widget _buildMaleRadioTile(ProfileDetailViewModel viewModel) =>
|
||||||
RadioListTile<String?>(
|
RadioListTile<String?>(
|
||||||
value: 'Male',
|
value: 'male',
|
||||||
title: _buildMaleTitle(),
|
title: _buildMaleTitle(),
|
||||||
activeColor: kcPrimaryColor,
|
activeColor: kcPrimaryColor,
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
|
|
@ -400,7 +400,7 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
|
|
||||||
Widget _buildFemaleRadioTile(ProfileDetailViewModel viewModel) =>
|
Widget _buildFemaleRadioTile(ProfileDetailViewModel viewModel) =>
|
||||||
RadioListTile<String?>(
|
RadioListTile<String?>(
|
||||||
value: 'Female',
|
value: 'female',
|
||||||
title: _buildFemaleTitle(),
|
title: _buildFemaleTitle(),
|
||||||
activeColor: kcPrimaryColor,
|
activeColor: kcPrimaryColor,
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:audioplayers/audioplayers.dart';
|
import 'package:audioplayers/audioplayers.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_circular_progress_indicator.dart';
|
||||||
|
|
||||||
import '../common/app_colors.dart';
|
import '../common/app_colors.dart';
|
||||||
import '../common/enmus.dart';
|
import '../common/enmus.dart';
|
||||||
|
|
@ -50,13 +51,22 @@ class LearnPracticeAnswerCard extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildButtonState(LearnPracticeViewModel viewModel) =>
|
Widget _buildButtonState(LearnPracticeViewModel viewModel) =>
|
||||||
(viewModel.busy(answer['busy_object']) && viewModel.playing == voice) ||
|
viewModel.busyObject == answer['busy_object'] &&
|
||||||
(viewModel.busyObject == answer['busy_object'] &&
|
viewModel.playing == voice
|
||||||
viewModel.playing == voice &&
|
? viewModel.busy(answer['busy_object'])
|
||||||
viewModel.player.state == PlayerState.playing)
|
? _buildProgressIndicatorWrapper()
|
||||||
? _buildPauseIcon()
|
: viewModel.player.state == PlayerState.playing
|
||||||
|
? _buildPauseIcon()
|
||||||
|
: _buildPlayIcon()
|
||||||
: _buildPlayIcon();
|
: _buildPlayIcon();
|
||||||
|
|
||||||
|
Widget _buildProgressIndicatorWrapper() => Center(
|
||||||
|
child: _buildProgressIndicator(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildProgressIndicator() =>
|
||||||
|
const CustomCircularProgressIndicator(color: kcWhite);
|
||||||
|
|
||||||
Widget _buildPlayIcon() => const Icon(
|
Widget _buildPlayIcon() => const Icon(
|
||||||
Icons.play_arrow_rounded,
|
Icons.play_arrow_rounded,
|
||||||
size: 25,
|
size: 25,
|
||||||
|
|
|
||||||
|
|
@ -50,13 +50,13 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.7.0"
|
version: "2.7.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.13.0"
|
version: "2.13.1"
|
||||||
audioplayers:
|
audioplayers:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ dependencies:
|
||||||
intl: any
|
intl: any
|
||||||
dio: ^5.9.0
|
dio: ^5.9.0
|
||||||
path: ^1.9.1
|
path: ^1.9.1
|
||||||
|
async: ^2.13.1
|
||||||
pinput: ^6.0.1
|
pinput: ^6.0.1
|
||||||
stacked: ^3.4.0
|
stacked: ^3.4.0
|
||||||
iconsax: ^0.0.8
|
iconsax: ^0.0.8
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user