Merge branch 'release/0.1.11'
- fix(player): Change the video player into chewie for better performance
This commit is contained in:
commit
b943666bb4
|
|
@ -52,6 +52,7 @@ import 'package:yimaru_app/services/in_app_update_service.dart';
|
||||||
import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart';
|
import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart';
|
||||||
import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart';
|
import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart';
|
||||||
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart';
|
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart';
|
||||||
|
import 'package:yimaru_app/services/vimeo_service.dart';
|
||||||
// @stacked-import
|
// @stacked-import
|
||||||
|
|
||||||
@StackedApp(
|
@StackedApp(
|
||||||
|
|
@ -112,6 +113,7 @@ import 'package:yimaru_app/ui/views/assessment/assessment_view.dart';
|
||||||
LazySingleton(classType: AudioPlayerService),
|
LazySingleton(classType: AudioPlayerService),
|
||||||
LazySingleton(classType: VoiceRecorderService),
|
LazySingleton(classType: VoiceRecorderService),
|
||||||
LazySingleton(classType: InAppUpdateService),
|
LazySingleton(classType: InAppUpdateService),
|
||||||
|
LazySingleton(classType: VimeoService),
|
||||||
// @stacked-service
|
// @stacked-service
|
||||||
],
|
],
|
||||||
bottomsheets: [
|
bottomsheets: [
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import '../services/permission_handler_service.dart';
|
||||||
import '../services/secure_storage_service.dart';
|
import '../services/secure_storage_service.dart';
|
||||||
import '../services/smart_auth_service.dart';
|
import '../services/smart_auth_service.dart';
|
||||||
import '../services/status_checker_service.dart';
|
import '../services/status_checker_service.dart';
|
||||||
|
import '../services/vimeo_service.dart';
|
||||||
import '../services/voice_recorder_service.dart';
|
import '../services/voice_recorder_service.dart';
|
||||||
|
|
||||||
final locator = StackedLocator.instance;
|
final locator = StackedLocator.instance;
|
||||||
|
|
@ -55,4 +56,5 @@ Future<void> setupLocator(
|
||||||
locator.registerLazySingleton(() => AudioPlayerService());
|
locator.registerLazySingleton(() => AudioPlayerService());
|
||||||
locator.registerLazySingleton(() => VoiceRecorderService());
|
locator.registerLazySingleton(() => VoiceRecorderService());
|
||||||
locator.registerLazySingleton(() => InAppUpdateService());
|
locator.registerLazySingleton(() => InAppUpdateService());
|
||||||
|
locator.registerLazySingleton(() => VimeoService());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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'),
|
||||||
|
|
|
||||||
85
lib/services/vimeo_service.dart
Normal file
85
lib/services/vimeo_service.dart
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
class VimeoService {
|
||||||
|
Future<String?> getVideoUrl(String vimeoUrl) async {
|
||||||
|
final videoId = _extractVideoId(vimeoUrl);
|
||||||
|
|
||||||
|
if (videoId == null) {
|
||||||
|
throw Exception('Invalid Vimeo URL');
|
||||||
|
}
|
||||||
|
|
||||||
|
final html = await _fetchHtml(videoId);
|
||||||
|
|
||||||
|
final config = _extractConfig(html);
|
||||||
|
|
||||||
|
if (config == null) {
|
||||||
|
throw Exception('Failed to extract Vimeo config');
|
||||||
|
}
|
||||||
|
|
||||||
|
return _extractHlsUrl(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> _fetchHtml(String videoId) async {
|
||||||
|
final response = await http.get(
|
||||||
|
Uri.parse('https://player.vimeo.com/video/$videoId'),
|
||||||
|
headers: {
|
||||||
|
'Origin': 'https://vimeo.com',
|
||||||
|
'Referer': 'https://vimeo.com/',
|
||||||
|
'Accept': 'text/html,application/xhtml+xml',
|
||||||
|
'User-Agent':
|
||||||
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return response.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, dynamic>? _extractConfig(String html) {
|
||||||
|
final regex = RegExp(
|
||||||
|
r'window\.playerConfig\s*=\s*({.*?})\s*</script>',
|
||||||
|
dotAll: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
final match = regex.firstMatch(html);
|
||||||
|
if (match == null) return null;
|
||||||
|
|
||||||
|
final jsonString = match.group(1);
|
||||||
|
if (jsonString == null) return null;
|
||||||
|
|
||||||
|
return jsonDecode(jsonString);
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _extractHlsUrl(Map<String, dynamic> config) {
|
||||||
|
try {
|
||||||
|
final files = config['request']?['files'];
|
||||||
|
final hls = files?['hls'];
|
||||||
|
|
||||||
|
if (hls == null) return null;
|
||||||
|
|
||||||
|
final cdns = hls['cdns'] as Map<String, dynamic>;
|
||||||
|
|
||||||
|
// Prefer fastly_skyfire or fallback to first CDN
|
||||||
|
final cdn = cdns['fastly_skyfire'] ?? cdns.values.first;
|
||||||
|
|
||||||
|
return cdn['url'];
|
||||||
|
} catch (_) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _extractVideoId(String url) {
|
||||||
|
try {
|
||||||
|
final uri = Uri.parse(url);
|
||||||
|
|
||||||
|
if (uri.pathSegments.isNotEmpty) {
|
||||||
|
return uri.pathSegments.last;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (_) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -312,9 +312,8 @@ TextStyle style14MG400 = const TextStyle(
|
||||||
TextStyle style14DG500 =
|
TextStyle style14DG500 =
|
||||||
const TextStyle(color: kcDarkGrey, fontWeight: FontWeight.w500);
|
const TextStyle(color: kcDarkGrey, fontWeight: FontWeight.w500);
|
||||||
|
|
||||||
TextStyle style18MG500 =
|
TextStyle style18MG500 = const TextStyle(
|
||||||
const TextStyle(fontSize: 18,color: kcMediumGrey, fontWeight: FontWeight.w500);
|
fontSize: 18, color: kcMediumGrey, fontWeight: FontWeight.w500);
|
||||||
|
|
||||||
|
|
||||||
TextStyle style14DG400 = const TextStyle(
|
TextStyle style14DG400 = const TextStyle(
|
||||||
color: kcDarkGrey,
|
color: kcDarkGrey,
|
||||||
|
|
|
||||||
|
|
@ -75,11 +75,11 @@ class AssessmentViewModel extends BaseViewModel {
|
||||||
_currentQuestionIndex = 0;
|
_currentQuestionIndex = 0;
|
||||||
_pageController.jumpToPage(_currentQuestionIndex);
|
_pageController.jumpToPage(_currentQuestionIndex);
|
||||||
_currentAssessment = assessments[currentAssessmentIndex];
|
_currentAssessment = assessments[currentAssessmentIndex];
|
||||||
await getAssessmentQuestions(
|
await getAssessmentQuestions(_currentAssessment?.id ?? 0);
|
||||||
_currentAssessment?.id ?? 0);
|
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> evaluateAssessment() {
|
Map<String, dynamic> evaluateAssessment() {
|
||||||
bool levelPassed = canPassLevel();
|
bool levelPassed = canPassLevel();
|
||||||
if (levelPassed) {
|
if (levelPassed) {
|
||||||
|
|
@ -166,8 +166,7 @@ class AssessmentViewModel extends BaseViewModel {
|
||||||
_currentQuestionIndex = 0;
|
_currentQuestionIndex = 0;
|
||||||
_proficiencyLevel = response['level'];
|
_proficiencyLevel = response['level'];
|
||||||
_currentAssessment = assessments[currentAssessmentIndex];
|
_currentAssessment = assessments[currentAssessmentIndex];
|
||||||
await getAssessmentQuestions(
|
await getAssessmentQuestions(_currentAssessment?.id ?? 0);
|
||||||
_currentAssessment?.id ?? 0);
|
|
||||||
_pageController.jumpToPage(_currentQuestionIndex);
|
_pageController.jumpToPage(_currentQuestionIndex);
|
||||||
} else {
|
} else {
|
||||||
_proficiencyLevel = response['level'];
|
_proficiencyLevel = response['level'];
|
||||||
|
|
@ -274,8 +273,7 @@ class AssessmentViewModel extends BaseViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assessments
|
// Assessments
|
||||||
Future<void> getAssessments() async =>
|
Future<void> getAssessments() async => await runBusyFuture(_getAssessments(),
|
||||||
await runBusyFuture(_getAssessments(),
|
|
||||||
busyObject: StateObjects.assessments);
|
busyObject: StateObjects.assessments);
|
||||||
|
|
||||||
Future<void> _getAssessments() async {
|
Future<void> _getAssessments() async {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
||||||
|
|
||||||
import '../../../widgets/assessment_loading_screen.dart';
|
|
||||||
import '../assessment_viewmodel.dart';
|
import '../assessment_viewmodel.dart';
|
||||||
|
|
||||||
class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
|
class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
|
||||||
|
|
@ -140,7 +139,8 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
|
||||||
onTap: viewModel.selectedAnswers.containsKey(question.toString())
|
onTap: viewModel.selectedAnswers.containsKey(question.toString())
|
||||||
? () => viewModel.nextQuestion()
|
? () => viewModel.nextQuestion()
|
||||||
: null,
|
: null,
|
||||||
text: viewModel.currentQuestionIndex == viewModel.assessmentQuestions.length - 1
|
text: viewModel.currentQuestionIndex ==
|
||||||
|
viewModel.assessmentQuestions.length - 1
|
||||||
? 'Finish Level'
|
? 'Finish Level'
|
||||||
: 'Continue',
|
: 'Continue',
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
||||||
|
|
@ -87,9 +86,7 @@ class AssessmentResultScreen extends ViewModelWidget<AssessmentViewModel> {
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildIconWrapper(AssessmentViewModel viewModel) =>
|
Widget _buildIconWrapper(AssessmentViewModel viewModel) =>
|
||||||
viewModel.proficiencyLevel != null
|
viewModel.proficiencyLevel != null ? _buildIcon(viewModel) : Container();
|
||||||
? _buildIcon(viewModel)
|
|
||||||
: Container();
|
|
||||||
|
|
||||||
Widget _buildIcon(AssessmentViewModel viewModel) => SvgPicture.asset(
|
Widget _buildIcon(AssessmentViewModel viewModel) => SvgPicture.asset(
|
||||||
'assets/icons/${viewModel.proficiencyLevel?.substring(0, 1).toLowerCase()}_${viewModel.proficiencyLevel?.substring(1).toLowerCase()}.svg');
|
'assets/icons/${viewModel.proficiencyLevel?.substring(0, 1).toLowerCase()}_${viewModel.proficiencyLevel?.substring(1).toLowerCase()}.svg');
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart';
|
||||||
import 'package:yimaru_app/ui/views/profile/profile_view.dart';
|
import 'package:yimaru_app/ui/views/profile/profile_view.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/coming_soon.dart';
|
import 'package:yimaru_app/ui/widgets/coming_soon.dart';
|
||||||
|
|
||||||
import '../../common/enmus.dart';
|
|
||||||
import '../../widgets/page_loading_indicator.dart';
|
|
||||||
import 'home_viewmodel.dart';
|
import 'home_viewmodel.dart';
|
||||||
|
|
||||||
class HomeView extends StackedView<HomeViewModel> {
|
class HomeView extends StackedView<HomeViewModel> {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
|
import 'package:chewie/chewie.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:vimeo_video_player/vimeo_video_player.dart';
|
import 'package:vimeo_video_player/vimeo_video_player.dart';
|
||||||
import 'package:yimaru_app/models/learn_lesson.dart';
|
import 'package:yimaru_app/models/learn_lesson.dart';
|
||||||
|
|
||||||
import '../../common/app_colors.dart';
|
import '../../common/app_colors.dart';
|
||||||
|
import '../../common/enmus.dart';
|
||||||
import '../../common/ui_helpers.dart';
|
import '../../common/ui_helpers.dart';
|
||||||
import '../../widgets/custom_elevated_button.dart';
|
import '../../widgets/custom_elevated_button.dart';
|
||||||
|
import '../../widgets/empty_video_player.dart';
|
||||||
import '../../widgets/small_app_bar.dart';
|
import '../../widgets/small_app_bar.dart';
|
||||||
import 'learn_lesson_detail_viewmodel.dart';
|
import 'learn_lesson_detail_viewmodel.dart';
|
||||||
|
|
||||||
|
|
@ -20,6 +23,12 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
||||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onViewModelReady(LearnLessonDetailViewModel viewModel) async {
|
||||||
|
await viewModel.initializePlayer(lesson.videoUrl ?? '');
|
||||||
|
super.onViewModelReady(viewModel);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onDispose(LearnLessonDetailViewModel viewModel) {
|
void onDispose(LearnLessonDetailViewModel viewModel) {
|
||||||
viewModel.close();
|
viewModel.close();
|
||||||
|
|
@ -120,19 +129,23 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
||||||
height: 200,
|
height: 200,
|
||||||
color: kcBlack,
|
color: kcBlack,
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
child: _buildVideoPlayer(viewModel),
|
child: _buildVideoPlayerState(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Widget _buildVideoPlayerState(LearnLessonDetailViewModel viewModel) =>
|
||||||
|
viewModel.chewieController != null &&
|
||||||
|
viewModel.videoPlayerController != null &&
|
||||||
|
!viewModel.busy(StateObjects.loadLessonVideo)
|
||||||
|
? _buildVideoPlayer(viewModel)
|
||||||
|
: _buildEmptyVideoPlayer();
|
||||||
|
|
||||||
Widget _buildVideoPlayer(LearnLessonDetailViewModel viewModel) =>
|
Widget _buildVideoPlayer(LearnLessonDetailViewModel viewModel) =>
|
||||||
_buildVimeoPlayer(viewModel);
|
_buildChewiePlayer(viewModel);
|
||||||
|
|
||||||
Widget _buildVimeoPlayer(LearnLessonDetailViewModel viewModel) =>
|
Widget _buildChewiePlayer(LearnLessonDetailViewModel viewModel) =>
|
||||||
VimeoVideoPlayer(
|
Chewie(controller: viewModel.chewieController!);
|
||||||
isAutoPlay: true,
|
|
||||||
onInAppWebViewCreated: (controller) =>
|
Widget _buildEmptyVideoPlayer() => const EmptyVideoPlayer();
|
||||||
viewModel.initializePlayer(controller),
|
|
||||||
videoId: lesson.videoUrl?.split('/').last ?? '',
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildDescriptionWrapper() => Padding(
|
Widget _buildDescriptionWrapper() => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,67 @@
|
||||||
|
import 'package:chewie/chewie.dart';
|
||||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:stacked_services/stacked_services.dart';
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
|
import 'package:video_player/video_player.dart';
|
||||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||||
|
|
||||||
import '../../../app/app.locator.dart';
|
import '../../../app/app.locator.dart';
|
||||||
import '../../../app/app.router.dart';
|
import '../../../app/app.router.dart';
|
||||||
import '../../../services/status_checker_service.dart';
|
import '../../../services/status_checker_service.dart';
|
||||||
|
import '../../../services/vimeo_service.dart';
|
||||||
|
import '../../common/app_constants.dart';
|
||||||
|
import '../../common/helper_functions.dart';
|
||||||
|
import '../../common/ui_helpers.dart';
|
||||||
|
|
||||||
class LearnLessonDetailViewModel extends BaseViewModel {
|
class LearnLessonDetailViewModel extends BaseViewModel {
|
||||||
// Dependency injection
|
// Dependency injection
|
||||||
final _statusChecker = locator<StatusCheckerService>();
|
final _vimeoService = locator<VimeoService>();
|
||||||
|
|
||||||
final _navigationService = locator<NavigationService>();
|
final _navigationService = locator<NavigationService>();
|
||||||
|
|
||||||
// Video player config
|
// Video player config
|
||||||
InAppWebViewController? _webViewController;
|
ChewieController? _chewieController;
|
||||||
|
|
||||||
InAppWebViewController? get webViewController => _webViewController;
|
ChewieController? get chewieController => _chewieController;
|
||||||
|
|
||||||
|
VideoPlayerController? _videoPlayerController;
|
||||||
|
|
||||||
|
VideoPlayerController? get videoPlayerController => _videoPlayerController;
|
||||||
|
|
||||||
// Video player
|
// Video player
|
||||||
void close() {
|
void close() {
|
||||||
webViewController?.dispose();
|
_videoPlayerController?.dispose();
|
||||||
|
_chewieController?.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> pause() async {
|
Future<void> pause() async {
|
||||||
await webViewController?.pause();
|
await _chewieController?.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
void initializePlayer(InAppWebViewController controller) {
|
Future<void> initializePlayer(String url) async =>
|
||||||
_webViewController = controller;
|
await runBusyFuture(_initializePlayer(url),
|
||||||
rebuildUi();
|
busyObject: StateObjects.loadLessonVideo);
|
||||||
|
|
||||||
|
Future<void> _initializePlayer(String url) async {
|
||||||
|
final playableUrl = await _vimeoService.getVideoUrl(url);
|
||||||
|
|
||||||
|
if (playableUrl == null) {
|
||||||
|
throw Exception("Unable to load video");
|
||||||
}
|
}
|
||||||
|
|
||||||
void onLoadVideoStart() {
|
_videoPlayerController =
|
||||||
setBusyForObject(StateObjects.loadLessonVideo, true);
|
VideoPlayerController.networkUrl(Uri.parse(playableUrl));
|
||||||
rebuildUi();
|
|
||||||
}
|
|
||||||
|
|
||||||
void onLoadVideoComplete() {
|
await _videoPlayerController?.initialize();
|
||||||
setBusyForObject(StateObjects.loadLessonVideo, false);
|
|
||||||
rebuildUi();
|
_chewieController = ChewieController(
|
||||||
|
videoPlayerController: _videoPlayerController!,
|
||||||
|
autoPlay: true,
|
||||||
|
looping: true,
|
||||||
|
aspectRatio: 16 / 9,
|
||||||
|
);
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||||
|
await viewModel.stopRecording();
|
||||||
viewModel.pop();
|
viewModel.pop();
|
||||||
viewModel.stopRecording();
|
|
||||||
viewModel.pop();
|
viewModel.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,8 +59,12 @@ class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
||||||
required LearnPracticeViewModel viewModel}) =>
|
required LearnPracticeViewModel viewModel}) =>
|
||||||
PopScope(
|
PopScope(
|
||||||
canPop: false,
|
canPop: false,
|
||||||
onPopInvokedWithResult: (value, data) async =>
|
onPopInvokedWithResult: (didPop, data) {
|
||||||
await _showSheet(context: context, viewModel: viewModel),
|
if (!didPop) {
|
||||||
|
Future.microtask(() async =>
|
||||||
|
await _showSheet(context: context, viewModel: viewModel));
|
||||||
|
}
|
||||||
|
},
|
||||||
child: _buildScaffoldWrapper(viewModel));
|
child: _buildScaffoldWrapper(viewModel));
|
||||||
|
|
||||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
@ -128,14 +129,14 @@ 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 +256,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ 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_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/wave_wrapper.dart';
|
import 'package:yimaru_app/ui/widgets/wave_wrapper.dart';
|
||||||
|
|
||||||
import '../../../../models/learn_question.dart';
|
import '../../../../models/learn_question.dart';
|
||||||
|
|
@ -26,9 +25,9 @@ class InteractLearnPracticeScreen
|
||||||
{super.key, required this.index, required this.question});
|
{super.key, required this.index, required this.question});
|
||||||
|
|
||||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||||
|
await viewModel.stopRecording();
|
||||||
viewModel.pop();
|
viewModel.pop();
|
||||||
viewModel.pop();
|
viewModel.pop();
|
||||||
viewModel.stopRecording();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _reply(LearnPracticeViewModel viewModel) =>
|
void _reply(LearnPracticeViewModel viewModel) =>
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ import 'package:yimaru_app/ui/widgets/small_app_bar.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/large_app_bar.dart';
|
|
||||||
import '../../../widgets/refresh_button.dart';
|
|
||||||
|
|
||||||
class LearnLoadingScreen extends StatelessWidget {
|
class LearnLoadingScreen extends StatelessWidget {
|
||||||
final bool isEmpty;
|
final bool isEmpty;
|
||||||
|
|
@ -58,9 +56,11 @@ class LearnLoadingScreen extends StatelessWidget {
|
||||||
|
|
||||||
Widget _buildPageIndicator() => const PageLoadingIndicator();
|
Widget _buildPageIndicator() => const PageLoadingIndicator();
|
||||||
|
|
||||||
Widget _buildRefreshButtonWrapper() => Align(
|
Widget _buildRefreshButtonWrapper() =>
|
||||||
alignment: Alignment.center,
|
Align(alignment: Alignment.center, child: _buildRefreshButton());
|
||||||
child:_buildRefreshButton());
|
|
||||||
|
|
||||||
Widget _buildRefreshButton()=> NoDataIndicator(title:'No practice available!' ,onTap: onTap,);
|
Widget _buildRefreshButton() => NoDataIndicator(
|
||||||
|
title: 'No practice available!',
|
||||||
|
onTap: onTap,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,9 @@ class LearnPracticeIntroScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
const LearnPracticeIntroScreen({super.key});
|
const LearnPracticeIntroScreen({super.key});
|
||||||
|
|
||||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||||
|
await viewModel.stopRecording();
|
||||||
viewModel.pop();
|
viewModel.pop();
|
||||||
viewModel.pop();
|
viewModel.pop();
|
||||||
viewModel.stopRecording();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showSheet(
|
Future<void> _showSheet(
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,8 @@ class LearnPracticeQuestionsScreen
|
||||||
required LearnQuestion question,
|
required LearnQuestion question,
|
||||||
}) =>
|
}) =>
|
||||||
[
|
[
|
||||||
if(index ==1) _buildStartLearnPracticeScreen(index: index, question: question),
|
if (index == 1)
|
||||||
|
_buildStartLearnPracticeScreen(index: index, question: question),
|
||||||
_buildInteractLearnPracticeScreen(index: index, question: question)
|
_buildInteractLearnPracticeScreen(index: index, question: question)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@ class LearnPracticeResultScreen
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||||
|
await viewModel.stopRecording();
|
||||||
viewModel.pop();
|
viewModel.pop();
|
||||||
viewModel.pop();
|
viewModel.pop();
|
||||||
viewModel.stopRecording();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showSheet(
|
Future<void> _showSheet(
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@ class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
{super.key, required this.index, required this.question});
|
{super.key, required this.index, required this.question});
|
||||||
|
|
||||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||||
|
await viewModel.stopRecording();
|
||||||
viewModel.pop();
|
viewModel.pop();
|
||||||
viewModel.pop();
|
viewModel.pop();
|
||||||
viewModel.stopRecording();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _start(LearnPracticeViewModel viewModel) {
|
void _start(LearnPracticeViewModel viewModel) {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
|
|
||||||
import 'package:yimaru_app/ui/widgets/profile_card.dart';
|
import 'package:yimaru_app/ui/widgets/profile_card.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/profile_image.dart';
|
import 'package:yimaru_app/ui/widgets/profile_image.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/view_profile_button.dart';
|
import 'package:yimaru_app/ui/widgets/view_profile_button.dart';
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,8 @@ class StartupViewModel extends ReactiveViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
Future<void> replaceWithFailure() async => await _navigationService.replaceWithFailureView(
|
Future<void> replaceWithFailure() async =>
|
||||||
|
await _navigationService.replaceWithFailureView(
|
||||||
label: 'Check you internet connection',
|
label: 'Check you internet connection',
|
||||||
onTap: () async => await _getProfileStatus());
|
onTap: () async => await _getProfileStatus());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
: viewModel.player.state == PlayerState.playing
|
||||||
? _buildPauseIcon()
|
? _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,
|
||||||
|
|
|
||||||
|
|
@ -46,5 +46,6 @@ class LearnPracticeResultsWrapper
|
||||||
itemBuilder: (context, index) => _buildResult(viewModel.answers[index]),
|
itemBuilder: (context, index) => _buildResult(viewModel.answers[index]),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildResult(Map<String, dynamic> answer) => LearnPracticeResultCard(answer: answer);
|
Widget _buildResult(Map<String, dynamic> answer) =>
|
||||||
|
LearnPracticeResultCard(answer: answer);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ class NoDataIndicator extends StatelessWidget {
|
||||||
_buildIconWrapper(),
|
_buildIconWrapper(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
Widget _buildTitle() => Text(
|
||||||
|
|
@ -40,6 +39,4 @@ class NoDataIndicator extends StatelessWidget {
|
||||||
size: 75,
|
size: 75,
|
||||||
color: kcPrimaryColor,
|
color: kcPrimaryColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
name: yimaru_app
|
name: yimaru_app
|
||||||
version: 0.1.10+12
|
version: 0.1.11+13
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
description: A new Flutter project.
|
description: A new Flutter project.
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import 'package:yimaru_app/services/course_service.dart';
|
||||||
import 'package:yimaru_app/services/audio_player_service.dart';
|
import 'package:yimaru_app/services/audio_player_service.dart';
|
||||||
import 'package:yimaru_app/services/voice_recorder_service.dart';
|
import 'package:yimaru_app/services/voice_recorder_service.dart';
|
||||||
import 'package:yimaru_app/services/in_app_update_service.dart';
|
import 'package:yimaru_app/services/in_app_update_service.dart';
|
||||||
|
import 'package:yimaru_app/services/vimeo_service.dart';
|
||||||
// @stacked-import
|
// @stacked-import
|
||||||
|
|
||||||
import 'test_helpers.mocks.dart';
|
import 'test_helpers.mocks.dart';
|
||||||
|
|
@ -44,6 +45,8 @@ import 'test_helpers.mocks.dart';
|
||||||
MockSpec<AudioPlayerService>(onMissingStub: OnMissingStub.returnDefault),
|
MockSpec<AudioPlayerService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
MockSpec<VoiceRecorderService>(onMissingStub: OnMissingStub.returnDefault),
|
MockSpec<VoiceRecorderService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
MockSpec<InAppUpdateService>(onMissingStub: OnMissingStub.returnDefault),
|
MockSpec<InAppUpdateService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
MockSpec<VimeoService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
MockSpec<VimeoService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
// @stacked-mock-spec
|
// @stacked-mock-spec
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
@ -66,6 +69,8 @@ void registerServices() {
|
||||||
getAndRegisterAudioPlayerService();
|
getAndRegisterAudioPlayerService();
|
||||||
getAndRegisterVoiceRecorderService();
|
getAndRegisterVoiceRecorderService();
|
||||||
getAndRegisterInAppUpdateService();
|
getAndRegisterInAppUpdateService();
|
||||||
|
getAndRegisterVimeoService();
|
||||||
|
getAndRegisterVimeoService();
|
||||||
// @stacked-mock-register
|
// @stacked-mock-register
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,6 +232,13 @@ MockInAppUpdateService getAndRegisterInAppUpdateService() {
|
||||||
locator.registerSingleton<InAppUpdateService>(service);
|
locator.registerSingleton<InAppUpdateService>(service);
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MockVimeoService getAndRegisterVimeoService() {
|
||||||
|
_removeRegistrationIfExists<VimeoService>();
|
||||||
|
final service = MockVimeoService();
|
||||||
|
locator.registerSingleton<VimeoService>(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
// @stacked-mock-create
|
// @stacked-mock-create
|
||||||
|
|
||||||
void _removeRegistrationIfExists<T extends Object>() {
|
void _removeRegistrationIfExists<T extends Object>() {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
11
test/services/vimeo_service_test.dart
Normal file
11
test/services/vimeo_service_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('VimeoServiceTest -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user