fix(learn): Restructure learn hierarchy.
This commit is contained in:
parent
5d35b02d5e
commit
1599bfa0ba
|
|
@ -14,11 +14,27 @@
|
|||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "900714037062-ngc0gc426sfnnjjr494g4vni46ne5uqv.apps.googleusercontent.com",
|
||||
"client_id": "900714037062-4trqu7ln6en4kcm6gadk0uo01qijn1mk.apps.googleusercontent.com",
|
||||
"client_type": 1,
|
||||
"android_info": {
|
||||
"package_name": "com.yimaru.lms.app",
|
||||
"certificate_hash": "139ee56ac9763191d1eee882efc440c10530e6e9"
|
||||
"certificate_hash": "b9cb22406df59b3b9b210896cc10fc704cc25858"
|
||||
}
|
||||
},
|
||||
{
|
||||
"client_id": "900714037062-d9aqa2eoni3ppumdpi39d5rtub2bsbbs.apps.googleusercontent.com",
|
||||
"client_type": 1,
|
||||
"android_info": {
|
||||
"package_name": "com.yimaru.lms.app",
|
||||
"certificate_hash": "29797902ad6a24212b9d9fad71562907956f6a6c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"client_id": "900714037062-ok9oeme95rfcvljtg065aj0f7mmsr0fa.apps.googleusercontent.com",
|
||||
"client_type": 1,
|
||||
"android_info": {
|
||||
"package_name": "com.yimaru.lms.app",
|
||||
"certificate_hash": "928ead08b5e39d6a861a55ae7cceb8c402d1ee7a"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,4 +1,5 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// dart format width=80
|
||||
|
||||
// **************************************************************************
|
||||
// StackedBottomsheetGenerator
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ import 'package:yimaru_app/ui/views/privacy_policy/privacy_policy_view.dart';
|
|||
import 'package:yimaru_app/ui/views/terms_and_conditions/terms_and_conditions_view.dart';
|
||||
import 'package:yimaru_app/ui/views/register/register_view.dart';
|
||||
import 'package:yimaru_app/ui/views/login/login_view.dart';
|
||||
import 'package:yimaru_app/ui/views/learn/learn_view.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_level/learn_level_view.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_module/learn_module_view.dart';
|
||||
import 'package:yimaru_app/services/authentication_service.dart';
|
||||
import 'package:yimaru_app/services/api_service.dart';
|
||||
|
|
@ -51,8 +49,9 @@ import 'package:yimaru_app/ui/views/course/course_view.dart';
|
|||
import 'package:yimaru_app/services/audio_player_service.dart';
|
||||
import 'package:yimaru_app/services/voice_recorder_service.dart';
|
||||
import 'package:yimaru_app/ui/views/course_practice_question/course_practice_question_view.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_subcategory/learn_subcategory_view.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_submodule/learn_submodule_view.dart';
|
||||
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_course/learn_course_view.dart';
|
||||
// @stacked-import
|
||||
|
||||
@StackedApp(
|
||||
|
|
@ -73,8 +72,6 @@ import 'package:yimaru_app/ui/views/learn_submodule/learn_submodule_view.dart';
|
|||
MaterialRoute(page: TermsAndConditionsView),
|
||||
MaterialRoute(page: RegisterView),
|
||||
MaterialRoute(page: LoginView),
|
||||
MaterialRoute(page: LearnView),
|
||||
MaterialRoute(page: LearnLevelView),
|
||||
MaterialRoute(page: LearnModuleView),
|
||||
MaterialRoute(page: WelcomeView),
|
||||
MaterialRoute(page: AssessmentView),
|
||||
|
|
@ -92,8 +89,8 @@ import 'package:yimaru_app/ui/views/learn_submodule/learn_submodule_view.dart';
|
|||
MaterialRoute(page: CourseSubcategoryView),
|
||||
MaterialRoute(page: CourseView),
|
||||
MaterialRoute(page: CoursePracticeQuestionView),
|
||||
MaterialRoute(page: LearnSubcategoryView),
|
||||
MaterialRoute(page: LearnSubmoduleView),
|
||||
MaterialRoute(page: LearnProgramView),
|
||||
MaterialRoute(page: LearnCourseView),
|
||||
// @stacked-route
|
||||
],
|
||||
dependencies: [
|
||||
|
|
@ -114,6 +111,7 @@ import 'package:yimaru_app/ui/views/learn_submodule/learn_submodule_view.dart';
|
|||
LazySingleton(classType: CourseService),
|
||||
LazySingleton(classType: AudioPlayerService),
|
||||
LazySingleton(classType: VoiceRecorderService),
|
||||
LazySingleton(classType: InAppUpdateService),
|
||||
// @stacked-service
|
||||
],
|
||||
bottomsheets: [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// dart format width=80
|
||||
|
||||
// **************************************************************************
|
||||
// StackedDialogGenerator
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// dart format width=80
|
||||
|
||||
// **************************************************************************
|
||||
// StackedLocatorGenerator
|
||||
|
|
@ -19,6 +20,7 @@ import '../services/dio_service.dart';
|
|||
import '../services/google_auth_service.dart';
|
||||
import '../services/image_downloader_service.dart';
|
||||
import '../services/image_picker_service.dart';
|
||||
import '../services/in_app_update_service.dart';
|
||||
import '../services/notification_service.dart';
|
||||
import '../services/permission_handler_service.dart';
|
||||
import '../services/secure_storage_service.dart';
|
||||
|
|
@ -28,10 +30,8 @@ import '../services/voice_recorder_service.dart';
|
|||
|
||||
final locator = StackedLocator.instance;
|
||||
|
||||
Future<void> setupLocator({
|
||||
String? environment,
|
||||
EnvironmentFilter? environmentFilter,
|
||||
}) async {
|
||||
Future<void> setupLocator(
|
||||
{String? environment, EnvironmentFilter? environmentFilter}) async {
|
||||
// Register environments
|
||||
locator.registerEnvironment(
|
||||
environment: environment, environmentFilter: environmentFilter);
|
||||
|
|
@ -54,4 +54,5 @@ Future<void> setupLocator({
|
|||
locator.registerLazySingleton(() => CourseService());
|
||||
locator.registerLazySingleton(() => AudioPlayerService());
|
||||
locator.registerLazySingleton(() => VoiceRecorderService());
|
||||
locator.registerLazySingleton(() => InAppUpdateService());
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -64,7 +64,7 @@ class DefaultFirebaseOptions {
|
|||
projectId: 'yimaru-academy-5e7e2',
|
||||
storageBucket: 'yimaru-academy-5e7e2.firebasestorage.app',
|
||||
androidClientId:
|
||||
'900714037062-ngc0gc426sfnnjjr494g4vni46ne5uqv.apps.googleusercontent.com',
|
||||
'900714037062-4trqu7ln6en4kcm6gadk0uo01qijn1mk.apps.googleusercontent.com',
|
||||
iosClientId:
|
||||
'900714037062-35bg0hsou56hg37mbcbpiar9uti7tcku.apps.googleusercontent.com',
|
||||
iosBundleId: 'com.yimaru.lms.app',
|
||||
|
|
|
|||
35
lib/models/access.dart
Normal file
35
lib/models/access.dart
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'access.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class Access {
|
||||
final String? reason;
|
||||
|
||||
@JsonKey(name: 'total_count')
|
||||
final int? totalCount;
|
||||
|
||||
@JsonKey(name: 'is_completed')
|
||||
final bool? isCompleted;
|
||||
|
||||
@JsonKey(name: 'is_accessible')
|
||||
final bool? isAccessible;
|
||||
|
||||
@JsonKey(name: 'completed_count')
|
||||
final int? completedCount;
|
||||
|
||||
@JsonKey(name: 'progress_percent')
|
||||
final int? progressPercent;
|
||||
|
||||
const Access(
|
||||
{this.reason,
|
||||
this.totalCount,
|
||||
this.isCompleted,
|
||||
this.isAccessible,
|
||||
this.completedCount,
|
||||
this.progressPercent});
|
||||
|
||||
factory Access.fromJson(Map<String, dynamic> json) => _$AccessFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$AccessToJson(this);
|
||||
}
|
||||
25
lib/models/access.g.dart
Normal file
25
lib/models/access.g.dart
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'access.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
Access _$AccessFromJson(Map<String, dynamic> json) => Access(
|
||||
reason: json['reason'] as String?,
|
||||
totalCount: (json['total_count'] as num?)?.toInt(),
|
||||
isCompleted: json['is_completed'] as bool?,
|
||||
isAccessible: json['is_accessible'] as bool?,
|
||||
completedCount: (json['completed_count'] as num?)?.toInt(),
|
||||
progressPercent: (json['progress_percent'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$AccessToJson(Access instance) => <String, dynamic>{
|
||||
'reason': instance.reason,
|
||||
'total_count': instance.totalCount,
|
||||
'is_completed': instance.isCompleted,
|
||||
'is_accessible': instance.isAccessible,
|
||||
'completed_count': instance.completedCount,
|
||||
'progress_percent': instance.progressPercent,
|
||||
};
|
||||
34
lib/models/learn_course.dart
Normal file
34
lib/models/learn_course.dart
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:yimaru_app/models/access.dart';
|
||||
|
||||
part 'learn_course.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class LearnCourse {
|
||||
final int? id;
|
||||
|
||||
final String? name;
|
||||
|
||||
final Access? access;
|
||||
|
||||
final String? description;
|
||||
|
||||
@JsonKey(name: 'sort_order')
|
||||
final int? sortOrder;
|
||||
|
||||
@JsonKey(name: 'program_id')
|
||||
final int? programId;
|
||||
|
||||
const LearnCourse(
|
||||
{this.id,
|
||||
this.name,
|
||||
this.access,
|
||||
this.programId,
|
||||
this.sortOrder,
|
||||
this.description});
|
||||
|
||||
factory LearnCourse.fromJson(Map<String, dynamic> json) =>
|
||||
_$LearnCourseFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LearnCourseToJson(this);
|
||||
}
|
||||
28
lib/models/learn_course.g.dart
Normal file
28
lib/models/learn_course.g.dart
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'learn_course.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
LearnCourse _$LearnCourseFromJson(Map<String, dynamic> json) => LearnCourse(
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
name: json['name'] as String?,
|
||||
access: json['access'] == null
|
||||
? null
|
||||
: Access.fromJson(json['access'] as Map<String, dynamic>),
|
||||
programId: (json['program_id'] as num?)?.toInt(),
|
||||
sortOrder: (json['sort_order'] as num?)?.toInt(),
|
||||
description: json['description'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LearnCourseToJson(LearnCourse instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'access': instance.access,
|
||||
'description': instance.description,
|
||||
'sort_order': instance.sortOrder,
|
||||
'program_id': instance.programId,
|
||||
};
|
||||
43
lib/models/learn_lesson.dart
Normal file
43
lib/models/learn_lesson.dart
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
import 'access.dart';
|
||||
|
||||
part 'learn_lesson.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class LearnLesson {
|
||||
final int? id;
|
||||
|
||||
final String? title;
|
||||
|
||||
final Access? access;
|
||||
|
||||
final String? thumbnail;
|
||||
|
||||
final String? description;
|
||||
|
||||
@JsonKey(name: 'module_id')
|
||||
final int? moduleId;
|
||||
|
||||
@JsonKey(name: 'sort_order')
|
||||
final int? sortOrder;
|
||||
|
||||
@JsonKey(name: 'video_url')
|
||||
final String? videoUrl;
|
||||
|
||||
const LearnLesson({
|
||||
this.id,
|
||||
this.title,
|
||||
this.access,
|
||||
this.videoUrl,
|
||||
this.moduleId,
|
||||
this.thumbnail,
|
||||
this.sortOrder,
|
||||
this.description,
|
||||
});
|
||||
|
||||
factory LearnLesson.fromJson(Map<String, dynamic> json) =>
|
||||
_$LearnLessonFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LearnLessonToJson(this);
|
||||
}
|
||||
32
lib/models/learn_lesson.g.dart
Normal file
32
lib/models/learn_lesson.g.dart
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'learn_lesson.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
LearnLesson _$LearnLessonFromJson(Map<String, dynamic> json) => LearnLesson(
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
title: json['title'] as String?,
|
||||
access: json['access'] == null
|
||||
? null
|
||||
: Access.fromJson(json['access'] as Map<String, dynamic>),
|
||||
videoUrl: json['video_url'] as String?,
|
||||
moduleId: (json['module_id'] as num?)?.toInt(),
|
||||
thumbnail: json['thumbnail'] as String?,
|
||||
sortOrder: (json['sort_order'] as num?)?.toInt(),
|
||||
description: json['description'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LearnLessonToJson(LearnLesson instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'title': instance.title,
|
||||
'access': instance.access,
|
||||
'thumbnail': instance.thumbnail,
|
||||
'description': instance.description,
|
||||
'module_id': instance.moduleId,
|
||||
'sort_order': instance.sortOrder,
|
||||
'video_url': instance.videoUrl,
|
||||
};
|
||||
41
lib/models/learn_module.dart
Normal file
41
lib/models/learn_module.dart
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:yimaru_app/models/access.dart';
|
||||
|
||||
part 'learn_module.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class LearnModule {
|
||||
final int? id;
|
||||
|
||||
final String? icon;
|
||||
|
||||
final String? name;
|
||||
|
||||
final Access? access;
|
||||
|
||||
final String? description;
|
||||
|
||||
@JsonKey(name: 'program_id')
|
||||
final int? programId;
|
||||
|
||||
@JsonKey(name: 'course_id')
|
||||
final int? courseId;
|
||||
|
||||
@JsonKey(name: 'sort_order')
|
||||
final int? sortOrder;
|
||||
|
||||
const LearnModule(
|
||||
{this.id,
|
||||
this.icon,
|
||||
this.name,
|
||||
this.access,
|
||||
this.courseId,
|
||||
this.sortOrder,
|
||||
this.programId,
|
||||
this.description});
|
||||
|
||||
factory LearnModule.fromJson(Map<String, dynamic> json) =>
|
||||
_$LearnModuleFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LearnModuleToJson(this);
|
||||
}
|
||||
32
lib/models/learn_module.g.dart
Normal file
32
lib/models/learn_module.g.dart
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'learn_module.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
LearnModule _$LearnModuleFromJson(Map<String, dynamic> json) => LearnModule(
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
icon: json['icon'] as String?,
|
||||
name: json['name'] as String?,
|
||||
access: json['access'] == null
|
||||
? null
|
||||
: Access.fromJson(json['access'] as Map<String, dynamic>),
|
||||
courseId: (json['course_id'] as num?)?.toInt(),
|
||||
sortOrder: (json['sort_order'] as num?)?.toInt(),
|
||||
programId: (json['program_id'] as num?)?.toInt(),
|
||||
description: json['description'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LearnModuleToJson(LearnModule instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'icon': instance.icon,
|
||||
'name': instance.name,
|
||||
'access': instance.access,
|
||||
'description': instance.description,
|
||||
'program_id': instance.programId,
|
||||
'course_id': instance.courseId,
|
||||
'sort_order': instance.sortOrder,
|
||||
};
|
||||
43
lib/models/learn_practice.dart
Normal file
43
lib/models/learn_practice.dart
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'learn_practice.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class LearnPractice {
|
||||
final int? id;
|
||||
|
||||
final String? title;
|
||||
|
||||
@JsonKey(name: 'parent_id')
|
||||
final int? parentId;
|
||||
|
||||
@JsonKey(name: 'quick_tips')
|
||||
final String? quickTips;
|
||||
|
||||
@JsonKey(name: 'story_image')
|
||||
final String? storyImage;
|
||||
|
||||
@JsonKey(name: 'parent_kind')
|
||||
final String? parentKind;
|
||||
|
||||
@JsonKey(name: 'question_set_id')
|
||||
final int? questionSetId;
|
||||
|
||||
@JsonKey(name: 'story_description')
|
||||
final String? storyDescription;
|
||||
|
||||
const LearnPractice(
|
||||
{this.id,
|
||||
this.title,
|
||||
this.parentId,
|
||||
this.quickTips,
|
||||
this.storyImage,
|
||||
this.parentKind,
|
||||
this.questionSetId,
|
||||
this.storyDescription});
|
||||
|
||||
factory LearnPractice.fromJson(Map<String, dynamic> json) =>
|
||||
_$LearnPracticeFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LearnPracticeToJson(this);
|
||||
}
|
||||
31
lib/models/learn_practice.g.dart
Normal file
31
lib/models/learn_practice.g.dart
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'learn_practice.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
LearnPractice _$LearnPracticeFromJson(Map<String, dynamic> json) =>
|
||||
LearnPractice(
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
title: json['title'] as String?,
|
||||
parentId: (json['parent_id'] as num?)?.toInt(),
|
||||
quickTips: json['quick_tips'] as String?,
|
||||
storyImage: json['story_image'] as String?,
|
||||
parentKind: json['parent_kind'] as String?,
|
||||
questionSetId: (json['question_set_id'] as num?)?.toInt(),
|
||||
storyDescription: json['story_description'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LearnPracticeToJson(LearnPractice instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'title': instance.title,
|
||||
'parent_id': instance.parentId,
|
||||
'quick_tips': instance.quickTips,
|
||||
'story_image': instance.storyImage,
|
||||
'parent_kind': instance.parentKind,
|
||||
'question_set_id': instance.questionSetId,
|
||||
'story_description': instance.storyDescription,
|
||||
};
|
||||
26
lib/models/learn_program.dart
Normal file
26
lib/models/learn_program.dart
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:yimaru_app/models/access.dart';
|
||||
|
||||
part 'learn_program.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class LearnProgram {
|
||||
final int? id;
|
||||
|
||||
final String? name;
|
||||
|
||||
final Access? access;
|
||||
|
||||
final String? description;
|
||||
|
||||
@JsonKey(name: 'sort_order')
|
||||
final int? sortOrder;
|
||||
|
||||
const LearnProgram(
|
||||
{this.id, this.name, this.access, this.sortOrder, this.description});
|
||||
|
||||
factory LearnProgram.fromJson(Map<String, dynamic> json) =>
|
||||
_$LearnProgramFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LearnProgramToJson(this);
|
||||
}
|
||||
26
lib/models/learn_program.g.dart
Normal file
26
lib/models/learn_program.g.dart
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'learn_program.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
LearnProgram _$LearnProgramFromJson(Map<String, dynamic> json) => LearnProgram(
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
name: json['name'] as String?,
|
||||
access: json['access'] == null
|
||||
? null
|
||||
: Access.fromJson(json['access'] as Map<String, dynamic>),
|
||||
sortOrder: (json['sort_order'] as num?)?.toInt(),
|
||||
description: json['description'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LearnProgramToJson(LearnProgram instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'access': instance.access,
|
||||
'description': instance.description,
|
||||
'sort_order': instance.sortOrder,
|
||||
};
|
||||
56
lib/models/learn_question.dart
Normal file
56
lib/models/learn_question.dart
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:yimaru_app/models/option.dart';
|
||||
|
||||
part 'learn_question.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class LearnQuestion {
|
||||
final int? id;
|
||||
|
||||
final int? points;
|
||||
|
||||
@JsonKey(name: 'set_id')
|
||||
final int? setId;
|
||||
|
||||
@JsonKey(name: 'question_id')
|
||||
final int? questionId;
|
||||
|
||||
@JsonKey(name: 'voice_prompt')
|
||||
final String? voicePrompt;
|
||||
|
||||
@JsonKey(name: 'question_text')
|
||||
final String? questionText;
|
||||
|
||||
@JsonKey(name: 'display_order')
|
||||
final int? displayOrder;
|
||||
|
||||
@JsonKey(name: 'question_type')
|
||||
final String? questionType;
|
||||
|
||||
@JsonKey(name: 'question_status')
|
||||
final String? questionStatus;
|
||||
|
||||
@JsonKey(name: 'audio_correct_answer_text')
|
||||
final String? audioCorrectAnswerText;
|
||||
|
||||
@JsonKey(name: 'sample_answer_voice_prompt')
|
||||
final String? sampleAnswerVoicePrompt;
|
||||
|
||||
const LearnQuestion(
|
||||
{this.id,
|
||||
this.setId,
|
||||
this.points,
|
||||
this.questionId,
|
||||
this.voicePrompt,
|
||||
this.questionText,
|
||||
this.questionType,
|
||||
this.displayOrder,
|
||||
this.questionStatus,
|
||||
this.audioCorrectAnswerText,
|
||||
this.sampleAnswerVoicePrompt});
|
||||
|
||||
factory LearnQuestion.fromJson(Map<String, dynamic> json) =>
|
||||
_$LearnQuestionFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LearnQuestionToJson(this);
|
||||
}
|
||||
|
|
@ -1,33 +1,37 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'practice_question.dart';
|
||||
part of 'learn_question.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
PracticeQuestion _$PracticeQuestionFromJson(Map<String, dynamic> json) =>
|
||||
PracticeQuestion(
|
||||
LearnQuestion _$LearnQuestionFromJson(Map<String, dynamic> json) =>
|
||||
LearnQuestion(
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
tips: json['tips'] as String?,
|
||||
setId: (json['set_id'] as num?)?.toInt(),
|
||||
points: (json['points'] as num?)?.toInt(),
|
||||
questionId: (json['question_id'] as num?)?.toInt(),
|
||||
voicePrompt: json['voice_prompt'] as String?,
|
||||
questionText: json['question_text'] as String?,
|
||||
questionType: json['question_type'] as String?,
|
||||
displayOrder: (json['display_order'] as num?)?.toInt(),
|
||||
questionStatus: json['question_status'] as String?,
|
||||
audioCorrectAnswerText: json['audio_correct_answer_text'] as String?,
|
||||
sampleAnswerVoicePrompt: json['sample_answer_voice_prompt'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$PracticeQuestionToJson(PracticeQuestion instance) =>
|
||||
Map<String, dynamic> _$LearnQuestionToJson(LearnQuestion instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'points': instance.points,
|
||||
'tips': instance.tips,
|
||||
'set_id': instance.setId,
|
||||
'question_id': instance.questionId,
|
||||
'display_order': instance.displayOrder,
|
||||
'voice_prompt': instance.voicePrompt,
|
||||
'question_text': instance.questionText,
|
||||
'display_order': instance.displayOrder,
|
||||
'question_type': instance.questionType,
|
||||
'question_status': instance.questionStatus,
|
||||
'audio_correct_answer_text': instance.audioCorrectAnswerText,
|
||||
'sample_answer_voice_prompt': instance.sampleAnswerVoicePrompt,
|
||||
};
|
||||
|
|
@ -22,9 +22,15 @@ class Practice {
|
|||
@JsonKey(name: 'owner_type')
|
||||
final String? ownerType;
|
||||
|
||||
@JsonKey(name: 'intro_video_url')
|
||||
final String? introVideoUrl;
|
||||
|
||||
@JsonKey(name: 'shuffle_questions')
|
||||
final bool? shuffleQuestions;
|
||||
|
||||
@JsonKey(name: 'time_limit_minutes')
|
||||
final int? timeLimitMinutes;
|
||||
|
||||
const Practice(
|
||||
{this.id,
|
||||
this.title,
|
||||
|
|
@ -34,7 +40,9 @@ class Practice {
|
|||
this.ownerId,
|
||||
this.ownerType,
|
||||
this.description,
|
||||
this.shuffleQuestions});
|
||||
this.introVideoUrl,
|
||||
this.shuffleQuestions,
|
||||
this.timeLimitMinutes});
|
||||
|
||||
factory Practice.fromJson(Map<String, dynamic> json) =>
|
||||
_$PracticeFromJson(json);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ Practice _$PracticeFromJson(Map<String, dynamic> json) => Practice(
|
|||
ownerId: (json['owner_id'] as num?)?.toInt(),
|
||||
ownerType: json['owner_type'] as String?,
|
||||
description: json['description'] as String?,
|
||||
introVideoUrl: json['intro_video_url'] as String?,
|
||||
shuffleQuestions: json['shuffle_questions'] as bool?,
|
||||
timeLimitMinutes: (json['time_limit_minutes'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$PracticeToJson(Practice instance) => <String, dynamic>{
|
||||
|
|
@ -27,5 +29,7 @@ Map<String, dynamic> _$PracticeToJson(Practice instance) => <String, dynamic>{
|
|||
'owner_id': instance.ownerId,
|
||||
'set_type': instance.setType,
|
||||
'owner_type': instance.ownerType,
|
||||
'intro_video_url': instance.introVideoUrl,
|
||||
'shuffle_questions': instance.shuffleQuestions,
|
||||
'time_limit_minutes': instance.timeLimitMinutes,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'practice_question.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class PracticeQuestion {
|
||||
final int? id;
|
||||
|
||||
final int? points;
|
||||
|
||||
final String? tips;
|
||||
|
||||
@JsonKey(name: 'set_id')
|
||||
final int? setId;
|
||||
|
||||
@JsonKey(name: 'question_id')
|
||||
final int? questionId;
|
||||
|
||||
@JsonKey(name: 'display_order')
|
||||
final int? displayOrder;
|
||||
|
||||
@JsonKey(name: 'question_text')
|
||||
final String? questionText;
|
||||
|
||||
@JsonKey(name: 'question_type')
|
||||
final String? questionType;
|
||||
|
||||
@JsonKey(name: 'question_status')
|
||||
final String? questionStatus;
|
||||
|
||||
const PracticeQuestion(
|
||||
{this.id,
|
||||
this.tips,
|
||||
this.setId,
|
||||
this.points,
|
||||
this.questionId,
|
||||
this.questionText,
|
||||
this.questionType,
|
||||
this.displayOrder,
|
||||
this.questionStatus});
|
||||
|
||||
factory PracticeQuestion.fromJson(Map<String, dynamic> json) =>
|
||||
_$PracticeQuestionFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$PracticeQuestionToJson(this);
|
||||
}
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
import 'package:dio/dio.dart';
|
||||
import 'package:yimaru_app/models/learn_lesson.dart';
|
||||
import 'package:yimaru_app/models/learn_practice.dart';
|
||||
import 'package:yimaru_app/models/learn_program.dart';
|
||||
import 'package:yimaru_app/models/level.dart';
|
||||
import 'package:yimaru_app/models/question.dart';
|
||||
import 'package:yimaru_app/models/subcategory.dart';
|
||||
|
|
@ -7,12 +10,14 @@ import 'package:yimaru_app/models/course_lesson.dart';
|
|||
import 'package:yimaru_app/models/course_progress.dart';
|
||||
import 'package:yimaru_app/models/course.dart';
|
||||
import 'package:yimaru_app/models/practice.dart';
|
||||
import 'package:yimaru_app/models/practice_question.dart';
|
||||
import 'package:yimaru_app/models/user.dart';
|
||||
import 'package:yimaru_app/services/dio_service.dart';
|
||||
import 'package:yimaru_app/ui/common/app_constants.dart';
|
||||
|
||||
import '../app/app.locator.dart';
|
||||
import '../models/learn_course.dart';
|
||||
import '../models/learn_module.dart';
|
||||
import '../models/learn_question.dart';
|
||||
import '../models/lesson.dart';
|
||||
import '../models/module.dart';
|
||||
import '../models/submodule.dart';
|
||||
|
|
@ -215,7 +220,7 @@ class ApiService {
|
|||
}
|
||||
}
|
||||
|
||||
// GEt profile completion status
|
||||
// Get profile completion status
|
||||
Future<Map<String, dynamic>> getProfileStatus(User? user) async {
|
||||
try {
|
||||
Response response = await _service.dio.get(
|
||||
|
|
@ -373,6 +378,201 @@ class ApiService {
|
|||
}
|
||||
}
|
||||
|
||||
// Learn learn programs
|
||||
Future<List<LearnProgram>> getLearnPrograms() async {
|
||||
try {
|
||||
List<LearnProgram> learnPrograms = [];
|
||||
|
||||
final Response response =
|
||||
await _service.dio.get('$kBaseUrl/api/$kApiVersionUrl/$kProgramsUrl');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['programs'] as List;
|
||||
learnPrograms = decodedData.map(
|
||||
(e) {
|
||||
return LearnProgram.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return learnPrograms;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Learn learn courses
|
||||
Future<List<LearnCourse>> getLearnCourse(int id) async {
|
||||
try {
|
||||
List<LearnCourse> learnCourses = [];
|
||||
|
||||
final Response response = await _service.dio
|
||||
.get('$kBaseUrl/api/$kApiVersionUrl/$kProgramsUrl/$id/$kCoursesUrl');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['courses'] as List;
|
||||
learnCourses = decodedData.map(
|
||||
(e) {
|
||||
return LearnCourse.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return learnCourses;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Learn course practices
|
||||
Future<List<LearnPractice>> getLearnCoursePractices(int id) async {
|
||||
try {
|
||||
List<LearnPractice> practices = [];
|
||||
|
||||
final Response response = await _service.dio
|
||||
.get('$kBaseUrl/api/$kApiVersionUrl/$kCoursesUrl/$id/$kPracticesUrl');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['practices'] as List;
|
||||
practices = decodedData.map(
|
||||
(e) {
|
||||
return LearnPractice.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return practices;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get learn modules
|
||||
Future<List<LearnModule>> getLearnModules(int id) async {
|
||||
try {
|
||||
List<LearnModule> modules = [];
|
||||
|
||||
final Response response = await _service.dio
|
||||
.get('$kBaseUrl/api/$kApiVersionUrl/$kCoursesUrl/$id/$kModulesUrl');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['modules'] as List;
|
||||
modules = decodedData.map(
|
||||
(e) {
|
||||
return LearnModule.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return modules;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Learn module practices
|
||||
Future<List<LearnPractice>> getLearnModulePractices(int id) async {
|
||||
try {
|
||||
List<LearnPractice> practices = [];
|
||||
|
||||
final Response response = await _service.dio
|
||||
.get('$kBaseUrl/api/$kApiVersionUrl/$kModulesUrl/$id/$kPracticesUrl');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data'] as List;
|
||||
practices = decodedData.map(
|
||||
(e) {
|
||||
return LearnPractice.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return practices;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get learn lessons
|
||||
Future<List<LearnLesson>> getLearnLessons(int id) async {
|
||||
try {
|
||||
List<LearnLesson> lessons = [];
|
||||
|
||||
final Response response = await _service.dio
|
||||
.get('$kBaseUrl/api/$kApiVersionUrl/$kModulesUrl/$id/$kLessonsUrl');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['lessons'] as List;
|
||||
lessons = decodedData.map(
|
||||
(e) {
|
||||
return LearnLesson.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return lessons;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Learn lesson practices
|
||||
Future<List<LearnPractice>> getLearnLessonPractices(int id) async {
|
||||
try {
|
||||
List<LearnPractice> practices = [];
|
||||
|
||||
final Response response = await _service.dio
|
||||
.get('$kBaseUrl/api/$kApiVersionUrl/$kLessonsUrl/$id/$kPracticesUrl');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['practices'] as List;
|
||||
practices = decodedData.map(
|
||||
(e) {
|
||||
return LearnPractice.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return practices;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get learn questions
|
||||
Future<List<LearnQuestion>> getLearnQuestions(int id) async {
|
||||
try {
|
||||
List<LearnQuestion> questions = [];
|
||||
print('Here');
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/api/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data'] as List;
|
||||
questions = decodedData.map(
|
||||
(e) {
|
||||
return LearnQuestion.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return questions;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/* TO BE MODIFIED*/
|
||||
|
||||
// Get categories
|
||||
Future<List<Category>> getCategories() async {
|
||||
try {
|
||||
|
|
@ -541,9 +741,9 @@ class ApiService {
|
|||
}
|
||||
|
||||
// Get course practic questions
|
||||
Future<List<PracticeQuestion>> getCoursePracticeQuestions(int id) async {
|
||||
Future<List<Question>> getCoursePracticeQuestions(int id) async {
|
||||
try {
|
||||
List<PracticeQuestion> coursePracticeQuestions = [];
|
||||
List<Question> coursePracticeQuestions = [];
|
||||
|
||||
final Response response = await _service.dio
|
||||
.get('$kBaseUrl/$kPracticeBaseUrl/$id/$kCoursePracticeQuestions');
|
||||
|
|
@ -553,7 +753,7 @@ class ApiService {
|
|||
var decodedData = data['data'] as List;
|
||||
coursePracticeQuestions = decodedData.map(
|
||||
(e) {
|
||||
return PracticeQuestion.fromJson(e);
|
||||
return Question.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return coursePracticeQuestions;
|
||||
|
|
@ -724,4 +924,52 @@ class ApiService {
|
|||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Practices
|
||||
Future<List<Practice>> getPractices(int id) async {
|
||||
try {
|
||||
List<Practice> coursePractices = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kPracticeBaseUrl/$kCoursePractice?owner_type=SUB_MODULE&owner_id=$id');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data'] as List;
|
||||
coursePractices = decodedData.map(
|
||||
(e) {
|
||||
return Practice.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return coursePractices;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Questions
|
||||
Future<List<Question>> getQuestions(int id) async {
|
||||
try {
|
||||
List<Question> questions = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/api/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data'] as List;
|
||||
questions = decodedData.map(
|
||||
(e) {
|
||||
return Question.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return questions;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class AudioPlayerService with ListenableServiceMixin {
|
|||
Stream<PlayerState> get stateStream => _player.onPlayerStateChanged;
|
||||
|
||||
Future<void> playUrl(String url) async {
|
||||
final playableUrl = getPlayableUrl(url);
|
||||
final playableUrl = getReadableUrl(url);
|
||||
|
||||
if (playableUrl == null) {
|
||||
throw Exception("Invalid audio URL");
|
||||
|
|
|
|||
54
lib/services/in_app_update_service.dart
Normal file
54
lib/services/in_app_update_service.dart
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import 'package:battery_plus/battery_plus.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:in_app_update/in_app_update.dart';
|
||||
import 'package:storage_info/storage_info.dart';
|
||||
|
||||
class InAppUpdateService {
|
||||
Future<int> getBatteryLevel() async {
|
||||
final battery = Battery();
|
||||
final batteryLevel = await battery.batteryLevel;
|
||||
return batteryLevel;
|
||||
}
|
||||
|
||||
Future<int> getAvailableStorage() async {
|
||||
try {
|
||||
final availableStorage =
|
||||
await StorageInfo().getStorageFreeSpace(SpaceUnit.Bytes);
|
||||
return availableStorage.toInt(); // Convert GB to bytes
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> checkForUpdate() async {
|
||||
const requiredStorage = 500 * 1024 * 1024;
|
||||
|
||||
final batteryLevel =
|
||||
await getBatteryLevel(); // Implement getBatteryLevel function
|
||||
final int storageAvailable =
|
||||
await getAvailableStorage(); // Implement getAvailableStorage
|
||||
if (batteryLevel < 20 || storageAvailable < requiredStorage) {
|
||||
if (batteryLevel < 20 || storageAvailable < requiredStorage) {
|
||||
// KewedeConst().showErrorToast(
|
||||
// 'Unable to update app, please charge your phone & free up space.');
|
||||
} else if (batteryLevel < 20) {
|
||||
// KewedeConst()
|
||||
// .showErrorToast('Unable to update app, please charge your phone.');
|
||||
} else if (storageAvailable < requiredStorage) {
|
||||
// KewedeConst()
|
||||
// .showErrorToast('Unable to update app, please free up space.');
|
||||
}
|
||||
// Show user-friendly message explaining why update failed and suggesting solutions (e.g., charge device, free up space)
|
||||
}
|
||||
try {
|
||||
final info = await InAppUpdate.checkForUpdate();
|
||||
if (info.updateAvailability == UpdateAvailability.updateAvailable) {
|
||||
await InAppUpdate.completeFlexibleUpdate();
|
||||
}
|
||||
|
||||
// ... rest of your update logic ...
|
||||
} on PlatformException {
|
||||
// Handle specific error code for better user experience and potentially different error messages for each issue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,4 @@
|
|||
import 'package:battery_plus/battery_plus.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:in_app_update/in_app_update.dart';
|
||||
import 'package:internet_connection_checker_plus/internet_connection_checker_plus.dart';
|
||||
import 'package:storage_info/storage_info.dart';
|
||||
import 'package:yimaru_app/services/secure_storage_service.dart';
|
||||
|
||||
import '../app/app.locator.dart';
|
||||
|
|
@ -16,13 +12,6 @@ class StatusCheckerService {
|
|||
|
||||
bool get previousConnection => _previousConnection;
|
||||
|
||||
// Get phone battery level
|
||||
Future<int> getBatteryLevel() async {
|
||||
final battery = Battery();
|
||||
final batteryLevel = await battery.batteryLevel;
|
||||
return batteryLevel;
|
||||
}
|
||||
|
||||
// Check internet connection
|
||||
Future<bool> checkConnection() async {
|
||||
if (await InternetConnection().hasInternetAccess) {
|
||||
|
|
@ -36,45 +25,4 @@ class StatusCheckerService {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check phone available storage
|
||||
Future<int> getAvailableStorage() async {
|
||||
try {
|
||||
final availableStorage =
|
||||
await StorageInfo().getStorageFreeSpace(SpaceUnit.Bytes);
|
||||
return availableStorage.toInt(); // Convert GB to bytes
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for latest update
|
||||
Future<void> checkAndUpdate() async {
|
||||
const requiredStorage = 500 * 1024 * 1024;
|
||||
|
||||
final batteryLevel =
|
||||
await getBatteryLevel(); // Implement getBatteryLevel function
|
||||
final int storageAvailable =
|
||||
await getAvailableStorage(); // Implement getAvailableStorage
|
||||
if (batteryLevel < 20 || storageAvailable < requiredStorage) {
|
||||
if (batteryLevel < 20 || storageAvailable < requiredStorage) {
|
||||
// 'Unable to update app, please charge your phone & free up space.');
|
||||
} else if (batteryLevel < 20) {
|
||||
// .showErrorToast('Unable to update app, please charge your phone.');
|
||||
} else if (storageAvailable < requiredStorage) {
|
||||
// .showErrorToast('Unable to update app, please free up space.');
|
||||
}
|
||||
return; // Prevent update from starting
|
||||
}
|
||||
try {
|
||||
if (await checkConnection()) {
|
||||
await InAppUpdate
|
||||
.checkForUpdate(); // Continue update only if sufficient resources available
|
||||
}
|
||||
|
||||
// ... rest of your update logic ...
|
||||
} on PlatformException {
|
||||
// Handle specific error code for better user experience and potentially different error messages for each issue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,12 @@ class VoiceRecorderService with ListenableServiceMixin {
|
|||
|
||||
WaveformRecorderController get waveController => _waveController;
|
||||
|
||||
|
||||
bool _isRecording = false;
|
||||
|
||||
bool get isRecording => _isRecording;
|
||||
|
||||
|
||||
// Start voice recording
|
||||
Future<void> startRecording() async {
|
||||
await _waveController.startRecording();
|
||||
|
|
@ -35,4 +41,5 @@ class VoiceRecorderService with ListenableServiceMixin {
|
|||
if (file == null) return null;
|
||||
return file.path;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,14 @@ String kModulesUrl = 'modules';
|
|||
|
||||
String kLessonsUrl = 'lessons';
|
||||
|
||||
String kProgramsUrl = 'programs';
|
||||
|
||||
String kRegisterUrl = 'register';
|
||||
|
||||
String kPracticesUrl = 'practices';
|
||||
|
||||
String kQuestionsUrl = 'questions';
|
||||
|
||||
String kCategoryUrl = 'categories';
|
||||
|
||||
String kCoursePractice = 'by-owner';
|
||||
|
|
@ -34,6 +40,8 @@ String kCompleteLessonUrl = 'complete';
|
|||
|
||||
String kResetPassword = 'resetPassword';
|
||||
|
||||
String kQuestionSetsUrl = 'question-sets';
|
||||
|
||||
String kRequestResetCode = 'sendResetCode';
|
||||
|
||||
String kSubcategoriesUrl = 'sub-categories';
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// Practice voice
|
||||
enum Voice { sample, recorded }
|
||||
|
||||
// Response status
|
||||
enum ResponseStatus { success, failure }
|
||||
|
||||
|
|
@ -7,6 +10,9 @@ enum LoginMethod { phone, email, google }
|
|||
// Sign-up method
|
||||
enum SignUpMethod { phone, email, google }
|
||||
|
||||
// Learn practice
|
||||
enum LearnPractices { course, module, lesson }
|
||||
|
||||
// Voice recording state
|
||||
enum VoiceRecordingState { pending, recording }
|
||||
|
||||
|
|
@ -16,8 +22,8 @@ enum ProficiencyLevels { a1, a2, b1, b2, none }
|
|||
// Progress status
|
||||
enum ProgressStatuses { pending, started, completed }
|
||||
|
||||
// Duolingo assessment types
|
||||
enum DuolingoAssessmentType { speaking, reading, writing, listening }
|
||||
// Duolingo types
|
||||
enum DuolingoAssessments { speaking, reading, writing, listening }
|
||||
|
||||
// State object
|
||||
enum StateObjects {
|
||||
|
|
@ -27,17 +33,18 @@ enum StateObjects {
|
|||
register,
|
||||
verifyOtp,
|
||||
resendOtp,
|
||||
learnLevels,
|
||||
learnLessons,
|
||||
learnModules,
|
||||
learnCourses,
|
||||
profileImage,
|
||||
learnPrograms,
|
||||
courseLessons,
|
||||
profileUpdate,
|
||||
resetPassword,
|
||||
subcategories,
|
||||
loginWithEmail,
|
||||
coursePractice,
|
||||
learnPractices,
|
||||
loginWithGoogle,
|
||||
loadLessonVideo,
|
||||
loadCourseVideo,
|
||||
|
|
@ -46,7 +53,6 @@ enum StateObjects {
|
|||
courseCategories,
|
||||
profileCompletion,
|
||||
registerWithGoogle,
|
||||
learnSubcategories,
|
||||
learnPracticeSample,
|
||||
learnPracticeAnswer,
|
||||
loginWithPhoneNumber,
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ Color getColor() {
|
|||
}
|
||||
|
||||
// Get playable url
|
||||
String? getPlayableUrl(String url) {
|
||||
String? getReadableUrl(String url) {
|
||||
try {
|
||||
// Case 1: /file/d/FILE_ID/view
|
||||
final fileIdRegex = RegExp(r'/file/d/([a-zA-Z0-9_-]+)');
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// dart format width=80
|
||||
|
||||
// **************************************************************************
|
||||
// StackedFormGenerator
|
||||
|
|
@ -145,7 +146,7 @@ extension ValueProperties on FormStateHelper {
|
|||
}
|
||||
|
||||
extension Methods on FormStateHelper {
|
||||
setAnswerValidationMessage(String? validationMessage) =>
|
||||
void setAnswerValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[AnswerValueKey] = validationMessage;
|
||||
|
||||
/// Clears text input fields on the Form
|
||||
|
|
@ -167,7 +168,7 @@ String? getValidationMessage(String key) {
|
|||
if (validatorForKey == null) return null;
|
||||
|
||||
String? validationMessageForKey = validatorForKey(
|
||||
_CoursePracticeQuestionViewTextEditingControllers[key]!.text,
|
||||
_CoursePracticeQuestionViewTextEditingControllers[key]?.text,
|
||||
);
|
||||
|
||||
return validationMessageForKey;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/models/practice_question.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../models/option.dart';
|
||||
|
|
@ -43,10 +42,9 @@ class CoursePracticeQuestionViewModel extends FormViewModel {
|
|||
|
||||
Question? get currentQuestion => _currentQuestion;
|
||||
|
||||
List<PracticeQuestion> _coursePracticeQuestions = [];
|
||||
List<Question> _coursePracticeQuestions = [];
|
||||
|
||||
List<PracticeQuestion> get coursePracticeQuestions =>
|
||||
_coursePracticeQuestions;
|
||||
List<Question> get coursePracticeQuestions => _coursePracticeQuestions;
|
||||
|
||||
int _currentQuestionIndex = 0;
|
||||
|
||||
|
|
@ -184,8 +182,8 @@ class CoursePracticeQuestionViewModel extends FormViewModel {
|
|||
_coursePracticeQuestions =
|
||||
await _apiService.getCoursePracticeQuestions(id);
|
||||
if (_coursePracticeQuestions.isNotEmpty) {
|
||||
_currentQuestion = await _apiService.getCoursePracticeQuestion(
|
||||
coursePracticeQuestions.first.questionId ?? 0);
|
||||
_currentQuestion = await _apiService
|
||||
.getCoursePracticeQuestion(coursePracticeQuestions.first.id ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// dart format width=80
|
||||
|
||||
// **************************************************************************
|
||||
// StackedFormGenerator
|
||||
|
|
@ -143,7 +144,7 @@ extension ValueProperties on FormStateHelper {
|
|||
}
|
||||
|
||||
extension Methods on FormStateHelper {
|
||||
setAssessmentValidationMessage(String? validationMessage) =>
|
||||
void setAssessmentValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[AssessmentValueKey] = validationMessage;
|
||||
|
||||
/// Clears text input fields on the Form
|
||||
|
|
@ -165,7 +166,7 @@ String? getValidationMessage(String key) {
|
|||
if (validatorForKey == null) return null;
|
||||
|
||||
String? validationMessageForKey = validatorForKey(
|
||||
_DuolingoViewTextEditingControllers[key]!.text,
|
||||
_DuolingoViewTextEditingControllers[key]?.text,
|
||||
);
|
||||
|
||||
return validationMessageForKey;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class DuolingoViewModel extends FormViewModel {
|
|||
Map<String, dynamic> _selectedAssessment = {
|
||||
'label': 'Speaking 01',
|
||||
'intro_title': 'Speak About the Photo',
|
||||
'type': DuolingoAssessmentType.speaking,
|
||||
'type': DuolingoAssessments.speaking,
|
||||
'outro_title': 'Speaking Practice Completed',
|
||||
'outro_subtitle': 'You’ve finished this speaking session. Great work!',
|
||||
'intro_subtitle':
|
||||
|
|
@ -40,7 +40,7 @@ class DuolingoViewModel extends FormViewModel {
|
|||
{
|
||||
'label': 'Speaking 01',
|
||||
'intro_title': 'Speak About the Photo',
|
||||
'type': DuolingoAssessmentType.speaking,
|
||||
'type': DuolingoAssessments.speaking,
|
||||
'outro_title': 'Speaking Practice Completed',
|
||||
'outro_subtitle': 'You’ve finished this speaking session. Great work!',
|
||||
'intro_subtitle':
|
||||
|
|
@ -49,7 +49,7 @@ class DuolingoViewModel extends FormViewModel {
|
|||
{
|
||||
'label': 'Speaking 02',
|
||||
'intro_title': 'Read, Then Speak',
|
||||
'type': DuolingoAssessmentType.speaking,
|
||||
'type': DuolingoAssessments.speaking,
|
||||
'outro_title': 'Speaking Practice Completed',
|
||||
'intro_subtitle': 'You will speak about the given topic',
|
||||
'outro_subtitle': 'You’ve finished this speaking session. Great work!',
|
||||
|
|
@ -57,7 +57,7 @@ class DuolingoViewModel extends FormViewModel {
|
|||
{
|
||||
'label': 'Speaking 03',
|
||||
'intro_title': 'Speaking Sample',
|
||||
'type': DuolingoAssessmentType.speaking,
|
||||
'type': DuolingoAssessments.speaking,
|
||||
'outro_title': 'Speaking Practice Completed',
|
||||
'intro_subtitle': 'You’ll speak for 1–3 minutes about a given topic.',
|
||||
'outro_subtitle': 'You’ve finished this speaking session. Great work!',
|
||||
|
|
@ -65,7 +65,7 @@ class DuolingoViewModel extends FormViewModel {
|
|||
{
|
||||
'label': 'Speaking 04',
|
||||
'intro_title': 'Interactive Speaking',
|
||||
'type': DuolingoAssessmentType.speaking,
|
||||
'type': DuolingoAssessments.speaking,
|
||||
'outro_title': 'Speaking Practice Completed',
|
||||
'intro_subtitle': ' You’ll answer a series of short questions.',
|
||||
'outro_subtitle': 'You’ve finished this speaking session. Great work!',
|
||||
|
|
@ -73,7 +73,7 @@ class DuolingoViewModel extends FormViewModel {
|
|||
{
|
||||
'label': 'Writing 05',
|
||||
'intro_title': 'Write About the Photo',
|
||||
'type': DuolingoAssessmentType.writing,
|
||||
'type': DuolingoAssessments.writing,
|
||||
'outro_title': 'Writing Practice Completed',
|
||||
'outro_subtitle': 'You’ve finished this writing session. Great work!',
|
||||
'intro_subtitle':
|
||||
|
|
@ -82,7 +82,7 @@ class DuolingoViewModel extends FormViewModel {
|
|||
{
|
||||
'label': 'Writing 06',
|
||||
'intro_title': 'Writing Sample',
|
||||
'type': DuolingoAssessmentType.writing,
|
||||
'type': DuolingoAssessments.writing,
|
||||
'outro_title': 'Writing Practice Completed',
|
||||
'outro_subtitle': 'You’ve finished this writing session. Great work!',
|
||||
'intro_subtitle':
|
||||
|
|
@ -90,7 +90,7 @@ class DuolingoViewModel extends FormViewModel {
|
|||
},
|
||||
{
|
||||
'label': 'Writing 07',
|
||||
'type': DuolingoAssessmentType.writing,
|
||||
'type': DuolingoAssessments.writing,
|
||||
'outro_title': 'Writing Practice Completed',
|
||||
'intro_title': 'Interactive Writing Part 1',
|
||||
'outro_subtitle': 'You’ve finished this writing session. Great work!',
|
||||
|
|
@ -99,7 +99,7 @@ class DuolingoViewModel extends FormViewModel {
|
|||
},
|
||||
{
|
||||
'label': 'Writing 08',
|
||||
'type': DuolingoAssessmentType.writing,
|
||||
'type': DuolingoAssessments.writing,
|
||||
'intro_title': 'Interactive Writing Part 2',
|
||||
'outro_title': 'Writing Practice Completed',
|
||||
'outro_subtitle': 'You’ve finished this writing session. Great work!',
|
||||
|
|
@ -109,7 +109,7 @@ class DuolingoViewModel extends FormViewModel {
|
|||
{
|
||||
'label': 'Listening 09',
|
||||
'intro_title': 'Listen and Type',
|
||||
'type': DuolingoAssessmentType.listening,
|
||||
'type': DuolingoAssessments.listening,
|
||||
'outro_title': 'Listening Practice Completed',
|
||||
'intro_subtitle':
|
||||
'You will hear a short audio clip. Type exactly what you hear.',
|
||||
|
|
@ -117,7 +117,7 @@ class DuolingoViewModel extends FormViewModel {
|
|||
},
|
||||
{
|
||||
'label': 'Listening 10',
|
||||
'type': DuolingoAssessmentType.listening,
|
||||
'type': DuolingoAssessments.listening,
|
||||
'outro_title': 'Listening Practice Completed',
|
||||
'intro_title': 'Interactive Listening - Part 1',
|
||||
'intro_subtitle': ' Listen carefully and complete the missing words.',
|
||||
|
|
@ -125,7 +125,7 @@ class DuolingoViewModel extends FormViewModel {
|
|||
},
|
||||
{
|
||||
'label': 'Listening 11',
|
||||
'type': DuolingoAssessmentType.listening,
|
||||
'type': DuolingoAssessments.listening,
|
||||
'outro_title': 'Listening Practice Completed',
|
||||
'intro_title': 'Interactive Listening - Part 2',
|
||||
'intro_subtitle': 'Listen and choose the correct option.',
|
||||
|
|
@ -133,7 +133,7 @@ class DuolingoViewModel extends FormViewModel {
|
|||
},
|
||||
{
|
||||
'label': 'Assessment 12',
|
||||
'type': DuolingoAssessmentType.listening,
|
||||
'type': DuolingoAssessments.listening,
|
||||
'title': 'Interactive Listening - Part 3',
|
||||
'outro_title': 'Listening Practice Completed',
|
||||
'subtitle': 'Write a summary of the conversation you just had',
|
||||
|
|
@ -142,14 +142,14 @@ class DuolingoViewModel extends FormViewModel {
|
|||
{
|
||||
'label': 'Reading 13',
|
||||
'intro_title': 'Read and Select',
|
||||
'type': DuolingoAssessmentType.reading,
|
||||
'type': DuolingoAssessments.reading,
|
||||
'intro_subtitle':
|
||||
'Read the sentence and select the option that correctly completes the meaning.'
|
||||
},
|
||||
{
|
||||
'label': 'Reading 14',
|
||||
'intro_title': 'Fill in the blank',
|
||||
'type': DuolingoAssessmentType.reading,
|
||||
'type': DuolingoAssessments.reading,
|
||||
'intro_subtitle': 'Complete the sentences by filling in the missing words'
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import '../../../widgets/wave_wrapper.dart';
|
|||
class DuolingoIntroScreen extends ViewModelWidget<DuolingoViewModel> {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final DuolingoAssessmentType type;
|
||||
final DuolingoAssessments type;
|
||||
|
||||
const DuolingoIntroScreen(
|
||||
{super.key,
|
||||
|
|
@ -22,11 +22,11 @@ class DuolingoIntroScreen extends ViewModelWidget<DuolingoViewModel> {
|
|||
required this.subtitle});
|
||||
|
||||
IconData _getIcon() {
|
||||
if (type == DuolingoAssessmentType.speaking) {
|
||||
if (type == DuolingoAssessments.speaking) {
|
||||
return Icons.waves;
|
||||
} else if (type == DuolingoAssessmentType.writing) {
|
||||
} else if (type == DuolingoAssessments.writing) {
|
||||
return Iconsax.pen_add;
|
||||
} else if (type == DuolingoAssessmentType.listening) {
|
||||
} else if (type == DuolingoAssessments.listening) {
|
||||
return Icons.hearing;
|
||||
} else {
|
||||
return Icons.book;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// dart format width=80
|
||||
|
||||
// **************************************************************************
|
||||
// StackedFormGenerator
|
||||
|
|
@ -230,13 +231,13 @@ extension ValueProperties on FormStateHelper {
|
|||
}
|
||||
|
||||
extension Methods on FormStateHelper {
|
||||
setPasswordValidationMessage(String? validationMessage) =>
|
||||
void setPasswordValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[PasswordValueKey] = validationMessage;
|
||||
setResetCodeValidationMessage(String? validationMessage) =>
|
||||
void setResetCodeValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[ResetCodeValueKey] = validationMessage;
|
||||
setEmailValidationMessage(String? validationMessage) =>
|
||||
void setEmailValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[EmailValueKey] = validationMessage;
|
||||
setConfirmPasswordValidationMessage(String? validationMessage) =>
|
||||
void setConfirmPasswordValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[ConfirmPasswordValueKey] =
|
||||
validationMessage;
|
||||
|
||||
|
|
@ -265,7 +266,7 @@ String? getValidationMessage(String key) {
|
|||
if (validatorForKey == null) return null;
|
||||
|
||||
String? validationMessageForKey = validatorForKey(
|
||||
_ForgetPasswordViewTextEditingControllers[key]!.text,
|
||||
_ForgetPasswordViewTextEditingControllers[key]?.text,
|
||||
);
|
||||
|
||||
return validationMessageForKey;
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ import 'package:stacked/stacked.dart';
|
|||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
import 'package:yimaru_app/ui/views/course_category/course_category_view.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_subcategory/learn_subcategory_view.dart';
|
||||
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/startup/startup_view.dart';
|
||||
import 'package:yimaru_app/ui/widgets/coming_soon.dart';
|
||||
|
||||
import 'home_viewmodel.dart';
|
||||
|
||||
|
|
@ -18,7 +19,6 @@ class HomeView extends StackedView<HomeViewModel> {
|
|||
@override
|
||||
void onViewModelReady(HomeViewModel viewModel) async {
|
||||
// Removable
|
||||
print('HERE');
|
||||
await _init(viewModel);
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
|
@ -83,9 +83,9 @@ Widget _buildProfileIcon() => const Icon(Icons.person);
|
|||
Widget getViewForIndex(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return const LearnSubcategoryView();
|
||||
return const LearnProgramView();
|
||||
case 1:
|
||||
return const CourseCategoryView();
|
||||
return const ComingSoon();
|
||||
|
||||
default:
|
||||
return const ProfileView();
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/widgets/learn_tile.dart';
|
||||
|
||||
import '../../../models/course.dart';
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/enmus.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||
import '../../widgets/small_app_bar.dart';
|
||||
import 'learn_viewmodel.dart';
|
||||
|
||||
class LearnView extends StackedView<LearnViewModel> {
|
||||
final int id;
|
||||
|
||||
const LearnView({Key? key, required this.id}) : super(key: key);
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnViewModel viewModel) async {
|
||||
await viewModel.getCourses(id);
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
||||
@override
|
||||
LearnViewModel viewModelBuilder(BuildContext context) => LearnViewModel();
|
||||
|
||||
@override
|
||||
Widget builder(
|
||||
BuildContext context,
|
||||
LearnViewModel viewModel,
|
||||
Widget? child,
|
||||
) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(LearnViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(LearnViewModel viewModel) =>
|
||||
SafeArea(child: _buildBody(viewModel));
|
||||
|
||||
Widget _buildBody(LearnViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn(LearnViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildLearnColumnWrapper(viewModel)
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(LearnViewModel viewModel) => SmallAppBar(
|
||||
onTap: viewModel.pop,
|
||||
showBackButton: true,
|
||||
);
|
||||
|
||||
Widget _buildLearnColumnWrapper(LearnViewModel viewModel) =>
|
||||
Expanded(child: _buildLearnColumnScrollView(viewModel));
|
||||
|
||||
Widget _buildLearnColumnScrollView(LearnViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildListViewBuilder(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildListViewBuilder(LearnViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.learnCourses)
|
||||
? _buildProgressIndicator()
|
||||
: _buildListView(viewModel);
|
||||
|
||||
Widget _buildProgressIndicator() => const Center(
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
Widget _buildListView(LearnViewModel viewModel) => ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.courses.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
separatorBuilder: (context, index) => verticalSpaceSmall,
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
course: viewModel.courses[index],
|
||||
onTap: () async => await viewModel
|
||||
.navigateToLearnLevel(viewModel.courses[index].id ?? 0),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildTile({
|
||||
required Course course,
|
||||
required GestureTapCallback onTap,
|
||||
}) =>
|
||||
LearnTile(
|
||||
onTap: onTap,
|
||||
course: course,
|
||||
);
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/app/app.router.dart';
|
||||
import 'package:yimaru_app/models/course.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
|
||||
class LearnViewModel extends BaseViewModel {
|
||||
// Dependency injection
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
final _statusChecker = locator<StatusCheckerService>();
|
||||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
// Learn courses
|
||||
List<Course> _courses = [];
|
||||
|
||||
List<Course> get courses => _courses;
|
||||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> navigateToLearnLevel(int id) async =>
|
||||
_navigationService.navigateToLearnLevelView(id: id);
|
||||
|
||||
// Remote api call
|
||||
|
||||
// Learn courses
|
||||
Future<void> getCourses(int id) async => await runBusyFuture(_getCourses(id),
|
||||
busyObject: StateObjects.learnCourses);
|
||||
|
||||
Future<void> _getCourses(int id) async {
|
||||
if (_courses.isEmpty) {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
_courses = await _apiService.getCourses(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
103
lib/ui/views/learn_course/learn_course_view.dart
Normal file
103
lib/ui/views/learn_course/learn_course_view.dart
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/learn_course.dart';
|
||||
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/enmus.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||
import '../../widgets/learn_course_tile.dart';
|
||||
import '../../widgets/small_app_bar.dart';
|
||||
import 'learn_course_viewmodel.dart';
|
||||
|
||||
class LearnCourseView extends StackedView<LearnCourseViewModel> {
|
||||
final int id;
|
||||
const LearnCourseView({Key? key, required this.id}) : super(key: key);
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnCourseViewModel viewModel) async {
|
||||
await viewModel.getLearnCourses(id);
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
||||
@override
|
||||
LearnCourseViewModel viewModelBuilder(BuildContext context) =>
|
||||
LearnCourseViewModel();
|
||||
|
||||
@override
|
||||
Widget builder(
|
||||
BuildContext context,
|
||||
LearnCourseViewModel viewModel,
|
||||
Widget? child,
|
||||
) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(LearnCourseViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(LearnCourseViewModel viewModel) =>
|
||||
SafeArea(child: _buildBody(viewModel));
|
||||
|
||||
Widget _buildBody(LearnCourseViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn(LearnCourseViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildCoursesColumnWrapper(viewModel)
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(LearnCourseViewModel viewModel) => SmallAppBar(
|
||||
onTap: viewModel.pop,
|
||||
showBackButton: true,
|
||||
);
|
||||
|
||||
Widget _buildCoursesColumnWrapper(LearnCourseViewModel viewModel) =>
|
||||
Expanded(child: _buildLevelsColumnScrollView(viewModel));
|
||||
|
||||
Widget _buildLevelsColumnScrollView(LearnCourseViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildListViewBuilder(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildListViewBuilder(LearnCourseViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.learnCourses)
|
||||
? _buildProgressIndicator()
|
||||
: _buildListView(viewModel);
|
||||
|
||||
Widget _buildProgressIndicator() => const Center(
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
Widget _buildListView(LearnCourseViewModel viewModel) => ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.learnCourses.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
course: viewModel.learnCourses[index],
|
||||
onViewTap: () async => await viewModel
|
||||
.navigateToLearnModule(viewModel.learnCourses[index]),
|
||||
onPracticeTap: () async => await viewModel
|
||||
.navigateToLearnPractice(viewModel.learnCourses[index].id ?? 0),
|
||||
),
|
||||
separatorBuilder: (context, index) => verticalSpaceSmall,
|
||||
);
|
||||
|
||||
Widget _buildTile({
|
||||
required LearnCourse course,
|
||||
required GestureTapCallback onViewTap,
|
||||
required GestureTapCallback onPracticeTap,
|
||||
}) =>
|
||||
LearnCourseTile(
|
||||
course: course,
|
||||
onViewTap: onViewTap,
|
||||
onPracticeTap: onPracticeTap,
|
||||
);
|
||||
}
|
||||
49
lib/ui/views/learn_course/learn_course_viewmodel.dart
Normal file
49
lib/ui/views/learn_course/learn_course_viewmodel.dart
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/app/app.router.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../models/learn_course.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/enmus.dart';
|
||||
|
||||
class LearnCourseViewModel extends BaseViewModel {
|
||||
// Dependency injection
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
final _statusChecker = locator<StatusCheckerService>();
|
||||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
// Learn courses
|
||||
List<LearnCourse> _learnCourses = [];
|
||||
|
||||
List<LearnCourse> get learnCourses => _learnCourses;
|
||||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> navigateToLearnModule(LearnCourse course) async =>
|
||||
_navigationService.navigateToLearnModuleView(course: course);
|
||||
|
||||
Future<void> navigateToLearnPractice(int id) async => await _navigationService
|
||||
.navigateToLearnPracticeView(id: id, practice: LearnPractices.course);
|
||||
|
||||
// Remote api call
|
||||
|
||||
// Learn courses
|
||||
Future<void> getLearnCourses(int id) async =>
|
||||
await runBusyFuture(_getLearnCourses(id),
|
||||
busyObject: StateObjects.learnCourses);
|
||||
|
||||
Future<void> _getLearnCourses(int id) async {
|
||||
if (_learnCourses.isEmpty) {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
_learnCourses = await _apiService.getLearnCourse(id);
|
||||
_learnCourses
|
||||
.sort((a, b) => (a.sortOrder ?? 0).compareTo(b.sortOrder ?? 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/submodule.dart';
|
||||
import 'package:yimaru_app/models/learn_lesson.dart';
|
||||
import 'package:yimaru_app/models/learn_module.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
import 'package:yimaru_app/ui/widgets/learn_lesson_tile.dart';
|
||||
import 'package:yimaru_app/ui/widgets/module_progress.dart';
|
||||
import 'package:yimaru_app/ui/widgets/motivation_card.dart';
|
||||
|
||||
import '../../../models/lesson.dart';
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||
|
|
@ -14,18 +14,16 @@ import '../../widgets/small_app_bar.dart';
|
|||
import 'learn_lesson_viewmodel.dart';
|
||||
|
||||
class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
||||
final Submodule submodule;
|
||||
|
||||
const LearnLessonView({Key? key, required this.submodule}) : super(key: key);
|
||||
final LearnModule module;
|
||||
|
||||
const LearnLessonView({Key? key, required this.module}) : super(key: key);
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnLessonViewModel viewModel) async {
|
||||
await viewModel.getLessons(submodule.id ?? 0);
|
||||
await viewModel.getLessons(module.id ?? 0);
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
||||
|
||||
Widget getPadding(context) {
|
||||
double half = screenHeight(context) / 2;
|
||||
return SizedBox(
|
||||
|
|
@ -116,10 +114,8 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
|||
verticalSpaceTiny,
|
||||
_buildSubtitle(),
|
||||
verticalSpaceSmall,
|
||||
verticalSpaceSmall,
|
||||
_buildModuleProgress(),
|
||||
verticalSpaceMedium,
|
||||
verticalSpaceMedium,
|
||||
_buildMotivationCard(),
|
||||
verticalSpaceMedium,
|
||||
_buildHeader(),
|
||||
|
|
@ -128,13 +124,13 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
|||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
submodule.title ?? '',
|
||||
module.name ?? '',
|
||||
style: style16DG600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
submodule.description ?? '',
|
||||
style: style14DG600,
|
||||
module.description ?? '',
|
||||
style: style14DG500,
|
||||
);
|
||||
|
||||
Widget _buildModuleProgress() => const ModuleProgress();
|
||||
|
|
@ -146,16 +142,14 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
|||
style: style18DG700,
|
||||
);
|
||||
|
||||
|
||||
Widget _buildListViewBuilder(LearnLessonViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.learnLessons)
|
||||
? _buildProgressIndicator()
|
||||
: _buildListView(viewModel);
|
||||
|
||||
Widget _buildProgressIndicator() => const Center(
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
Widget _buildListView(LearnLessonViewModel viewModel) => ListView.builder(
|
||||
shrinkWrap: true,
|
||||
|
|
@ -163,16 +157,21 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
|||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
lesson: viewModel.lessons[index],
|
||||
onLessonTap: () async => await viewModel.navigateToLearnLessonDetail(viewModel.lessons[index]),
|
||||
onLessonTap: () async => await viewModel
|
||||
.navigateToLearnLessonDetail(viewModel.lessons[index]),
|
||||
onPracticeTap: () async => await viewModel
|
||||
.navigateToLearnPractice(viewModel.lessons[index].id ?? 0),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildTile({
|
||||
required Lesson lesson,
|
||||
required LearnLesson lesson,
|
||||
required GestureTapCallback? onLessonTap,
|
||||
required GestureTapCallback? onPracticeTap,
|
||||
}) =>
|
||||
LearnLessonTile(
|
||||
lesson: lesson,
|
||||
onLessonTap: onLessonTap,
|
||||
onPracticeTap: onPracticeTap,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/app/app.router.dart';
|
||||
import 'package:yimaru_app/models/learn_lesson.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../models/lesson.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
|
||||
|
|
@ -17,28 +17,30 @@ class LearnLessonViewModel extends BaseViewModel {
|
|||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
// Learn lessons
|
||||
List<Lesson> _lessons = [];
|
||||
List<LearnLesson> _lessons = [];
|
||||
|
||||
List<Lesson> get lessons => _lessons;
|
||||
List<LearnLesson> get lessons => _lessons;
|
||||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> navigateToLearnLessonDetail(Lesson lesson) async =>
|
||||
Future<void> navigateToLearnPractice(int id) async => await _navigationService
|
||||
.navigateToLearnPracticeView(id: id, practice: LearnPractices.lesson);
|
||||
|
||||
Future<void> navigateToLearnLessonDetail(LearnLesson lesson) async =>
|
||||
await _navigationService.navigateToLearnLessonDetailView(lesson: lesson);
|
||||
|
||||
// Remote api call
|
||||
|
||||
// Learn modules
|
||||
// Learn lessons
|
||||
Future<void> getLessons(int id) async => await runBusyFuture(_getLessons(id),
|
||||
busyObject: StateObjects.learnLessons);
|
||||
|
||||
Future<void> _getLessons(int id) async {
|
||||
if (_lessons.isEmpty) {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
_lessons = await _apiService.getLessons(id);
|
||||
_lessons.sort(
|
||||
(a, b) => (a.displayOrder ?? 0).compareTo(b.displayOrder ?? 0));
|
||||
_lessons = await _apiService.getLearnLessons(id);
|
||||
_lessons.sort((a, b) => (a.sortOrder ?? 0).compareTo(b.sortOrder ?? 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,27 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:vimeo_video_player/vimeo_video_player.dart';
|
||||
import 'package:yimaru_app/ui/widgets/empty_video_player.dart';
|
||||
import 'package:yimaru_app/models/learn_lesson.dart';
|
||||
|
||||
import '../../../models/lesson.dart';
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/enmus.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_elevated_button.dart';
|
||||
import '../../widgets/small_app_bar.dart';
|
||||
import 'learn_lesson_detail_viewmodel.dart';
|
||||
|
||||
class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
||||
final Lesson lesson;
|
||||
final LearnLesson lesson;
|
||||
|
||||
const LearnLessonDetailView({Key? key, required this.lesson})
|
||||
: super(key: key);
|
||||
|
||||
Future<void> _navigate(LearnLessonDetailViewModel viewModel) async {
|
||||
await viewModel.pause();
|
||||
// await viewModel.navigateToLearnPractice(practices);
|
||||
await viewModel.navigateToLearnPractice();
|
||||
}
|
||||
|
||||
@override
|
||||
void onDispose(LearnLessonDetailViewModel viewModel) {
|
||||
print('DISPOSED');
|
||||
viewModel.close();
|
||||
super.onDispose(viewModel);
|
||||
}
|
||||
|
|
@ -126,8 +123,6 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
|||
child: _buildVideoPlayer(viewModel),
|
||||
);
|
||||
|
||||
|
||||
|
||||
Widget _buildVideoPlayer(LearnLessonDetailViewModel viewModel) =>
|
||||
_buildVimeoPlayer(viewModel);
|
||||
|
||||
|
|
@ -136,11 +131,9 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
|||
isAutoPlay: true,
|
||||
onInAppWebViewCreated: (controller) =>
|
||||
viewModel.initializePlayer(controller),
|
||||
videoId: lesson.teachingVideoUrl?.split('/').last ?? '',
|
||||
videoId: lesson.videoUrl?.split('/').last ?? '',
|
||||
);
|
||||
|
||||
Widget _buildEmptyVideoPlayer() => const EmptyVideoPlayer();
|
||||
|
||||
Widget _buildDescriptionWrapper() => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildDescription(),
|
||||
|
|
@ -148,7 +141,7 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
|||
|
||||
Widget _buildDescription() => Text(
|
||||
lesson.description ?? '',
|
||||
style: style14DG600,
|
||||
style: style14DG400,
|
||||
);
|
||||
|
||||
Widget _buildContinueButtonWrapper(LearnLessonDetailViewModel viewModel) =>
|
||||
|
|
@ -164,8 +157,8 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
|||
Widget _buildContinueButton(LearnLessonDetailViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Lessons',
|
||||
borderRadius: 12,
|
||||
text: 'Practices',
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
onTap: () async => await _navigate(viewModel),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import 'package:chewie/chewie.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
import 'package:yimaru_app/app/app.router.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
|
||||
|
|
@ -10,34 +8,26 @@ import '../../../app/app.locator.dart';
|
|||
import '../../../services/status_checker_service.dart';
|
||||
|
||||
class LearnLessonDetailViewModel extends BaseViewModel {
|
||||
// Dependency injection
|
||||
final _statusChecker = locator<StatusCheckerService>();
|
||||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
// Video player config
|
||||
ChewieController? _chewieController;
|
||||
|
||||
ChewieController? get chewieController => _chewieController;
|
||||
|
||||
InAppWebViewController? _webViewController;
|
||||
|
||||
InAppWebViewController? get webViewController => _webViewController;
|
||||
|
||||
VideoPlayerController? _videoPlayerController;
|
||||
|
||||
VideoPlayerController? get videoPlayerController => _videoPlayerController;
|
||||
|
||||
// Video player
|
||||
void close() {
|
||||
_videoPlayerController?.dispose();
|
||||
_chewieController?.dispose();
|
||||
webViewController?.dispose();
|
||||
}
|
||||
|
||||
Future<void> pause() async {
|
||||
await _chewieController?.pause();
|
||||
await webViewController?.pause();
|
||||
}
|
||||
|
||||
void initializePlayer(InAppWebViewController controller){
|
||||
void initializePlayer(InAppWebViewController controller) {
|
||||
_webViewController = controller;
|
||||
rebuildUi();
|
||||
}
|
||||
|
|
@ -52,17 +42,9 @@ class LearnLessonDetailViewModel extends BaseViewModel {
|
|||
rebuildUi();
|
||||
}
|
||||
|
||||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> navigateToLearnPractice(
|
||||
List<Map<String, dynamic>> practices) async =>
|
||||
await _navigationService.navigateToLearnPracticeView(
|
||||
practices: practices,
|
||||
buttonLabel: 'Start Practice',
|
||||
title: 'Let \'s practice what you just learnt!',
|
||||
subtitle:
|
||||
'I’ll ask you a few questions, and you can respond naturally.',
|
||||
);
|
||||
Future<void> navigateToLearnPractice() async {}
|
||||
// await _navigationService.navigateToLearnPracticeView();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,100 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/widgets/learn_level_tile.dart';
|
||||
import 'package:yimaru_app/ui/widgets/small_app_bar.dart';
|
||||
|
||||
import '../../../models/level.dart';
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/enmus.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||
import 'learn_level_viewmodel.dart';
|
||||
|
||||
class LearnLevelView extends StackedView<LearnLevelViewModel> {
|
||||
final int id;
|
||||
const LearnLevelView({Key? key, required this.id}) : super(key: key);
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnLevelViewModel viewModel) async {
|
||||
await viewModel.getLevels(id);
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
||||
@override
|
||||
LearnLevelViewModel viewModelBuilder(BuildContext context) =>
|
||||
LearnLevelViewModel();
|
||||
|
||||
@override
|
||||
Widget builder(
|
||||
BuildContext context,
|
||||
LearnLevelViewModel viewModel,
|
||||
Widget? child,
|
||||
) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(LearnLevelViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(LearnLevelViewModel viewModel) =>
|
||||
SafeArea(child: _buildBody(viewModel));
|
||||
|
||||
Widget _buildBody(LearnLevelViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn(LearnLevelViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildLevelsColumnWrapper(viewModel)
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(LearnLevelViewModel viewModel) => SmallAppBar(
|
||||
title: 'Levels',
|
||||
onTap: viewModel.pop,
|
||||
showBackButton: true,
|
||||
);
|
||||
|
||||
Widget _buildLevelsColumnWrapper(LearnLevelViewModel viewModel) =>
|
||||
Expanded(child: _buildLevelsColumnScrollView(viewModel));
|
||||
|
||||
Widget _buildLevelsColumnScrollView(LearnLevelViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildListViewBuilder(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildListViewBuilder(LearnLevelViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.learnLevels)
|
||||
? _buildProgressIndicator()
|
||||
: _buildListView(viewModel);
|
||||
|
||||
Widget _buildProgressIndicator() => const Center(
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
Widget _buildListView(LearnLevelViewModel viewModel) => ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.levels.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
level: viewModel.levels[index],
|
||||
onTap: () async =>
|
||||
await viewModel.navigateToModule(viewModel.levels[index]),
|
||||
),
|
||||
separatorBuilder: (context, index) => verticalSpaceSmall,
|
||||
);
|
||||
|
||||
Widget _buildTile({
|
||||
required Level level,
|
||||
required GestureTapCallback onTap,
|
||||
}) =>
|
||||
LearnLevelTile(
|
||||
onTap: onTap,
|
||||
level: level,
|
||||
);
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/app/app.router.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../models/level.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/enmus.dart';
|
||||
|
||||
class LearnLevelViewModel extends BaseViewModel {
|
||||
// Dependency injection
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
final _statusChecker = locator<StatusCheckerService>();
|
||||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
// Learn levels
|
||||
List<Level> _levels = [];
|
||||
|
||||
List<Level> get levels => _levels;
|
||||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> navigateToModule(Level level) async =>
|
||||
_navigationService.navigateToLearnModuleView(level: level);
|
||||
|
||||
// Remote api call
|
||||
|
||||
// Learn levels
|
||||
Future<void> getLevels(int id) async =>
|
||||
await runBusyFuture(_getLevels(id), busyObject: StateObjects.learnLevels);
|
||||
|
||||
Future<void> _getLevels(int id) async {
|
||||
if (_levels.isEmpty) {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
_levels = await _apiService.getLevels(id);
|
||||
_levels.sort(
|
||||
(a, b) => (a.displayOrder ?? 0).compareTo(b.displayOrder ?? 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/level.dart';
|
||||
import 'package:yimaru_app/models/learn_module.dart';
|
||||
import 'package:yimaru_app/ui/widgets/learn_module_tile.dart';
|
||||
import 'package:yimaru_app/ui/widgets/overall_learn_progress.dart';
|
||||
|
||||
import '../../../models/module.dart';
|
||||
import '../../../models/learn_course.dart';
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/enmus.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
|
|
@ -13,13 +13,13 @@ import '../../widgets/small_app_bar.dart';
|
|||
import 'learn_module_viewmodel.dart';
|
||||
|
||||
class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
||||
final Level level;
|
||||
final LearnCourse course;
|
||||
|
||||
const LearnModuleView({Key? key, required this.level}) : super(key: key);
|
||||
const LearnModuleView({Key? key, required this.course}) : super(key: key);
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnModuleViewModel viewModel) async {
|
||||
await viewModel.getModules(level.id ?? 0);
|
||||
await viewModel.getLearnModules(1);
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +58,6 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildAppBar(LearnModuleViewModel viewModel) => SmallAppBar(
|
||||
title: 'Modules',
|
||||
onTap: viewModel.pop,
|
||||
showBackButton: true,
|
||||
);
|
||||
|
|
@ -78,27 +77,28 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
|||
);
|
||||
|
||||
List<Widget> _buildLevelsColumnChildren(LearnModuleViewModel viewModel) => [
|
||||
verticalSpaceMedium,
|
||||
verticalSpaceSmall,
|
||||
_buildTitle(),
|
||||
_buildSubtitle(),
|
||||
verticalSpaceLarge,
|
||||
verticalSpaceMedium,
|
||||
_buildOverallProgress(),
|
||||
verticalSpaceMedium,
|
||||
_buildListViewBuilder(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
level.title ?? '',
|
||||
course.name ?? '',
|
||||
style: style18P600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
'Your Current Level',
|
||||
style: style14DG400,
|
||||
style: style14P400,
|
||||
);
|
||||
|
||||
Widget _buildOverallProgress() => OverallLearnProgress(
|
||||
color: kcPrimaryColor.withOpacity(0.1),
|
||||
indicatorBackgroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
);
|
||||
|
||||
Widget _buildListViewBuilder(LearnModuleViewModel viewModel) =>
|
||||
|
|
@ -116,17 +116,20 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
|||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
module: viewModel.modules[index],
|
||||
onModuleTap: () async => await viewModel
|
||||
.navigateToLearnSubmodule(viewModel.modules[index]),
|
||||
onModuleTap: () async =>
|
||||
await viewModel.navigateToLearnLesson(viewModel.modules[index]),
|
||||
onPracticeTap: () async => await viewModel
|
||||
.navigateToLearnPractice(viewModel.modules[index].id ?? 0),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildTile({
|
||||
required Module module,
|
||||
required LearnModule module,
|
||||
required GestureTapCallback onModuleTap,
|
||||
required GestureTapCallback onPracticeTap,
|
||||
}) =>
|
||||
LearnModuleTile(
|
||||
module: module,
|
||||
onModuleTap: onModuleTap,
|
||||
);
|
||||
module: module,
|
||||
onModuleTap: onModuleTap,
|
||||
onPracticeTap: onPracticeTap);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/app/app.router.dart';
|
||||
import 'package:yimaru_app/models/module.dart';
|
||||
import 'package:yimaru_app/models/learn_module.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
|
|
@ -17,28 +17,31 @@ class LearnModuleViewModel extends BaseViewModel {
|
|||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
// Learn module
|
||||
List<Module> _modules = [];
|
||||
List<LearnModule> _modules = [];
|
||||
|
||||
List<Module> get modules => _modules;
|
||||
List<LearnModule> get modules => _modules;
|
||||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> navigateToLearnSubmodule(Module module) async =>
|
||||
await _navigationService.navigateToLearnSubmoduleView(module: module);
|
||||
Future<void> navigateToLearnLesson(LearnModule module) async =>
|
||||
await _navigationService.navigateToLearnLessonView(module: module);
|
||||
|
||||
Future<void> navigateToLearnPractice(int id) async => await _navigationService
|
||||
.navigateToLearnPracticeView(id: id, practice: LearnPractices.module);
|
||||
|
||||
// Remote api call
|
||||
|
||||
// Learn modules
|
||||
Future<void> getModules(int id) async => await runBusyFuture(_getModules(id),
|
||||
busyObject: StateObjects.learnModules);
|
||||
Future<void> getLearnModules(int id) async =>
|
||||
await runBusyFuture(_getLearnModules(id),
|
||||
busyObject: StateObjects.learnModules);
|
||||
|
||||
Future<void> _getModules(int id) async {
|
||||
Future<void> _getLearnModules(int id) async {
|
||||
if (_modules.isEmpty) {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
_modules = await _apiService.getModules(id);
|
||||
_modules.sort(
|
||||
(a, b) => (a.displayOrder ?? 0).compareTo(b.displayOrder ?? 0));
|
||||
_modules = await _apiService.getLearnModules(id);
|
||||
_modules.sort((a, b) => (a.sortOrder ?? 0).compareTo(b.sortOrder ?? 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +1,30 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_practice/screens/finish_learn_practice_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_completion_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_result_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practices_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_questions_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_practice/screens/interact_learn_practice_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_intro_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_practice/screens/start_learn_practice_screen.dart';
|
||||
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
|
||||
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../widgets/cancel_learn_practice_sheet.dart';
|
||||
import 'learn_practice_viewmodel.dart';
|
||||
|
||||
class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final String buttonLabel;
|
||||
final List<Map<String, dynamic>> practices;
|
||||
final int id;
|
||||
final LearnPractices practice;
|
||||
|
||||
const LearnPracticeView(
|
||||
{Key? key,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.practices,
|
||||
required this.buttonLabel})
|
||||
const LearnPracticeView({Key? key, required this.id, required this.practice})
|
||||
: super(key: key);
|
||||
|
||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||
viewModel.pop();
|
||||
viewModel.goTo(0);
|
||||
viewModel.stopRecording();
|
||||
}
|
||||
|
||||
Future<void> _pop(
|
||||
{required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) async {
|
||||
{
|
||||
if (viewModel.currentPage == 3) {
|
||||
await _showSheet(context: context, viewModel: viewModel);
|
||||
} else {
|
||||
viewModel.goBack();
|
||||
}
|
||||
}
|
||||
viewModel.pop();
|
||||
}
|
||||
|
||||
Future<void> _showSheet(
|
||||
|
|
@ -54,6 +37,12 @@ class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
|||
builder: (_) => _buildSheet(viewModel),
|
||||
);
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnPracticeViewModel viewModel) async {
|
||||
await viewModel.getLearnPractices(id: id, practice: practice);
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
||||
@override
|
||||
LearnPracticeViewModel viewModelBuilder(BuildContext context) =>
|
||||
LearnPracticeViewModel();
|
||||
|
|
@ -70,50 +59,45 @@ class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
|||
{required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) =>
|
||||
PopScope(
|
||||
canPop: viewModel.currentPage == 0 ? true : false,
|
||||
onPopInvokedWithResult: (value, data) async =>
|
||||
await _pop(context: context, viewModel: viewModel),
|
||||
canPop: false,
|
||||
onPopInvokedWithResult: (value, data)
|
||||
async=>await _showSheet(context: context, viewModel: viewModel),
|
||||
child: _buildScaffoldWrapper(viewModel));
|
||||
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
||||
CancelLearnPracticeSheet(
|
||||
onClose: viewModel.pop,
|
||||
onContinue: viewModel.pop,
|
||||
user: viewModel.user?.firstName ?? '',
|
||||
onCancel: () async => await _cancel(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffoldWrapper(LearnPracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildBody(viewModel),
|
||||
body: _buildBodyState(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBody(LearnPracticeViewModel viewModel) =>
|
||||
IndexedStack(index: viewModel.currentPage, children: _buildScreens());
|
||||
Widget _buildBodyState(LearnPracticeViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.learnPractices)
|
||||
? const PageLoadingIndicator()
|
||||
: _buildBody(viewModel);
|
||||
|
||||
List<Widget> _buildScreens() => [
|
||||
_buildLearnPracticesScreen(),
|
||||
_buildLearnPracticeIntroScreen(),
|
||||
_buildStartLearnPracticeScreen(),
|
||||
_buildInteractLearnPracticeScreen(),
|
||||
Widget _buildBody(LearnPracticeViewModel viewModel) => IndexedStack(
|
||||
index: viewModel.currentPage, children: _buildScreens(viewModel));
|
||||
|
||||
List<Widget> _buildScreens(LearnPracticeViewModel viewModel) => [
|
||||
_buildLearnPracticeIntroScreen(viewModel),
|
||||
_buildLearnPracticeQuestionsScreen(viewModel),
|
||||
_buildFinishLearnPracticeScreen(),
|
||||
_buildLearnPracticeResultScreen(),
|
||||
_buildLearnPracticeCompletionScreen()
|
||||
];
|
||||
|
||||
Widget _buildLearnPracticesScreen() => LearnPracticesScreen(
|
||||
practices: practices,
|
||||
);
|
||||
Widget _buildLearnPracticeIntroScreen(LearnPracticeViewModel viewModel) =>
|
||||
const LearnPracticeIntroScreen();
|
||||
|
||||
Widget _buildLearnPracticeIntroScreen() => LearnPracticeIntroScreen(
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
buttonLabel: buttonLabel,
|
||||
);
|
||||
|
||||
Widget _buildStartLearnPracticeScreen() => const StartLearnPracticeScreen();
|
||||
|
||||
Widget _buildInteractLearnPracticeScreen() =>
|
||||
const InteractLearnPracticeScreen();
|
||||
Widget _buildLearnPracticeQuestionsScreen(LearnPracticeViewModel viewModel) =>
|
||||
const LearnPracticeQuestionsScreen();
|
||||
|
||||
Widget _buildFinishLearnPracticeScreen() => const FinishLearnPracticeScreen();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,30 @@
|
|||
import 'package:audioplayers/audioplayers.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:waveform_recorder/waveform_recorder.dart';
|
||||
import 'package:yimaru_app/models/learn_practice.dart';
|
||||
import 'package:yimaru_app/models/user.dart';
|
||||
import 'package:yimaru_app/services/authentication_service.dart';
|
||||
import 'package:yimaru_app/services/voice_recorder_service.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../models/learn_question.dart';
|
||||
import '../../../models/question.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/audio_player_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/app_colors.dart';
|
||||
|
||||
class LearnPracticeViewModel extends ReactiveViewModel {
|
||||
// Dependency injection
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
final _dialogService = locator<DialogService>();
|
||||
|
||||
final _statusChecker = locator<StatusCheckerService>();
|
||||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
final _audioPlayerService = locator<AudioPlayerService>();
|
||||
|
|
@ -67,23 +77,48 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
|||
VoiceRecordingState get recordingState => _recordingState;
|
||||
|
||||
// Busy object
|
||||
StateObjects _busyObject = StateObjects.none;
|
||||
String? _busyObject;
|
||||
|
||||
StateObjects get busyObject => _busyObject;
|
||||
String? get busyObject => _busyObject;
|
||||
|
||||
Voice? _playing;
|
||||
|
||||
Voice? get playing =>_playing;
|
||||
|
||||
// Learn practices
|
||||
List<LearnPractice> _practices = [];
|
||||
|
||||
List<LearnPractice> get practices => _practices;
|
||||
|
||||
// Practice questions
|
||||
List<LearnQuestion> _questions = [];
|
||||
|
||||
List<LearnQuestion> get questions => _questions;
|
||||
|
||||
// Practice answers
|
||||
List<Map<String, dynamic>> _answers = [];
|
||||
|
||||
List<Map<String, dynamic>> get answers => _answers;
|
||||
|
||||
// In-app navigation
|
||||
int _currentPage = 0;
|
||||
|
||||
int get currentPage => _currentPage;
|
||||
|
||||
// Practice
|
||||
Map<String, dynamic> _selectedPractice = {};
|
||||
final PageController _questionSetController = PageController();
|
||||
|
||||
Map<String, dynamic> get selectedPractice => _selectedPractice;
|
||||
PageController get questionSetController => _questionSetController;
|
||||
|
||||
final PageController _questionController = PageController();
|
||||
|
||||
PageController get questionController => _questionController;
|
||||
|
||||
// Voice recorder
|
||||
Future<void> stopRecording() async =>
|
||||
Future<void> stopRecording() async {
|
||||
if (_voiceRecorderService.waveController.isRecording) {
|
||||
await _voiceRecorderService.stopRecording();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> startRecording() async => await runBusyFuture(_startRecording(),
|
||||
busyObject: StateObjects.recordLearnPracticeAnswer);
|
||||
|
|
@ -91,24 +126,6 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
|||
Future<void> _startRecording() async =>
|
||||
await _voiceRecorderService.startRecording();
|
||||
|
||||
// Sample audio
|
||||
Future<void> playSampleAudio() async =>
|
||||
await runBusyFuture(_playSampleAudio(),
|
||||
busyObject: StateObjects.learnPracticeSample);
|
||||
|
||||
Future<void> _playSampleAudio() async {
|
||||
setBusyObject(StateObjects.learnPracticeSample);
|
||||
await _audioPlayerService.playUrl(_selectedPractice['sample_answer']);
|
||||
}
|
||||
|
||||
Future<void> pauseSampleAudio() async =>
|
||||
await runBusyFuture(_pauseSampleAudio(),
|
||||
busyObject: StateObjects.learnPracticeSample);
|
||||
|
||||
Future<void> _pauseSampleAudio() async {
|
||||
setBusyObject(StateObjects.learnPracticeSample);
|
||||
await _audioPlayerService.pause();
|
||||
}
|
||||
|
||||
// Play practice audio
|
||||
void _listenToAudio() {
|
||||
|
|
@ -125,46 +142,53 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
|||
});
|
||||
}
|
||||
|
||||
Future<void> playQuestionAudio() async =>
|
||||
await runBusyFuture(_playQuestionAudio(),
|
||||
Future<void> playVoicePrompt(LearnQuestion question) async =>
|
||||
await runBusyFuture(_playVoicePrompt(question),
|
||||
busyObject: StateObjects.learnPracticeQuestion);
|
||||
|
||||
Future<void> _playQuestionAudio() async {
|
||||
goTo(3);
|
||||
await _audioPlayerService.playUrl(_selectedPractice['question_audio_url']);
|
||||
Future<void> _playVoicePrompt(LearnQuestion question) async {
|
||||
_questionController.jumpToPage(1);
|
||||
await _audioPlayerService.playUrl(question.voicePrompt ?? '');
|
||||
}
|
||||
|
||||
// Recorded audio
|
||||
Future<void> playRecordedAudio() async =>
|
||||
await runBusyFuture(_playRecordedAudio(),
|
||||
busyObject: StateObjects.learnPracticeAnswer);
|
||||
|
||||
Future<void> _playRecordedAudio() async {
|
||||
setBusyObject(StateObjects.learnPracticeAnswer);
|
||||
await _audioPlayerService
|
||||
.playLocal(await _voiceRecorderService.getRecordedAudio() ?? '');
|
||||
Future<void> playResult({required Map<String,dynamic> answer,required Voice voice})async{
|
||||
setBusyObject(
|
||||
playing: voice,
|
||||
object: answer['busy_object']);
|
||||
await playAudio(voice: voice,answer: answer);
|
||||
}
|
||||
|
||||
Future<void> pauseRecordedAudio() async =>
|
||||
await runBusyFuture(_pauseRecordedAudio(),
|
||||
busyObject: StateObjects.learnPracticeAnswer);
|
||||
Future<void> playAudio({required Map<String,dynamic> answer,required Voice voice}) async =>
|
||||
await runBusyFuture(_playAudio(voice:voice,answer:answer),
|
||||
busyObject: answer['busy_object']);
|
||||
|
||||
Future<void> _pauseRecordedAudio() async {
|
||||
setBusyObject(StateObjects.learnPracticeAnswer);
|
||||
Future<void> _playAudio({required Map<String,dynamic> answer,required Voice voice}) async {
|
||||
|
||||
|
||||
if(voice == Voice.recorded){
|
||||
await _audioPlayerService
|
||||
.playLocal(answer['recorded_voice_answer']);
|
||||
}else{
|
||||
await _audioPlayerService.playUrl(answer['sample_voice_answer']);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Future<void> pauseAudio() async {
|
||||
await _audioPlayerService.pause();
|
||||
}
|
||||
|
||||
// Set busy object
|
||||
void setBusyObject(StateObjects object) {
|
||||
void setBusyObject({required String object,required Voice playing}) {
|
||||
_playing = playing;
|
||||
_busyObject = object;
|
||||
notifyListeners();
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
// Practice
|
||||
void setPractice(Map<String, dynamic> practice) {
|
||||
_selectedPractice = practice;
|
||||
goTo(1);
|
||||
}
|
||||
|
||||
|
||||
// Dialogue
|
||||
Future<bool?> showAbortDialog() async {
|
||||
|
|
@ -195,6 +219,53 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> nextQuestion({required int index,required LearnQuestion question}) async {
|
||||
await stopRecording();
|
||||
_answers.add({
|
||||
'busy_object': question.id.toString(),
|
||||
'sample_text_answer': question.audioCorrectAnswerText,
|
||||
'sample_voice_answer': question.sampleAnswerVoicePrompt,
|
||||
'recorded_voice_answer' : _voiceRecorderService.getRecordedAudio() ,
|
||||
});
|
||||
if (index != _questions.length) {
|
||||
_questionSetController.nextPage(
|
||||
duration: const Duration(milliseconds: 350),
|
||||
curve: Curves.easeInOutCubic);
|
||||
} else {
|
||||
goTo(3);
|
||||
}
|
||||
}
|
||||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
// Remote api call
|
||||
|
||||
// Learn practice
|
||||
Future<void> getLearnPractices(
|
||||
{required int id, required LearnPractices practice}) async =>
|
||||
await runBusyFuture(_getLearnPractices(id: id, practice: practice),
|
||||
busyObject: StateObjects.learnPractices);
|
||||
|
||||
Future<void> _getLearnPractices(
|
||||
{required int id, required LearnPractices practice}) async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
if (practice == LearnPractices.course) {
|
||||
_practices = await _apiService.getLearnCoursePractices(id);
|
||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||
} else if (practice == LearnPractices.module) {
|
||||
_practices = await _apiService.getLearnModulePractices(id);
|
||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||
} else {
|
||||
_practices = await _apiService.getLearnLessonPractices(id);
|
||||
print('PRACTICE LENGTH: ${_practices.length}');
|
||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _getLearnPracticeQuestions(int id) async {
|
||||
_questions = await _apiService.getLearnQuestions(id);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ 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 '../../../../models/learn_question.dart';
|
||||
import '../../../common/app_colors.dart';
|
||||
import '../../../common/enmus.dart';
|
||||
import '../../../common/ui_helpers.dart';
|
||||
|
|
@ -17,22 +18,24 @@ import '../../../widgets/small_app_bar.dart';
|
|||
|
||||
class InteractLearnPracticeScreen
|
||||
extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
const InteractLearnPracticeScreen({super.key});
|
||||
final int index;
|
||||
final LearnQuestion question;
|
||||
|
||||
const InteractLearnPracticeScreen(
|
||||
{super.key, required this.index, required this.question});
|
||||
|
||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||
viewModel.pop();
|
||||
viewModel.goTo(0);
|
||||
viewModel.pop();
|
||||
viewModel.stopRecording();
|
||||
}
|
||||
|
||||
void _start(LearnPracticeViewModel viewModel) {
|
||||
viewModel.playQuestionAudio();
|
||||
}
|
||||
void _start(LearnPracticeViewModel viewModel) =>
|
||||
viewModel.playVoicePrompt(question);
|
||||
|
||||
Future<void> _stop(LearnPracticeViewModel viewModel) async {
|
||||
await viewModel.stopRecording();
|
||||
viewModel.goTo(4);
|
||||
}
|
||||
|
||||
Future<void> _stop(LearnPracticeViewModel viewModel) async =>
|
||||
await viewModel.nextQuestion(index: index,question: question);
|
||||
|
||||
Future<void> _showSheet(
|
||||
{required BuildContext context,
|
||||
|
|
@ -41,7 +44,7 @@ class InteractLearnPracticeScreen
|
|||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: kcTransparent,
|
||||
builder: (_) => _buildSheet(viewModel),
|
||||
builder: (cxt) => _buildSheet(viewModel),
|
||||
);
|
||||
|
||||
@override
|
||||
|
|
@ -68,17 +71,10 @@ class InteractLearnPracticeScreen
|
|||
children: [
|
||||
_buildBodyColumnWrapper(context: context, viewModel: viewModel),
|
||||
_buildProgressIndicatorState(viewModel),
|
||||
_buildSpeakerState(context: context, viewModel: viewModel)
|
||||
_buildPageLoadingIndicatorState(viewModel)
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildSpeakerState(
|
||||
{required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) =>
|
||||
viewModel.busy(StateObjects.learnPracticeQuestion)
|
||||
? const PageLoadingIndicator()
|
||||
: Container();
|
||||
|
||||
Widget _buildBodyColumnWrapper(
|
||||
{required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) =>
|
||||
|
|
@ -100,24 +96,25 @@ class InteractLearnPracticeScreen
|
|||
{required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) =>
|
||||
[
|
||||
_buildAppBarWrapper(viewModel),
|
||||
_buildAppBarWrapper(context: context,viewModel: viewModel),
|
||||
_buildSpeakingIndicatorWrapper(viewModel),
|
||||
_buildLowerButtonsSectionWrapper(context: context, viewModel: viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarWrapper(LearnPracticeViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper( {required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
_buildAppBar(context: context,viewModel: viewModel),
|
||||
verticalSpaceMedium,
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(LearnPracticeViewModel viewModel) => SmallAppBar(
|
||||
Widget _buildAppBar( {required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => SmallAppBar(
|
||||
showBackButton: true,
|
||||
title: 'Practice Speaking',
|
||||
onTap: () async => await _cancel(viewModel),
|
||||
);
|
||||
onTap: () async => await _showSheet(context: context,viewModel: viewModel),
|
||||
title: 'Practice Speaking ($index/${viewModel.questions.length})');
|
||||
|
||||
Widget _buildSpeakingIndicatorWrapper(LearnPracticeViewModel viewModel) =>
|
||||
Column(
|
||||
|
|
@ -328,6 +325,7 @@ class InteractLearnPracticeScreen
|
|||
CancelLearnPracticeSheet(
|
||||
onClose: viewModel.pop,
|
||||
onContinue: viewModel.pop,
|
||||
user: viewModel.user?.firstName ?? '',
|
||||
onCancel: () async => await _cancel(viewModel),
|
||||
);
|
||||
|
||||
|
|
@ -355,4 +353,9 @@ class InteractLearnPracticeScreen
|
|||
activeColor: kcPrimaryColor,
|
||||
progress: viewModel.progress,
|
||||
backgroundColor: kcVeryLightGrey);
|
||||
|
||||
Widget _buildPageLoadingIndicatorState(LearnPracticeViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.learnPracticeQuestion)
|
||||
? const PageLoadingIndicator()
|
||||
: Container();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,8 +76,8 @@ class LearnPracticeCompletionScreen
|
|||
height: 55,
|
||||
borderRadius: 12,
|
||||
text: 'Continue',
|
||||
onTap: viewModel.pop,
|
||||
foregroundColor: kcWhite,
|
||||
onTap: () => viewModel.goTo(0),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,54 +1,79 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/learn_practice.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
|
||||
|
||||
import '../../../common/app_colors.dart';
|
||||
import '../../../common/ui_helpers.dart';
|
||||
import '../../../widgets/cancel_learn_practice_sheet.dart';
|
||||
import '../../../widgets/custom_elevated_button.dart';
|
||||
import '../../../widgets/small_app_bar.dart';
|
||||
import '../../../widgets/speaking_partner_image.dart';
|
||||
|
||||
class LearnPracticeIntroScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final String buttonLabel;
|
||||
const LearnPracticeIntroScreen({super.key});
|
||||
|
||||
const LearnPracticeIntroScreen(
|
||||
{super.key,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.buttonLabel});
|
||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||
viewModel.pop();
|
||||
viewModel.pop();
|
||||
viewModel.stopRecording();
|
||||
|
||||
}
|
||||
|
||||
Future<void> _showSheet(
|
||||
{required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) async =>
|
||||
await showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: kcTransparent,
|
||||
builder: (cxt) => _buildSheet(viewModel),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
_buildScaffoldWrapper(context: context,viewModel: viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(LearnPracticeViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper( {required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
body: _buildScaffold(context: context,viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(LearnPracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildColumnWrapper(viewModel));
|
||||
Widget _buildScaffold( {required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) =>
|
||||
SafeArea(child: _buildColumnWrapper(context: context,viewModel: viewModel));
|
||||
|
||||
Widget _buildColumnWrapper(LearnPracticeViewModel viewModel) => Padding(
|
||||
Widget _buildColumnWrapper( {required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildColumn(viewModel),
|
||||
child: _buildColumn(context: context,viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn(LearnPracticeViewModel viewModel) => Column(
|
||||
Widget _buildColumn( {required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
_buildAppBar(context: context,viewModel: viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildBodyColumnWrapper(viewModel),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(LearnPracticeViewModel viewModel) => SmallAppBar(
|
||||
Widget _buildAppBar( {required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => SmallAppBar(
|
||||
showBackButton: true,
|
||||
onTap: viewModel.goBack,
|
||||
title: 'Practice Speaking',
|
||||
onTap: () async => await _showSheet(context: context,viewModel: viewModel),
|
||||
|
||||
);
|
||||
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
||||
CancelLearnPracticeSheet(
|
||||
onClose: viewModel.pop,
|
||||
onContinue: viewModel.pop,
|
||||
user: viewModel.user?.firstName ?? '',
|
||||
onCancel: () async => await _cancel(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBodyColumnWrapper(LearnPracticeViewModel viewModel) => Expanded(
|
||||
|
|
@ -96,7 +121,7 @@ class LearnPracticeIntroScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildPartnerName() => Text.rich(
|
||||
TextSpan(text: 'Daniel', style: style14DG600, children: [
|
||||
TextSpan(text: 'Dawit', style: style14DG600, children: [
|
||||
TextSpan(
|
||||
text: ' - Your Speaking Partner',
|
||||
style: style14MG400,
|
||||
|
|
@ -105,13 +130,14 @@ class LearnPracticeIntroScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
title,
|
||||
'Let\'s practice what you just learnt!',
|
||||
style: style25DG600,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
subtitle,
|
||||
'I’ll ask you a few questions, and you can respond naturally.',
|
||||
maxLines: 1,
|
||||
style: style14DG400,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
|
@ -126,9 +152,9 @@ class LearnPracticeIntroScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
CustomElevatedButton(
|
||||
height: 55,
|
||||
borderRadius: 12,
|
||||
text: buttonLabel,
|
||||
text: 'Practice',
|
||||
foregroundColor: kcWhite,
|
||||
onTap: () => viewModel.goTo(2),
|
||||
onTap: () => viewModel.goTo(1),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/learn_question.dart';
|
||||
import 'package:yimaru_app/models/practice.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_practice/screens/start_learn_practice_screen.dart';
|
||||
import 'package:yimaru_app/ui/widgets/learn_practice_card.dart';
|
||||
|
||||
import '../../../common/app_colors.dart';
|
||||
import '../../../common/ui_helpers.dart';
|
||||
import '../../../widgets/small_app_bar.dart';
|
||||
import '../learn_practice_viewmodel.dart';
|
||||
import 'interact_learn_practice_screen.dart';
|
||||
|
||||
class LearnPracticeQuestionsScreen
|
||||
extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
const LearnPracticeQuestionsScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
|
||||
_buildQuestionSetView(viewModel);
|
||||
|
||||
Widget _buildQuestionSetView(LearnPracticeViewModel viewModel) =>
|
||||
PageView.builder(
|
||||
itemCount: viewModel.questions.length,
|
||||
controller: viewModel.questionSetController,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (cotext, index) => _buildQuestionView(
|
||||
index: index + 1,
|
||||
viewModel: viewModel,
|
||||
question: viewModel.questions[index]),
|
||||
);
|
||||
|
||||
Widget _buildQuestionView(
|
||||
{required int index,
|
||||
required LearnQuestion question,
|
||||
required LearnPracticeViewModel viewModel}) =>
|
||||
PageView(
|
||||
controller: viewModel.questionController,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
children: _buildScreens(index:index,question: question),
|
||||
);
|
||||
|
||||
List<Widget> _buildScreens({
|
||||
required int index,
|
||||
required LearnQuestion question,
|
||||
}) =>
|
||||
[
|
||||
_buildStartLearnPracticeScreen(index:index,question: question),
|
||||
_buildInteractLearnPracticeScreen(index:index,question: question)
|
||||
];
|
||||
|
||||
Widget _buildStartLearnPracticeScreen({
|
||||
required int index,
|
||||
required LearnQuestion question,}
|
||||
) => StartLearnPracticeScreen(
|
||||
index: index,
|
||||
question: question,
|
||||
);
|
||||
|
||||
Widget _buildInteractLearnPracticeScreen({
|
||||
required int index,
|
||||
required LearnQuestion question,}) =>
|
||||
InteractLearnPracticeScreen(index: index,question: question,);
|
||||
}
|
||||
|
|
@ -6,45 +6,84 @@ import 'package:yimaru_app/ui/widgets/learn_practice_results_wrapper.dart';
|
|||
|
||||
import '../../../common/app_colors.dart';
|
||||
import '../../../common/ui_helpers.dart';
|
||||
import '../../../widgets/cancel_learn_practice_sheet.dart';
|
||||
import '../../../widgets/custom_elevated_button.dart';
|
||||
import '../../../widgets/small_app_bar.dart';
|
||||
|
||||
class LearnPracticeResultScreen
|
||||
extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
|
||||
const LearnPracticeResultScreen({super.key});
|
||||
|
||||
|
||||
void _navigate(LearnPracticeViewModel viewModel){
|
||||
viewModel.questionSetController.jumpToPage(0);
|
||||
viewModel.goTo(0);
|
||||
}
|
||||
|
||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||
viewModel.pop();
|
||||
viewModel.pop();
|
||||
viewModel.stopRecording();
|
||||
|
||||
}
|
||||
|
||||
Future<void> _showSheet(
|
||||
{required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) async =>
|
||||
await showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: kcTransparent,
|
||||
builder: (cxt) => _buildSheet(viewModel),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
_buildScaffoldWrapper(context: context,viewModel: viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(LearnPracticeViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper({required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
body: _buildScaffold(context: context,viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(LearnPracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||
Widget _buildScaffold({required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) =>
|
||||
SafeArea(child: _buildBodyColumnWrapper(context: context,viewModel: viewModel));
|
||||
|
||||
Widget _buildBodyColumnWrapper(LearnPracticeViewModel viewModel) => Padding(
|
||||
Widget _buildBodyColumnWrapper({required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildBodyColumn(viewModel),
|
||||
child: _buildBodyColumn(context: context,viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBodyColumn(viewModel) => Column(
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
Widget _buildBodyColumn({required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => Column(
|
||||
children: _buildBodyColumnChildren(context: context,viewModel: viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(LearnPracticeViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren({required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
_buildAppBar(context: context,viewModel: viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildBodyWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBar(LearnPracticeViewModel viewModel) => SmallAppBar(
|
||||
Widget _buildAppBar({required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => SmallAppBar(
|
||||
title: 'Result',
|
||||
showBackButton: true,
|
||||
onTap: viewModel.goBack,
|
||||
onTap: () async => await _showSheet(context: context,viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
||||
CancelLearnPracticeSheet(
|
||||
onClose: viewModel.pop,
|
||||
onContinue: viewModel.pop,
|
||||
user: viewModel.user?.firstName ?? '',
|
||||
onCancel: () async => await _cancel(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBodyWrapper(LearnPracticeViewModel viewMode) => Expanded(
|
||||
|
|
@ -71,9 +110,9 @@ class LearnPracticeResultScreen
|
|||
];
|
||||
|
||||
Widget _buildResultsSection(LearnPracticeViewModel viewModel) =>
|
||||
LearnPracticeResultsWrapper(data: viewModel.selectedPractice);
|
||||
const LearnPracticeResultsWrapper();
|
||||
|
||||
Widget _buildLearnPracticeTipSection() => const LearnPracticeTipSection();
|
||||
Widget _buildLearnPracticeTipSection() => const LearnPracticeTipSection();
|
||||
|
||||
Widget _buildLowerButtonsSection(LearnPracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
@ -96,7 +135,7 @@ class LearnPracticeResultScreen
|
|||
text: 'Continue',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
onTap: () => viewModel.goTo(6),
|
||||
onTap: () => viewModel.goTo(4),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
);
|
||||
|
||||
|
|
@ -107,7 +146,8 @@ class LearnPracticeResultScreen
|
|||
borderRadius: 12,
|
||||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
onTap: () => viewModel.goTo(1),
|
||||
foregroundColor: kcPrimaryColor,
|
||||
onTap: () => _navigate(viewModel),
|
||||
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,100 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/widgets/learn_practice_card.dart';
|
||||
|
||||
import '../../../common/app_colors.dart';
|
||||
import '../../../common/ui_helpers.dart';
|
||||
import '../../../widgets/small_app_bar.dart';
|
||||
import '../learn_practice_viewmodel.dart';
|
||||
|
||||
class LearnPracticesScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
final List<Map<String, dynamic>> practices;
|
||||
|
||||
const LearnPracticesScreen({Key? key, required this.practices})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(LearnPracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(LearnPracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBody(viewModel));
|
||||
|
||||
Widget _buildBody(LearnPracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn(LearnPracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildPracticeColumnWrapper(viewModel),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(LearnPracticeViewModel viewModel) => SmallAppBar(
|
||||
onTap: viewModel.pop,
|
||||
showBackButton: true,
|
||||
);
|
||||
|
||||
Widget _buildPracticeColumnWrapper(LearnPracticeViewModel viewModel) =>
|
||||
Expanded(child: _buildPracticeColumnScrollView(viewModel));
|
||||
|
||||
Widget _buildPracticeColumnScrollView(LearnPracticeViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildPracticeColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildPracticeColumn(LearnPracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildPracticeColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildPracticeColumnChildren(LearnPracticeViewModel viewModel) =>
|
||||
[
|
||||
verticalSpaceMedium,
|
||||
_buildTitle(),
|
||||
_buildSubtitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildListView(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
'Learn Practices',
|
||||
style: style18DG700,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
'Select a practice test your progress',
|
||||
style: style14DG400,
|
||||
);
|
||||
|
||||
Widget _buildListView(LearnPracticeViewModel viewModel) => GridView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: practices.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) =>
|
||||
_buildCard(index: index, practice: practices[index]),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 15,
|
||||
crossAxisSpacing: 15,
|
||||
childAspectRatio: 1.45,
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildCard(
|
||||
{required int index, required Map<String, dynamic> practice}) =>
|
||||
LearnPracticeCard(
|
||||
index: index + 1,
|
||||
practice: practice,
|
||||
);
|
||||
}
|
||||
|
|
@ -3,57 +3,92 @@ import 'package:stacked/stacked.dart';
|
|||
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_column_button.dart';
|
||||
|
||||
import '../../../../models/learn_question.dart';
|
||||
import '../../../common/app_colors.dart';
|
||||
import '../../../common/ui_helpers.dart';
|
||||
import '../../../widgets/cancel_learn_practice_sheet.dart';
|
||||
import '../../../widgets/small_app_bar.dart';
|
||||
|
||||
class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
const StartLearnPracticeScreen({super.key});
|
||||
final int index;
|
||||
final LearnQuestion question;
|
||||
|
||||
const StartLearnPracticeScreen({super.key,required this.index,required this.question});
|
||||
|
||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||
viewModel.pop();
|
||||
viewModel.pop();
|
||||
viewModel.stopRecording();
|
||||
}
|
||||
|
||||
void _start(LearnPracticeViewModel viewModel) {
|
||||
viewModel.playQuestionAudio();
|
||||
viewModel.playVoicePrompt(question);
|
||||
}
|
||||
|
||||
Future<void> _showSheet(
|
||||
{required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) async =>
|
||||
await showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: kcTransparent,
|
||||
builder: (cxt) => _buildSheet(viewModel),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
_buildScaffoldWrapper(context: context,viewModel: viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(LearnPracticeViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper( {required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
body: _buildScaffold(context: context,viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(LearnPracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||
Widget _buildScaffold( {required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) =>
|
||||
SafeArea(child: _buildBodyColumnWrapper(context: context,viewModel: viewModel));
|
||||
|
||||
Widget _buildBodyColumnWrapper(LearnPracticeViewModel viewModel) => Padding(
|
||||
Widget _buildBodyColumnWrapper( {required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildBodyColumn(viewModel),
|
||||
child: _buildBodyColumn(context: context,viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBodyColumn(LearnPracticeViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn( {required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
children: _buildBodyColumnChildren(context: context,viewModel: viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(LearnPracticeViewModel viewModel) => [
|
||||
_buildAppBarWrapper(viewModel),
|
||||
List<Widget> _buildBodyColumnChildren( {required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => [
|
||||
_buildAppBarWrapper(context: context,viewModel: viewModel),
|
||||
_buildStartButtonWrapper(viewModel),
|
||||
_buildLowerButtonsSectionWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarWrapper(LearnPracticeViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper( {required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
_buildAppBar(context: context,viewModel: viewModel),
|
||||
verticalSpaceMedium,
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(LearnPracticeViewModel viewModel) => SmallAppBar(
|
||||
showBackButton: true,
|
||||
onTap: viewModel.goBack,
|
||||
title: 'Practice Speaking',
|
||||
Widget _buildAppBar( {required BuildContext context,
|
||||
required LearnPracticeViewModel viewModel}) => SmallAppBar(
|
||||
showBackButton: true,
|
||||
onTap: () async => await _showSheet(context: context,viewModel: viewModel),
|
||||
title: 'Practice Speaking ($index/${viewModel.questions.length})');
|
||||
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
||||
CancelLearnPracticeSheet(
|
||||
onClose: viewModel.pop,
|
||||
onContinue: viewModel.pop,
|
||||
user: viewModel.user?.firstName ?? '',
|
||||
onCancel: () async => await _cancel(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildStartButtonWrapper(LearnPracticeViewModel viewModel) => Expanded(
|
||||
|
|
|
|||
95
lib/ui/views/learn_program/learn_program_view.dart
Normal file
95
lib/ui/views/learn_program/learn_program_view.dart
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/learn_program.dart';
|
||||
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/enmus.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||
import '../../widgets/profile_app_bar.dart';
|
||||
import '../../widgets/learn_program_tile.dart';
|
||||
import 'learn_program_viewmodel.dart';
|
||||
|
||||
class LearnProgramView extends StackedView<LearnProgramViewModel> {
|
||||
const LearnProgramView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnProgramViewModel viewModel) async {
|
||||
await viewModel.getLearnPrograms();
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
||||
@override
|
||||
LearnProgramViewModel viewModelBuilder(BuildContext context) =>
|
||||
LearnProgramViewModel();
|
||||
|
||||
@override
|
||||
Widget builder(
|
||||
BuildContext context,
|
||||
LearnProgramViewModel viewModel,
|
||||
Widget? child,
|
||||
) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(LearnProgramViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(LearnProgramViewModel viewModel) =>
|
||||
SafeArea(child: _buildBody(viewModel));
|
||||
|
||||
Widget _buildBody(LearnProgramViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn(LearnProgramViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildProgramsColumnWrapper(viewModel)
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(LearnProgramViewModel viewModel) => ProfileAppBar(
|
||||
name: viewModel.user?.firstName,
|
||||
profileImage: viewModel.user?.profilePicture,
|
||||
);
|
||||
|
||||
Widget _buildProgramsColumnWrapper(LearnProgramViewModel viewModel) =>
|
||||
Expanded(child: _buildProgramsColumnScrollView(viewModel));
|
||||
|
||||
Widget _buildProgramsColumnScrollView(LearnProgramViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildListViewBuilder(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildListViewBuilder(LearnProgramViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.learnPrograms)
|
||||
? _buildProgressIndicator()
|
||||
: _buildListView(viewModel);
|
||||
|
||||
Widget _buildProgressIndicator() => const Center(
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
Widget _buildListView(LearnProgramViewModel viewModel) => ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.learnPrograms.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
separatorBuilder: (context, index) => verticalSpaceSmall,
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
program: viewModel.learnPrograms[index],
|
||||
onTap: () async => await viewModel
|
||||
.navigateToLearnCourse(viewModel.learnPrograms[index].id ?? 0),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildTile({
|
||||
required LearnProgram program,
|
||||
required GestureTapCallback onTap,
|
||||
}) =>
|
||||
LearnProgramTile(onTap: onTap, program: program);
|
||||
}
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/app/app.router.dart';
|
||||
import 'package:yimaru_app/models/learn_program.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../app/app.router.dart';
|
||||
import '../../../models/subcategory.dart';
|
||||
import '../../../models/user.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/authentication_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/enmus.dart';
|
||||
|
||||
class LearnSubcategoryViewModel extends ReactiveViewModel {
|
||||
class LearnProgramViewModel extends ReactiveViewModel {
|
||||
// Dependency injection
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
|
|
@ -29,26 +29,28 @@ class LearnSubcategoryViewModel extends ReactiveViewModel {
|
|||
|
||||
User? get user => _user;
|
||||
|
||||
// Learn subcategories
|
||||
List<Subcategory> _subcategories = [];
|
||||
// Learn programs
|
||||
List<LearnProgram> _learnPrograms = [];
|
||||
|
||||
List<Subcategory> get subcategories => _subcategories;
|
||||
List<LearnProgram> get learnPrograms => _learnPrograms;
|
||||
|
||||
// Navigation
|
||||
Future<void> navigateToLearn(int id) async =>
|
||||
_navigationService.navigateToLearnView(id: id);
|
||||
Future<void> navigateToLearnCourse(int id) async =>
|
||||
_navigationService.navigateToLearnCourseView(id: id);
|
||||
|
||||
// Remote api call
|
||||
|
||||
// Learn subcategories
|
||||
Future<void> getLearnSubcategories() async =>
|
||||
await runBusyFuture(_getLearnSubcategories(),
|
||||
busyObject: StateObjects.learnSubcategories);
|
||||
// Learn programs
|
||||
Future<void> getLearnPrograms() async =>
|
||||
await runBusyFuture(_getLearnPrograms(),
|
||||
busyObject: StateObjects.learnPrograms);
|
||||
|
||||
Future<void> _getLearnSubcategories() async {
|
||||
if (_subcategories.isEmpty) {
|
||||
Future<void> _getLearnPrograms() async {
|
||||
if (_learnPrograms.isEmpty) {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
_subcategories = await _apiService.getLearnSubcategories();
|
||||
_learnPrograms = await _apiService.getLearnPrograms();
|
||||
_learnPrograms
|
||||
.sort((a, b) => (a.sortOrder ?? 0).compareTo(b.sortOrder ?? 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/widgets/learn_subcategory_card.dart';
|
||||
|
||||
import '../../../models/subcategory.dart';
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/enmus.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||
import '../../widgets/profile_app_bar.dart';
|
||||
import 'learn_subcategory_viewmodel.dart';
|
||||
|
||||
class LearnSubcategoryView extends StackedView<LearnSubcategoryViewModel> {
|
||||
const LearnSubcategoryView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnSubcategoryViewModel viewModel) async {
|
||||
await viewModel.getLearnSubcategories();
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
||||
@override
|
||||
LearnSubcategoryViewModel viewModelBuilder(BuildContext context) =>
|
||||
LearnSubcategoryViewModel();
|
||||
|
||||
@override
|
||||
Widget builder(
|
||||
BuildContext context,
|
||||
LearnSubcategoryViewModel viewModel,
|
||||
Widget? child,
|
||||
) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(LearnSubcategoryViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(LearnSubcategoryViewModel viewModel) =>
|
||||
SafeArea(child: _buildBody(viewModel));
|
||||
|
||||
Widget _buildBody(LearnSubcategoryViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn(LearnSubcategoryViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildSubcategoryColumnWrapper(viewModel)
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(LearnSubcategoryViewModel viewModel) => ProfileAppBar(
|
||||
name: viewModel.user?.firstName,
|
||||
profileImage: viewModel.user?.profilePicture,
|
||||
);
|
||||
|
||||
Widget _buildSubcategoryColumnWrapper(LearnSubcategoryViewModel viewModel) =>
|
||||
Expanded(child: _buildSubcategoryColumnScrollView(viewModel));
|
||||
|
||||
Widget _buildSubcategoryColumnScrollView(
|
||||
LearnSubcategoryViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildListViewBuilder(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildListViewBuilder(LearnSubcategoryViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.learnSubcategories)
|
||||
? _buildProgressIndicator()
|
||||
: _buildListView(viewModel);
|
||||
|
||||
Widget _buildProgressIndicator() => const Center(
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
Widget _buildListView(LearnSubcategoryViewModel viewModel) =>
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.subcategories.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
separatorBuilder: (context, index) => verticalSpaceSmall,
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
subcategory: viewModel.subcategories[index],
|
||||
onTap: () async => await viewModel
|
||||
.navigateToLearn(viewModel.subcategories[index].id ?? 0),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildTile({
|
||||
required Subcategory subcategory,
|
||||
required GestureTapCallback onTap,
|
||||
}) =>
|
||||
LearnSubcategoryCard(
|
||||
onTap: onTap,
|
||||
subcategory: subcategory,
|
||||
);
|
||||
}
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/module.dart';
|
||||
import 'package:yimaru_app/models/submodule.dart';
|
||||
import 'package:yimaru_app/ui/widgets/course_module_banner.dart';
|
||||
import 'package:yimaru_app/ui/widgets/learn_submodule_tile.dart';
|
||||
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/enmus.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||
import '../../widgets/custom_elevated_button.dart';
|
||||
import '../../widgets/overall_learn_progress.dart';
|
||||
import '../../widgets/small_app_bar.dart';
|
||||
import 'learn_submodule_viewmodel.dart';
|
||||
|
||||
class LearnSubmoduleView extends StackedView<LearnSubmoduleViewModel> {
|
||||
final Module module;
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnSubmoduleViewModel viewModel) async {
|
||||
await viewModel.getSubmodules(module.id ?? 0);
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
||||
const LearnSubmoduleView({Key? key, required this.module}) : super(key: key);
|
||||
|
||||
@override
|
||||
LearnSubmoduleViewModel viewModelBuilder(BuildContext context) =>
|
||||
LearnSubmoduleViewModel();
|
||||
|
||||
@override
|
||||
Widget builder(
|
||||
BuildContext context,
|
||||
LearnSubmoduleViewModel viewModel,
|
||||
Widget? child,
|
||||
) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(LearnSubmoduleViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(LearnSubmoduleViewModel viewModel) =>
|
||||
SafeArea(child: _buildBody(viewModel));
|
||||
|
||||
Widget _buildBody(LearnSubmoduleViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn(LearnSubmoduleViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildModulesColumnWrapper(viewModel),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(LearnSubmoduleViewModel viewModel) => SmallAppBar(
|
||||
title: 'Submodules',
|
||||
onTap: viewModel.pop,
|
||||
showBackButton: true,
|
||||
);
|
||||
|
||||
Widget _buildModulesColumnWrapper(LearnSubmoduleViewModel viewModel) =>
|
||||
Expanded(child: _buildLevelsColumnScrollView(viewModel));
|
||||
|
||||
Widget _buildLevelsColumnScrollView(LearnSubmoduleViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildLevelsColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildLevelsColumn(LearnSubmoduleViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: _buildLevelsColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildLevelsColumnChildren(LearnSubmoduleViewModel viewModel) =>
|
||||
[
|
||||
verticalSpaceMedium,
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildCourseModuleBanner(),
|
||||
verticalSpaceMedium,
|
||||
_buildOverallProgress(),
|
||||
verticalSpaceTiny,
|
||||
_buildContinueButton(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildListViewBuilder(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
module.title ?? '',
|
||||
style: style18P600,
|
||||
);
|
||||
|
||||
Widget _buildCourseModuleBanner() => const CourseModuleBanner();
|
||||
|
||||
Widget _buildOverallProgress() => const OverallLearnProgress(
|
||||
color: Colors.transparent,
|
||||
);
|
||||
|
||||
Widget _buildContinueButton(LearnSubmoduleViewModel viewModel) =>
|
||||
const CustomElevatedButton(
|
||||
height: 55,
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text: 'Continue Submodule',
|
||||
backgroundColor: kcPrimaryColor);
|
||||
|
||||
Widget _buildListViewBuilder(LearnSubmoduleViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.learnSubmodules)
|
||||
? _buildProgressIndicator()
|
||||
: _buildListView(viewModel);
|
||||
|
||||
Widget _buildProgressIndicator() => const Center(
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
Widget _buildListView(LearnSubmoduleViewModel viewModel) => ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.submodules.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
submodule: viewModel.submodules[index],
|
||||
onPracticeTap: () {},
|
||||
onLessonTap: () async => await viewModel
|
||||
.navigateToLearnLessons(viewModel.submodules[index]),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildTile({
|
||||
required Submodule submodule,
|
||||
required GestureTapCallback onLessonTap,
|
||||
required GestureTapCallback onPracticeTap,
|
||||
}) =>
|
||||
LearnSubmoduleTile(
|
||||
submodule: submodule,
|
||||
onLessonTap: onLessonTap,
|
||||
onPracticeTap: onPracticeTap,
|
||||
);
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/app/app.router.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../models/submodule.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/enmus.dart';
|
||||
|
||||
class LearnSubmoduleViewModel extends BaseViewModel {
|
||||
// Dependency injection
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
final _statusChecker = locator<StatusCheckerService>();
|
||||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
// Learn submodule
|
||||
List<Submodule> _submodules = [];
|
||||
|
||||
List<Submodule> get submodules => _submodules;
|
||||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> navigateToLearnLessons(Submodule submodule) async =>
|
||||
await _navigationService.navigateToLearnLessonView(submodule: submodule);
|
||||
|
||||
// Remote api call
|
||||
|
||||
// Learn modules
|
||||
Future<void> getSubmodules(int id) async =>
|
||||
await runBusyFuture(_getSubmodules(id),
|
||||
busyObject: StateObjects.learnSubmodules);
|
||||
|
||||
Future<void> _getSubmodules(int id) async {
|
||||
if (_submodules.isEmpty) {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
_submodules = await _apiService.getSubmodules(id);
|
||||
_submodules.sort(
|
||||
(a, b) => (a.displayOrder ?? 0).compareTo(b.displayOrder ?? 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// dart format width=80
|
||||
|
||||
// **************************************************************************
|
||||
// StackedFormGenerator
|
||||
|
|
@ -219,13 +220,13 @@ extension ValueProperties on FormStateHelper {
|
|||
}
|
||||
|
||||
extension Methods on FormStateHelper {
|
||||
setOtpValidationMessage(String? validationMessage) =>
|
||||
void setOtpValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[OtpValueKey] = validationMessage;
|
||||
setPasswordValidationMessage(String? validationMessage) =>
|
||||
void setPasswordValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[PasswordValueKey] = validationMessage;
|
||||
setEmailValidationMessage(String? validationMessage) =>
|
||||
void setEmailValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[EmailValueKey] = validationMessage;
|
||||
setPhoneNumberValidationMessage(String? validationMessage) =>
|
||||
void setPhoneNumberValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[PhoneNumberValueKey] = validationMessage;
|
||||
|
||||
/// Clears text input fields on the Form
|
||||
|
|
@ -253,7 +254,7 @@ String? getValidationMessage(String key) {
|
|||
if (validatorForKey == null) return null;
|
||||
|
||||
String? validationMessageForKey = validatorForKey(
|
||||
_LoginViewTextEditingControllers[key]!.text,
|
||||
_LoginViewTextEditingControllers[key]?.text,
|
||||
);
|
||||
|
||||
return validationMessageForKey;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// dart format width=80
|
||||
|
||||
// **************************************************************************
|
||||
// StackedFormGenerator
|
||||
|
|
@ -253,15 +254,15 @@ extension ValueProperties on FormStateHelper {
|
|||
}
|
||||
|
||||
extension Methods on FormStateHelper {
|
||||
setTopicValidationMessage(String? validationMessage) =>
|
||||
void setTopicValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[TopicValueKey] = validationMessage;
|
||||
setFullNameValidationMessage(String? validationMessage) =>
|
||||
void setFullNameValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[FullNameValueKey] = validationMessage;
|
||||
setChallengeValidationMessage(String? validationMessage) =>
|
||||
void setChallengeValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[ChallengeValueKey] = validationMessage;
|
||||
setOccupationValidationMessage(String? validationMessage) =>
|
||||
void setOccupationValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[OccupationValueKey] = validationMessage;
|
||||
setLanguageGoalValidationMessage(String? validationMessage) =>
|
||||
void setLanguageGoalValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[LanguageGoalValueKey] = validationMessage;
|
||||
|
||||
/// Clears text input fields on the Form
|
||||
|
|
@ -291,7 +292,7 @@ String? getValidationMessage(String key) {
|
|||
if (validatorForKey == null) return null;
|
||||
|
||||
String? validationMessageForKey = validatorForKey(
|
||||
_OnboardingViewTextEditingControllers[key]!.text,
|
||||
_OnboardingViewTextEditingControllers[key]?.text,
|
||||
);
|
||||
|
||||
return validationMessageForKey;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// dart format width=80
|
||||
|
||||
// **************************************************************************
|
||||
// StackedFormGenerator
|
||||
|
|
@ -257,15 +258,15 @@ extension ValueProperties on FormStateHelper {
|
|||
}
|
||||
|
||||
extension Methods on FormStateHelper {
|
||||
setEmailValidationMessage(String? validationMessage) =>
|
||||
void setEmailValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[EmailValueKey] = validationMessage;
|
||||
setPhoneNumberValidationMessage(String? validationMessage) =>
|
||||
void setPhoneNumberValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[PhoneNumberValueKey] = validationMessage;
|
||||
setLastNameValidationMessage(String? validationMessage) =>
|
||||
void setLastNameValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[LastNameValueKey] = validationMessage;
|
||||
setFirstNameValidationMessage(String? validationMessage) =>
|
||||
void setFirstNameValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[FirstNameValueKey] = validationMessage;
|
||||
setOccupationValidationMessage(String? validationMessage) =>
|
||||
void setOccupationValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[OccupationValueKey] = validationMessage;
|
||||
|
||||
/// Clears text input fields on the Form
|
||||
|
|
@ -295,7 +296,7 @@ String? getValidationMessage(String key) {
|
|||
if (validatorForKey == null) return null;
|
||||
|
||||
String? validationMessageForKey = validatorForKey(
|
||||
_ProfileDetailViewTextEditingControllers[key]!.text,
|
||||
_ProfileDetailViewTextEditingControllers[key]?.text,
|
||||
);
|
||||
|
||||
return validationMessageForKey;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// dart format width=80
|
||||
|
||||
// **************************************************************************
|
||||
// StackedFormGenerator
|
||||
|
|
@ -252,15 +253,15 @@ extension ValueProperties on FormStateHelper {
|
|||
}
|
||||
|
||||
extension Methods on FormStateHelper {
|
||||
setOtpValidationMessage(String? validationMessage) =>
|
||||
void setOtpValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[OtpValueKey] = validationMessage;
|
||||
setEmailValidationMessage(String? validationMessage) =>
|
||||
void setEmailValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[EmailValueKey] = validationMessage;
|
||||
setPasswordValidationMessage(String? validationMessage) =>
|
||||
void setPasswordValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[PasswordValueKey] = validationMessage;
|
||||
setPhoneNumberValidationMessage(String? validationMessage) =>
|
||||
void setPhoneNumberValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[PhoneNumberValueKey] = validationMessage;
|
||||
setConfirmPasswordValidationMessage(String? validationMessage) =>
|
||||
void setConfirmPasswordValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[ConfirmPasswordValueKey] =
|
||||
validationMessage;
|
||||
|
||||
|
|
@ -291,7 +292,7 @@ String? getValidationMessage(String key) {
|
|||
if (validatorForKey == null) return null;
|
||||
|
||||
String? validationMessageForKey = validatorForKey(
|
||||
_RegisterViewTextEditingControllers[key]!.text,
|
||||
_RegisterViewTextEditingControllers[key]?.text,
|
||||
);
|
||||
|
||||
return validationMessageForKey;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,27 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/services/authentication_service.dart';
|
||||
import 'package:yimaru_app/services/in_app_update_service.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../app/app.router.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
|
||||
class StartupViewModel extends BaseViewModel {
|
||||
// Dependency injection
|
||||
final _statusChecker = locator<StatusCheckerService>();
|
||||
final _navigationService = locator<NavigationService>();
|
||||
final _inAppUpdateService = locator<InAppUpdateService>();
|
||||
final _authenticationService = locator<AuthenticationService>();
|
||||
|
||||
// Place anything here that needs to happen before we get into the application
|
||||
Future runStartupLogic() async {
|
||||
final loggedIn = await _authenticationService.userLoggedIn();
|
||||
|
||||
final firstTimeInstall = await _authenticationService.isFirstTimeInstall();
|
||||
|
||||
await _inAppUpdate();
|
||||
|
||||
if (firstTimeInstall) {
|
||||
await _navigationService.replaceWithWelcomeView();
|
||||
} else {
|
||||
|
|
@ -26,4 +34,10 @@ class StartupViewModel extends BaseViewModel {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _inAppUpdate() async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
await _inAppUpdateService.checkForUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,25 +7,17 @@ import '../../../app/app.locator.dart';
|
|||
import '../../../services/status_checker_service.dart';
|
||||
|
||||
class WelcomeViewModel extends BaseViewModel {
|
||||
// Dependency Injection
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
final _statusChecker = locator<StatusCheckerService>();
|
||||
|
||||
final _authenticationService = locator<AuthenticationService>();
|
||||
|
||||
int _currentPage = 0;
|
||||
|
||||
int get currentPage => _currentPage;
|
||||
|
||||
// Navigation
|
||||
Future<void> navigateToLogin() async =>
|
||||
await _navigationService.navigateToLoginView();
|
||||
|
||||
void next() {
|
||||
_currentPage++;
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
// Remote api call
|
||||
|
||||
// First time install
|
||||
|
|
@ -34,9 +26,7 @@ class WelcomeViewModel extends BaseViewModel {
|
|||
}
|
||||
|
||||
Future<void> _setFirstTimeInstall() async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
await _authenticationService.setFirstTimeInstall(false);
|
||||
await navigateToLogin();
|
||||
}
|
||||
await _authenticationService.setFirstTimeInstall(false);
|
||||
await navigateToLogin();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,37 +8,38 @@ import '../common/ui_helpers.dart';
|
|||
import 'custom_bottom_sheet.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
class CancelLearnPracticeSheet extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
class CancelLearnPracticeSheet extends StatelessWidget {
|
||||
final String user;
|
||||
final GestureTapCallback? onClose;
|
||||
final GestureTapCallback? onCancel;
|
||||
final GestureTapCallback? onContinue;
|
||||
|
||||
const CancelLearnPracticeSheet(
|
||||
{super.key, this.onClose, this.onCancel, this.onContinue});
|
||||
{super.key, this.onClose, this.onCancel, this.onContinue,required this.user});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
|
||||
_buildSheetWrapper(viewModel);
|
||||
Widget build(BuildContext context) =>
|
||||
_buildSheetWrapper();
|
||||
|
||||
Widget _buildSheetWrapper(LearnPracticeViewModel viewModel) =>
|
||||
Widget _buildSheetWrapper() =>
|
||||
CustomBottomSheet(
|
||||
height: 500, onTap: onClose, child: _buildColumnWrapper(viewModel));
|
||||
height: 500, onTap: onClose, child: _buildColumnWrapper());
|
||||
|
||||
Widget _buildColumnWrapper(LearnPracticeViewModel viewModel) => Padding(
|
||||
Widget _buildColumnWrapper() => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildColumn(viewModel),
|
||||
child: _buildColumn(),
|
||||
);
|
||||
|
||||
Widget _buildColumn(LearnPracticeViewModel viewModel) => Column(
|
||||
Widget _buildColumn() => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: _buildSheetChildren(viewModel),
|
||||
children: _buildSheetChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildSheetChildren(LearnPracticeViewModel viewModel) => [
|
||||
List<Widget> _buildSheetChildren() => [
|
||||
verticalSpaceLarge,
|
||||
_buildImage(),
|
||||
verticalSpaceMedium,
|
||||
_buildMessage(viewModel),
|
||||
_buildMessage(),
|
||||
_buildSubtitle(),
|
||||
verticalSpaceLarge,
|
||||
_buildContinueButton(),
|
||||
|
|
@ -49,10 +50,10 @@ class CancelLearnPracticeSheet extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
radius: 45,
|
||||
);
|
||||
|
||||
Widget _buildMessage(LearnPracticeViewModel viewModel) => Text.rich(
|
||||
Widget _buildMessage() => Text.rich(
|
||||
TextSpan(text: 'You’re almost there,', style: style18DG700, children: [
|
||||
TextSpan(
|
||||
text: ' ${viewModel.user?.firstName ?? ''}!',
|
||||
text: ' $user',
|
||||
style: style18P600,
|
||||
)
|
||||
]),
|
||||
|
|
|
|||
166
lib/ui/widgets/learn_course_tile.dart
Normal file
166
lib/ui/widgets/learn_course_tile.dart
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/widgets/progress_status.dart';
|
||||
|
||||
import '../../models/learn_course.dart';
|
||||
import '../common/app_colors.dart';
|
||||
import '../common/ui_helpers.dart';
|
||||
import '../views/learn_course/learn_course_viewmodel.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
class LearnCourseTile extends ViewModelWidget<LearnCourseViewModel> {
|
||||
final LearnCourse course;
|
||||
final GestureTapCallback? onViewTap;
|
||||
final GestureTapCallback? onPracticeTap;
|
||||
|
||||
const LearnCourseTile({
|
||||
super.key,
|
||||
this.onViewTap,
|
||||
this.onPracticeTap,
|
||||
required this.course,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnCourseViewModel viewModel) =>
|
||||
_buildExpansionTileCard(viewModel);
|
||||
|
||||
Widget _buildExpansionTileCard(LearnCourseViewModel viewModel) => Container(
|
||||
margin: const EdgeInsets.only(bottom: 15),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(color: kcPrimaryColor.withOpacity(0.1)),
|
||||
),
|
||||
child: _buildExpansionTile(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildExpansionTile(LearnCourseViewModel viewModel) => ExpansionTile(
|
||||
textColor: kcDarkGrey,
|
||||
subtitle: _buildContent(),
|
||||
title: _buildTitleWrapper(),
|
||||
collapsedIconColor: kcDarkGrey,
|
||||
collapsedTextColor: kcDarkGrey,
|
||||
shape: Border.all(color: kcTransparent),
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
enabled: (course.access?.isAccessible ?? false),
|
||||
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
tilePadding: const EdgeInsets.fromLTRB(15, 0, 15, 5),
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
showTrailingIcon: (course.access?.isAccessible ?? false),
|
||||
initiallyExpanded: (course.access?.isAccessible ?? false),
|
||||
collapsedBackgroundColor: (course.access?.isAccessible ?? false)
|
||||
? kcPrimaryColor.withOpacity(0.1)
|
||||
: kcBackgroundColor,
|
||||
children: _buildExpansionTileChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildExpansionTileChildren(LearnCourseViewModel viewModel) => [
|
||||
_buildDivider(),
|
||||
verticalSpaceSmall,
|
||||
_buildActionButtonsWrapper(viewModel),
|
||||
verticalSpaceSmall,
|
||||
];
|
||||
|
||||
Widget _buildTitleWrapper() => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: _buildTitleState(),
|
||||
);
|
||||
|
||||
Widget _buildTitleState() => (course.access?.isAccessible ?? false)
|
||||
? _buildActiveTitleWrapper()
|
||||
: _buildInactiveTitleWrapper();
|
||||
|
||||
Widget _buildInactiveTitleWrapper() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildInactiveTitleChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildInactiveTitleChildren() => [
|
||||
_buildLockIcon(),
|
||||
verticalSpaceSmall,
|
||||
_buildTitle(),
|
||||
];
|
||||
|
||||
Widget _buildLockIcon() => const Icon(
|
||||
Icons.lock_outline_rounded,
|
||||
color: kcLightGrey,
|
||||
);
|
||||
|
||||
Widget _buildActiveTitleWrapper() => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _buildActiveTitleChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildActiveTitleChildren() =>
|
||||
[_buildTitle(), horizontalSpaceSmall, _buildProgressStatusState()];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
course.name ?? '',
|
||||
style:
|
||||
(course.access?.isAccessible ?? false) ? style16P600 : style16DG600,
|
||||
);
|
||||
|
||||
Widget _buildProgressStatusState() =>
|
||||
!(course.access?.isCompleted ?? false) &&
|
||||
course.access?.progressPercent != 0
|
||||
? _buildProgressStatus()
|
||||
: Container();
|
||||
|
||||
Widget _buildProgressStatus() => const ProgressStatus(
|
||||
color: kcPrimaryColor,
|
||||
status: 'Current Level',
|
||||
);
|
||||
|
||||
Widget _buildContent() => Text(
|
||||
course.description ?? '',
|
||||
style: style14DG400,
|
||||
);
|
||||
|
||||
Widget _buildDivider() => const Divider(color: kcVeryLightGrey);
|
||||
|
||||
Widget _buildActionButtonsWrapper(LearnCourseViewModel viewModel) =>
|
||||
Container(
|
||||
height: 40,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildActionButtons(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildActionButtons(LearnCourseViewModel viewModel) => Row(
|
||||
children: [
|
||||
_buildViewButtonWrapper(viewModel),
|
||||
horizontalSpaceSmall,
|
||||
_buildPracticeButtonWrapper(viewModel)
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildViewButtonWrapper(LearnCourseViewModel viewModel) => Expanded(
|
||||
child: _buildModuleButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildModuleButton(LearnCourseViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 15,
|
||||
borderRadius: 12,
|
||||
onTap: onViewTap,
|
||||
text: 'View Courses',
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
);
|
||||
|
||||
Widget _buildPracticeButtonWrapper(LearnCourseViewModel viewModel) =>
|
||||
Expanded(
|
||||
child: _buildPracticeButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildPracticeButton(LearnCourseViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 15,
|
||||
borderRadius: 12,
|
||||
onTap: onPracticeTap,
|
||||
text: 'View Practices',
|
||||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
);
|
||||
}
|
||||
|
|
@ -1,22 +1,28 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:yimaru_app/models/lesson.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/learn_lesson.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_lesson/learn_lesson_viewmodel.dart';
|
||||
import 'package:yimaru_app/ui/widgets/mini_thumbnail.dart';
|
||||
|
||||
import '../common/app_colors.dart';
|
||||
import '../common/helper_functions.dart';
|
||||
import '../common/ui_helpers.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
import 'custom_linear_progress_indicator.dart';
|
||||
|
||||
class LearnLessonTile extends StatelessWidget {
|
||||
final Lesson lesson;
|
||||
class LearnLessonTile extends ViewModelWidget<LearnLessonViewModel> {
|
||||
final LearnLesson lesson;
|
||||
final GestureTapCallback? onLessonTap;
|
||||
final GestureTapCallback? onPracticeTap;
|
||||
|
||||
const LearnLessonTile({super.key, this.onLessonTap, required this.lesson});
|
||||
const LearnLessonTile(
|
||||
{super.key, this.onLessonTap, this.onPracticeTap, required this.lesson});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildContainer();
|
||||
Widget build(BuildContext context, LearnLessonViewModel viewModel) =>
|
||||
_buildContainer(viewModel);
|
||||
|
||||
Widget _buildContainer() => Container(
|
||||
Widget _buildContainer(LearnLessonViewModel viewModel) => Container(
|
||||
width: double.maxFinite,
|
||||
margin: const EdgeInsets.only(bottom: 15),
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -28,27 +34,27 @@ class LearnLessonTile extends StatelessWidget {
|
|||
// : kcGreen.withOpacity(0.1),
|
||||
),
|
||||
),
|
||||
child: _buildExpansionTile(),
|
||||
child: _buildExpansionTile(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildExpansionTile() => ExpansionTile(
|
||||
Widget _buildExpansionTile(LearnLessonViewModel viewModel) => ExpansionTile(
|
||||
enabled: true,
|
||||
title: _buildTitle(),
|
||||
textColor: kcDarkGrey,
|
||||
showTrailingIcon: true,
|
||||
initiallyExpanded: true,
|
||||
initiallyExpanded: false,
|
||||
trailing: _buildPendingIcon(),
|
||||
collapsedIconColor: kcDarkGrey,
|
||||
collapsedTextColor: kcDarkGrey,
|
||||
leading: _buildLeadingWrapper(),
|
||||
shape: Border.all(color: kcTransparent),
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
backgroundColor: kcGreen.withOpacity(0.1),
|
||||
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
tilePadding: const EdgeInsets.fromLTRB(15, 15, 15, 0),
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
collapsedBackgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
childrenPadding: const EdgeInsets.fromLTRB(15, 15, 15, 15),
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
|
||||
childrenPadding: const EdgeInsets.fromLTRB(15, 0, 15, 15),
|
||||
// enabled: status != ProgressStatuses.pending,
|
||||
// backgroundColor: ProgressStatuses.pending == status
|
||||
// ? kcPrimaryColor.withOpacity(0.1)
|
||||
|
|
@ -57,14 +63,18 @@ class LearnLessonTile extends StatelessWidget {
|
|||
// ? kcPrimaryColor.withOpacity(0.1)
|
||||
// : kcGreen.withOpacity(0.1),
|
||||
// initiallyExpanded: status != ProgressStatuses.completed ? true : false,
|
||||
children: _buildExpansionTileChildren(),
|
||||
children: _buildExpansionTileChildren(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildLeadingWrapper() =>
|
||||
MiniThumbnail(thumbnail: lesson.thumbnail ?? 'assets/images/image_1.png');
|
||||
Widget _buildLeadingWrapper() => MiniThumbnail(
|
||||
thumbnail:
|
||||
getReadableUrl(lesson.thumbnail ?? 'assets/images/image_1.png') ??
|
||||
'assets/images/image_1.png');
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
lesson.title ?? '',
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
style: style16DG600,
|
||||
);
|
||||
|
||||
|
|
@ -82,20 +92,23 @@ class LearnLessonTile extends StatelessWidget {
|
|||
color: kcPrimaryColor,
|
||||
);
|
||||
|
||||
List<Widget> _buildExpansionTileChildren() => [_buildExpansionTileItem()];
|
||||
List<Widget> _buildExpansionTileChildren(LearnLessonViewModel viewModel) =>
|
||||
[_buildExpansionTileItem(viewModel)];
|
||||
|
||||
Widget _buildExpansionTileItem() => Column(
|
||||
Widget _buildExpansionTileItem(LearnLessonViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildExpansionTileItemChildren(),
|
||||
children: _buildExpansionTileItemChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildExpansionTileItemChildren() => [
|
||||
List<Widget> _buildExpansionTileItemChildren(
|
||||
LearnLessonViewModel viewModel) =>
|
||||
[
|
||||
_buildProgress(),
|
||||
horizontalSpaceSmall,
|
||||
// _buildProgressText(),
|
||||
// verticalSpaceSmall,
|
||||
_buildActionButtonWrapper()
|
||||
_buildProgressText(),
|
||||
verticalSpaceSmall,
|
||||
_buildActionButtonWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildProgress() => const CustomLinearProgressIndicator(
|
||||
|
|
@ -104,17 +117,53 @@ class LearnLessonTile extends StatelessWidget {
|
|||
backgroundColor: kcVeryLightGrey,
|
||||
);
|
||||
|
||||
// Widget _buildProgressText() => Text(
|
||||
// ProgressStatuses.completed == status ? 'Completed' : 'In Progress',
|
||||
// style: style14P400,
|
||||
// );
|
||||
|
||||
Widget _buildActionButtonWrapper() => SizedBox(
|
||||
height: 50,
|
||||
child: _buildLessonButton(),
|
||||
Widget _buildProgressText() => Text(
|
||||
'In Progress',
|
||||
style: style14P600,
|
||||
);
|
||||
|
||||
Widget _buildLessonButton() => CustomElevatedButton(
|
||||
Widget _buildActionButtonWrapper(LearnLessonViewModel viewModel) => SizedBox(
|
||||
height: 40,
|
||||
child: _buildActionButtons(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildActionButtons(LearnLessonViewModel viewModel) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: __buildActionButtonChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> __buildActionButtonChildren(LearnLessonViewModel viewModel) => [
|
||||
_buildPracticeButtonWrapper(viewModel),
|
||||
horizontalSpaceSmall,
|
||||
_buildLessonButtonWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildPracticeButtonWrapper(LearnLessonViewModel viewModel) =>
|
||||
SizedBox(
|
||||
width: 125,
|
||||
child: _buildPracticeButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildPracticeButton(LearnLessonViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 15,
|
||||
text: 'Practice',
|
||||
borderRadius: 12,
|
||||
onTap: onPracticeTap,
|
||||
trailingIcon: Icons.mic,
|
||||
width: double.maxFinite,
|
||||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
);
|
||||
|
||||
Widget _buildLessonButtonWrapper(LearnLessonViewModel viewModel) => SizedBox(
|
||||
width: 125,
|
||||
child: _buildLessonButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildLessonButton(LearnLessonViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 15,
|
||||
text: 'Start',
|
||||
borderRadius: 12,
|
||||
|
|
|
|||
|
|
@ -1,98 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_level/learn_level_viewmodel.dart';
|
||||
import 'package:yimaru_app/ui/widgets/progress_status.dart';
|
||||
|
||||
import '../../models/level.dart';
|
||||
import '../common/app_colors.dart';
|
||||
import '../common/ui_helpers.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
class LearnLevelTile extends ViewModelWidget<LearnLevelViewModel> {
|
||||
final Level level;
|
||||
final GestureTapCallback? onTap;
|
||||
|
||||
const LearnLevelTile({
|
||||
super.key,
|
||||
this.onTap,
|
||||
required this.level,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnLevelViewModel viewModel) =>
|
||||
_buildExpansionTileCard(viewModel);
|
||||
|
||||
Widget _buildExpansionTileCard(LearnLevelViewModel viewModel) => Container(
|
||||
margin: const EdgeInsets.only(bottom: 15),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(
|
||||
color: kcPrimaryColor.withOpacity(0.2),
|
||||
// color:
|
||||
// current ? kcPrimaryColor.withOpacity(0.2) : kcVeryLightGrey,
|
||||
),
|
||||
),
|
||||
child: _buildExpansionTile(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildExpansionTile(LearnLevelViewModel viewModel) => ExpansionTile(
|
||||
enabled: true,
|
||||
textColor: kcDarkGrey,
|
||||
showTrailingIcon: true,
|
||||
title: _buildTitleRow(),
|
||||
initiallyExpanded: false,
|
||||
collapsedIconColor: kcDarkGrey,
|
||||
collapsedTextColor: kcDarkGrey,
|
||||
shape: Border.all(color: kcTransparent),
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
collapsedBackgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
childrenPadding: const EdgeInsets.only(left: 15, right: 15, bottom: 15),
|
||||
|
||||
//enabled: current,
|
||||
// showTrailingIcon: current,
|
||||
//initiallyExpanded: current,
|
||||
// collapsedBackgroundColor:
|
||||
// current ? kcPrimaryColor.withOpacity(0.1) : kcBackgroundColor,
|
||||
// backgroundColor:
|
||||
// current ? kcPrimaryColor.withOpacity(0.1) : kcBackgroundColor,
|
||||
children: _buildExpansionTileChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildExpansionTileChildren(LearnLevelViewModel viewModel) =>
|
||||
[_buildViewButton(viewModel)];
|
||||
|
||||
Widget _buildTitleRow() => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _buildTitleChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildTitleChildren() => [
|
||||
_buildTitle(),
|
||||
// if (current) horizontalSpaceSmall,
|
||||
// if (current) _buildProgressStatus()
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
level.title ?? '',
|
||||
style: style16P600,
|
||||
);
|
||||
|
||||
Widget _buildProgressStatus() => const ProgressStatus(
|
||||
color: kcPrimaryColor,
|
||||
status: 'Current Level',
|
||||
);
|
||||
|
||||
Widget _buildViewButton(LearnLevelViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 15,
|
||||
onTap: onTap,
|
||||
borderRadius: 12,
|
||||
text: 'View Level',
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
);
|
||||
}
|
||||
|
|
@ -1,19 +1,21 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/learn_module.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_module/learn_module_viewmodel.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart';
|
||||
import 'package:yimaru_app/ui/widgets/finish_practice_sheet.dart';
|
||||
|
||||
import '../../models/module.dart';
|
||||
import '../common/app_colors.dart';
|
||||
import '../common/ui_helpers.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
||||
final Module module;
|
||||
final LearnModule module;
|
||||
final GestureTapCallback? onModuleTap;
|
||||
final GestureTapCallback? onPracticeTap;
|
||||
|
||||
const LearnModuleTile({super.key, this.onModuleTap, required this.module});
|
||||
const LearnModuleTile(
|
||||
{super.key, this.onModuleTap, this.onPracticeTap, required this.module});
|
||||
|
||||
Future<void> _showSheet(
|
||||
{required BuildContext context,
|
||||
|
|
@ -35,7 +37,7 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
margin: const EdgeInsets.only(bottom: 15),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(color: kcVeryLightGrey),
|
||||
border: Border.all(color: kcPrimaryColor.withOpacity(0.1)),
|
||||
),
|
||||
child: _buildTileStack(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
|
@ -55,11 +57,11 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
required LearnModuleViewModel viewModel}) =>
|
||||
ExpansionTile(
|
||||
enabled: true,
|
||||
title: _buildTitle(),
|
||||
textColor: kcDarkGrey,
|
||||
showTrailingIcon: true,
|
||||
initiallyExpanded: true,
|
||||
subtitle: _buildContent(),
|
||||
title: _buildTitleWrapper(),
|
||||
leading: _buildIconWrapper(),
|
||||
collapsedIconColor: kcDarkGrey,
|
||||
collapsedTextColor: kcDarkGrey,
|
||||
|
|
@ -70,8 +72,8 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
childrenPadding: const EdgeInsets.fromLTRB(70, 15, 15, 15),
|
||||
// enabled: status != ProgressStatuses.pending,
|
||||
childrenPadding: const EdgeInsets.fromLTRB(70, 0, 15, 15),
|
||||
// enabled:(module.access?.isAccessible ?? false) ,
|
||||
// showTrailingIcon: status != ProgressStatuses.pending ? true : false,
|
||||
//initiallyExpanded: status == ProgressStatuses.started ? true : false,
|
||||
children:
|
||||
|
|
@ -88,8 +90,13 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
color: kcPrimaryColor,
|
||||
);
|
||||
|
||||
Widget _buildTitleWrapper() => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: _buildTitle(),
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
module.title ?? '',
|
||||
module.name ?? '',
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
style: style16P600,
|
||||
|
|
@ -123,8 +130,8 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
[
|
||||
// _buildProgressRow(),
|
||||
// verticalSpaceSmall,
|
||||
_buildProgressRow(),
|
||||
verticalSpaceSmall,
|
||||
_buildActionButtonWrapper(context: context, viewModel: viewModel)
|
||||
];
|
||||
|
||||
|
|
@ -141,12 +148,12 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildProgressStatus() => const CustomLinearProgressIndicator(
|
||||
progress: 0.75,
|
||||
progress: 0,
|
||||
activeColor: kcPrimaryColor,
|
||||
backgroundColor: kcVeryLightGrey);
|
||||
backgroundColor: kcPrimaryColorLight);
|
||||
|
||||
Widget _buildProgress() => const Text(
|
||||
'2/3',
|
||||
'0/0',
|
||||
style: TextStyle(color: kcDarkGrey),
|
||||
);
|
||||
|
||||
|
|
@ -155,17 +162,14 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
required LearnModuleViewModel viewModel}) =>
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: _buildModuleButton(viewModel),
|
||||
child: _buildActionButtons(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildActionButtons(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
Row(
|
||||
Widget _buildActionButtons(LearnModuleViewModel viewModel) => Row(
|
||||
children: [
|
||||
_buildModuleButtonWrapper(viewModel),
|
||||
horizontalSpaceSmall,
|
||||
_buildPracticeButtonWrapper(context: context, viewModel: viewModel)
|
||||
_buildPracticeButtonWrapper(viewModel)
|
||||
],
|
||||
);
|
||||
|
||||
|
|
@ -183,23 +187,21 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
backgroundColor: kcPrimaryColor,
|
||||
);
|
||||
|
||||
Widget _buildPracticeButtonWrapper(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
Widget _buildPracticeButtonWrapper(LearnModuleViewModel viewModel) =>
|
||||
Expanded(
|
||||
child: Container(),
|
||||
child: _buildPracticeButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildPracticeButton(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
const CustomElevatedButton(
|
||||
Widget _buildPracticeButton(LearnModuleViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 15,
|
||||
borderRadius: 12,
|
||||
text: 'View Practices',
|
||||
onTap: onPracticeTap,
|
||||
text: 'Take Practices',
|
||||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
|
||||
// onTap: () async => await viewModel.navigateToLearnPractice(practices),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,15 @@ import '../common/ui_helpers.dart';
|
|||
import '../views/learn_practice/learn_practice_viewmodel.dart';
|
||||
|
||||
class LearnPracticeAnswerCard extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
final Voice voice;
|
||||
final String title;
|
||||
final StateObjects object;
|
||||
final Map<String, dynamic> answer;
|
||||
|
||||
const LearnPracticeAnswerCard(
|
||||
{super.key, required this.title, required this.object});
|
||||
{super.key,
|
||||
required this.voice,
|
||||
required this.title,
|
||||
required this.answer});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
|
||||
|
|
@ -38,25 +42,20 @@ class LearnPracticeAnswerCard extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
shadowColor: WidgetStatePropertyAll(kcPrimaryColor),
|
||||
backgroundColor: WidgetStatePropertyAll(kcPrimaryColor),
|
||||
),
|
||||
onPressed: () async => viewModel.player.state == PlayerState.playing
|
||||
? viewModel.busyObject == StateObjects.learnPracticeSample
|
||||
? await viewModel.pauseSampleAudio()
|
||||
: await viewModel.pauseRecordedAudio()
|
||||
: object == StateObjects.learnPracticeSample
|
||||
? await viewModel.playSampleAudio()
|
||||
: await viewModel.playRecordedAudio(),
|
||||
onPressed: () async => viewModel.busyObject == answer['busy_object'] &&
|
||||
viewModel.player.state == PlayerState.playing
|
||||
? await viewModel.pauseAudio()
|
||||
: await viewModel.playResult(answer: answer, voice: voice),
|
||||
child: _buildButtonState(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildButtonState(LearnPracticeViewModel viewModel) =>
|
||||
viewModel.busy(object)
|
||||
? viewModel.busyObject == object
|
||||
? _buildPauseIcon()
|
||||
: _buildPlayIcon()
|
||||
: viewModel.busyObject == object &&
|
||||
viewModel.player.state == PlayerState.playing
|
||||
? _buildPauseIcon()
|
||||
: _buildPlayIcon();
|
||||
(viewModel.busy(answer['busy_object']) && viewModel.playing == voice) ||
|
||||
(viewModel.busyObject == answer['busy_object'] &&
|
||||
viewModel.playing == voice &&
|
||||
viewModel.player.state == PlayerState.playing)
|
||||
? _buildPauseIcon()
|
||||
: _buildPlayIcon();
|
||||
|
||||
Widget _buildPlayIcon() => const Icon(
|
||||
Icons.play_arrow_rounded,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/practice.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
|
||||
|
||||
import '../common/app_colors.dart';
|
||||
|
|
@ -8,8 +9,8 @@ import 'custom_elevated_button.dart';
|
|||
|
||||
class LearnPracticeCard extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
final int index;
|
||||
final Practice practice;
|
||||
final GestureTapCallback? onTap;
|
||||
final Map<String, dynamic> practice;
|
||||
|
||||
const LearnPracticeCard(
|
||||
{super.key, this.onTap, required this.index, required this.practice});
|
||||
|
|
@ -57,6 +58,6 @@ class LearnPracticeCard extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
text: 'Practice',
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
onTap: () => viewModel.setPractice(practice),
|
||||
// onTap: () => viewModel.setPractice(practice),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,30 +6,31 @@ import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart
|
|||
import 'package:yimaru_app/ui/widgets/learn_practice_answer_card.dart';
|
||||
|
||||
class LearnPracticeResultCard extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
final Map<String, dynamic> data;
|
||||
const LearnPracticeResultCard({super.key, required this.data});
|
||||
final Map<String, dynamic> answer;
|
||||
|
||||
const LearnPracticeResultCard({super.key, required this.answer});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
|
||||
_buildColumnWrapper();
|
||||
_buildColumnWrapper(viewModel);
|
||||
|
||||
Widget _buildColumnWrapper() => SizedBox(
|
||||
Widget _buildColumnWrapper(LearnPracticeViewModel viewModel) => SizedBox(
|
||||
height: 100,
|
||||
width: double.maxFinite,
|
||||
child: _buildColumn(),
|
||||
child: _buildColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn() => Column(
|
||||
Widget _buildColumn(LearnPracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildColumnChildren(),
|
||||
children: _buildColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildColumnChildren() =>
|
||||
[_buildQuestion(), verticalSpaceSmall, _buildRow()];
|
||||
List<Widget> _buildColumnChildren(LearnPracticeViewModel viewModel) =>
|
||||
[_buildQuestion(viewModel), verticalSpaceSmall, _buildRow()];
|
||||
|
||||
Widget _buildQuestion() => Text(
|
||||
data['question_text'],
|
||||
Widget _buildQuestion(LearnPracticeViewModel viewModel) => Text(
|
||||
answer['sample_text_answer'],
|
||||
style: style14DG400,
|
||||
);
|
||||
|
||||
|
|
@ -46,16 +47,18 @@ class LearnPracticeResultCard extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
Widget _buildSampleResponseWrapper() =>
|
||||
Expanded(child: _buildSampleResponse());
|
||||
|
||||
Widget _buildSampleResponse() => const LearnPracticeAnswerCard(
|
||||
Widget _buildSampleResponse() => LearnPracticeAnswerCard(
|
||||
answer: answer,
|
||||
voice: Voice.sample,
|
||||
title: 'Sample Answer',
|
||||
object: StateObjects.learnPracticeSample,
|
||||
);
|
||||
|
||||
Widget _buildActualResponseWrapper() =>
|
||||
Expanded(child: _buildActualResponse());
|
||||
|
||||
Widget _buildActualResponse() => const LearnPracticeAnswerCard(
|
||||
Widget _buildActualResponse() => LearnPracticeAnswerCard(
|
||||
answer: answer,
|
||||
title: 'Your Answer',
|
||||
object: StateObjects.learnPracticeAnswer,
|
||||
voice: Voice.recorded,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,39 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
|
||||
import 'package:yimaru_app/ui/widgets/learn_practice_result_card.dart';
|
||||
|
||||
import '../common/app_colors.dart';
|
||||
|
||||
class LearnPracticeResultsWrapper extends StatelessWidget {
|
||||
final Map<String, dynamic> data;
|
||||
|
||||
const LearnPracticeResultsWrapper({super.key, required this.data});
|
||||
class LearnPracticeResultsWrapper
|
||||
extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
const LearnPracticeResultsWrapper({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildContainer();
|
||||
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
|
||||
_buildContainer(viewModel);
|
||||
|
||||
Widget _buildContainer() => Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 25),
|
||||
Widget _buildContainer(LearnPracticeViewModel viewModel) => Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: kcPrimaryColor.withOpacity(0.1),
|
||||
),
|
||||
child: _buildColumn(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 25),
|
||||
child: _buildColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn() => Column(
|
||||
Widget _buildColumn(LearnPracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildColumnChildren(),
|
||||
children: _buildColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildColumnChildren() =>
|
||||
[_buildTitle(), verticalSpaceSmall, if (data.isNotEmpty) _buildResult()];
|
||||
List<Widget> _buildColumnChildren(LearnPracticeViewModel viewModel) => [
|
||||
_buildTitle(),
|
||||
verticalSpaceSmall,
|
||||
_buildResults(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
'Conversation Review',
|
||||
|
|
@ -36,5 +41,15 @@ class LearnPracticeResultsWrapper extends StatelessWidget {
|
|||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildResult() => LearnPracticeResultCard(data: data);
|
||||
Widget _buildResults(LearnPracticeViewModel viewModel) => ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.answers.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
separatorBuilder: (context, index) => verticalSpaceSmall,
|
||||
itemBuilder: (context, index) => _buildResult( viewModel.answers[index]),
|
||||
);
|
||||
|
||||
|
||||
|
||||
Widget _buildResult(Map<String,dynamic> answer) => LearnPracticeResultCard(answer: answer,);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,34 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
|
||||
|
||||
class LearnPracticeTipSection extends StatelessWidget {
|
||||
import '../../models/learn_practice.dart';
|
||||
|
||||
class LearnPracticeTipSection extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
const LearnPracticeTipSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildContainer();
|
||||
Widget build(BuildContext context,LearnPracticeViewModel viewModel) => _buildContainer(viewModel);
|
||||
|
||||
Widget _buildContainer() => Container(
|
||||
Widget _buildContainer(LearnPracticeViewModel viewModel) => Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 25),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: kcBlue.withOpacity(0.1),
|
||||
),
|
||||
child: _buildColumn(),
|
||||
child: _buildColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn() => Column(
|
||||
Widget _buildColumn(LearnPracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildColumnChildren(),
|
||||
children: _buildColumnChildren( viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildColumnChildren() =>
|
||||
[_buildTitleWrapper(), verticalSpaceTiny, _buildContent()];
|
||||
List<Widget> _buildColumnChildren(LearnPracticeViewModel viewModel) =>
|
||||
[_buildTitleWrapper(), verticalSpaceTiny, _buildContent(viewModel)];
|
||||
|
||||
Widget _buildTitleWrapper() => Row(
|
||||
children: [_buildLeading(), horizontalSpaceSmall, _buildTitle()],
|
||||
|
|
@ -40,8 +44,8 @@ class LearnPracticeTipSection extends StatelessWidget {
|
|||
style: style16B600,
|
||||
);
|
||||
|
||||
Widget _buildContent() => Text(
|
||||
"You can always do better!\nSpeak in full sentences instead of short phrases.\nMaintain a steady pace, not too fast, not too slow.\nUse varied vocabulary to make your answers richer.\nPause naturally instead of using fillers like “um” or “uh”.",
|
||||
Widget _buildContent(LearnPracticeViewModel viewModel) => Text(
|
||||
viewModel.practices.firstOrNull?.quickTips ?? '',
|
||||
style: style14B400,
|
||||
textAlign: TextAlign.start,
|
||||
);
|
||||
|
|
|
|||
129
lib/ui/widgets/learn_program_tile.dart
Normal file
129
lib/ui/widgets/learn_program_tile.dart
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/progress_status.dart';
|
||||
|
||||
import '../../models/learn_program.dart';
|
||||
import '../common/app_colors.dart';
|
||||
import '../views/learn_program/learn_program_viewmodel.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
class LearnProgramTile extends ViewModelWidget<LearnProgramViewModel> {
|
||||
final LearnProgram program;
|
||||
final GestureTapCallback? onTap;
|
||||
|
||||
const LearnProgramTile({super.key, this.onTap, required this.program});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnProgramViewModel viewModel) =>
|
||||
_buildExpansionTileCard(viewModel);
|
||||
|
||||
Widget _buildExpansionTileCard(LearnProgramViewModel viewModel) => Container(
|
||||
margin: const EdgeInsets.only(bottom: 15),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(color: kcPrimaryColor.withOpacity(0.1)),
|
||||
),
|
||||
child: _buildExpansionTile(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildExpansionTile(LearnProgramViewModel viewModel) => ExpansionTile(
|
||||
textColor: kcDarkGrey,
|
||||
subtitle: _buildContent(),
|
||||
title: _buildTitleWrapper(),
|
||||
collapsedIconColor: kcDarkGrey,
|
||||
collapsedTextColor: kcDarkGrey,
|
||||
shape: Border.all(color: kcTransparent),
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
enabled: (program.access?.isAccessible ?? false),
|
||||
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
tilePadding: const EdgeInsets.fromLTRB(15, 0, 15, 5),
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
showTrailingIcon: (program.access?.isAccessible ?? false),
|
||||
initiallyExpanded: (program.access?.isAccessible ?? false),
|
||||
collapsedBackgroundColor: (program.access?.isAccessible ?? false)
|
||||
? kcPrimaryColor.withOpacity(0.1)
|
||||
: kcBackgroundColor,
|
||||
children: _buildExpansionTileChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildExpansionTileChildren(LearnProgramViewModel viewModel) => [
|
||||
_buildDivider(),
|
||||
verticalSpaceSmall,
|
||||
_buildActionButtonWrapper(viewModel),
|
||||
verticalSpaceMedium,
|
||||
];
|
||||
|
||||
Widget _buildTitleWrapper() => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: _buildTitleState(),
|
||||
);
|
||||
|
||||
Widget _buildTitleState() => (program.access?.isAccessible ?? false)
|
||||
? _buildActiveTitleWrapper()
|
||||
: _buildInactiveTitleWrapper();
|
||||
|
||||
Widget _buildInactiveTitleWrapper() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildInactiveTitleChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildInactiveTitleChildren() => [
|
||||
_buildLockIcon(),
|
||||
verticalSpaceSmall,
|
||||
_buildTitle(),
|
||||
];
|
||||
|
||||
Widget _buildLockIcon() => const Icon(
|
||||
Icons.lock_outline_rounded,
|
||||
color: kcLightGrey,
|
||||
);
|
||||
|
||||
Widget _buildActiveTitleWrapper() => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _buildActiveTitleChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildActiveTitleChildren() =>
|
||||
[_buildTitle(), horizontalSpaceSmall, _buildProgressStatus()];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
program.name ?? '',
|
||||
style: (program.access?.isAccessible ?? false)
|
||||
? style16P600
|
||||
: style16DG600,
|
||||
);
|
||||
|
||||
Widget _buildProgressStatus() => ProgressStatus(
|
||||
color: kcPrimaryColor,
|
||||
status: (program.access?.isCompleted ?? false)
|
||||
? 'Completed'
|
||||
: 'In Progress',
|
||||
);
|
||||
|
||||
Widget _buildContent() => Text(
|
||||
program.description ?? '',
|
||||
style: style14DG400,
|
||||
);
|
||||
|
||||
Widget _buildDivider() => const Divider(color: kcVeryLightGrey);
|
||||
|
||||
Widget _buildActionButtonWrapper(LearnProgramViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildActionButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildActionButton(LearnProgramViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 15,
|
||||
onTap: onTap,
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
text: program.access?.progressPercent == 0
|
||||
? 'Start Learning'
|
||||
: 'Continue Learning',
|
||||
);
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:yimaru_app/models/subcategory.dart';
|
||||
|
||||
import '../common/app_colors.dart';
|
||||
import '../common/app_strings.dart';
|
||||
import '../common/helper_functions.dart';
|
||||
import '../common/ui_helpers.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
class LearnSubcategoryCard extends StatelessWidget {
|
||||
final Subcategory subcategory;
|
||||
final GestureTapCallback? onTap;
|
||||
|
||||
const LearnSubcategoryCard(
|
||||
{super.key, this.onTap, required this.subcategory});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildContainer();
|
||||
|
||||
Widget _buildContainer() => Container(
|
||||
height: 200,
|
||||
padding: const EdgeInsets.all(15),
|
||||
decoration: BoxDecoration(
|
||||
color: getColor(),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: _buildColumn(),
|
||||
);
|
||||
|
||||
Widget _buildColumn() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildColumnChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildColumnChildren() => [
|
||||
_buildTitle(),
|
||||
verticalSpaceTiny,
|
||||
_buildSubtitle(),
|
||||
verticalSpaceMedium,
|
||||
__buildStartButtonWrapper(),
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
subcategory.name ?? '',
|
||||
style: style18DG700,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
subcategory.description ?? ksCategorySubtitle,
|
||||
maxLines: 3,
|
||||
style: style16DG400,
|
||||
);
|
||||
|
||||
Widget __buildStartButtonWrapper() => SizedBox(
|
||||
height: 40,
|
||||
child: _buildStartButton(),
|
||||
);
|
||||
|
||||
Widget _buildStartButton() => CustomElevatedButton(
|
||||
height: 50,
|
||||
width: 200,
|
||||
onTap: onTap,
|
||||
borderRadius: 12,
|
||||
text: 'Select Course',
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
);
|
||||
}
|
||||
|
|
@ -1,237 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/submodule.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart';
|
||||
import 'package:yimaru_app/ui/widgets/finish_practice_sheet.dart';
|
||||
|
||||
import '../common/app_colors.dart';
|
||||
import '../common/ui_helpers.dart';
|
||||
import '../views/learn_submodule/learn_submodule_viewmodel.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
class LearnSubmoduleTile extends ViewModelWidget<LearnSubmoduleViewModel> {
|
||||
final Submodule submodule;
|
||||
final GestureTapCallback? onLessonTap;
|
||||
final GestureTapCallback? onPracticeTap;
|
||||
|
||||
const LearnSubmoduleTile(
|
||||
{super.key,
|
||||
this.onLessonTap,
|
||||
this.onPracticeTap,
|
||||
required this.submodule});
|
||||
|
||||
Future<void> _showSheet(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) async =>
|
||||
await showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: kcTransparent,
|
||||
builder: (_) => _buildSheet(viewModel),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnSubmoduleViewModel viewModel) =>
|
||||
_buildExpansionTileCard(context: context, viewModel: viewModel);
|
||||
|
||||
Widget _buildExpansionTileCard(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
Container(
|
||||
margin: const EdgeInsets.only(bottom: 15),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(color: kcVeryLightGrey),
|
||||
),
|
||||
child: _buildTileStack(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildTileStack(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
Stack(
|
||||
children: [
|
||||
_buildExpansionTile(context: context, viewModel: viewModel),
|
||||
// _buildContainerShaderState()
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildExpansionTile(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
ExpansionTile(
|
||||
enabled: true,
|
||||
title: _buildTitle(),
|
||||
textColor: kcDarkGrey,
|
||||
showTrailingIcon: true,
|
||||
initiallyExpanded: true,
|
||||
subtitle: _buildContent(),
|
||||
leading: _buildIconWrapper(),
|
||||
collapsedIconColor: kcDarkGrey,
|
||||
collapsedTextColor: kcDarkGrey,
|
||||
backgroundColor: kcBackgroundColor,
|
||||
shape: Border.all(color: kcTransparent),
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
collapsedBackgroundColor: kcBackgroundColor,
|
||||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
childrenPadding: const EdgeInsets.fromLTRB(70, 15, 15, 15),
|
||||
// enabled: status != ProgressStatuses.pending,
|
||||
// showTrailingIcon: status != ProgressStatuses.pending ? true : false,
|
||||
//initiallyExpanded: status == ProgressStatuses.started ? true : false,
|
||||
children:
|
||||
_buildExpansionTileChildren(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildIconWrapper() => CircleAvatar(
|
||||
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
child: _buildIcon(),
|
||||
);
|
||||
|
||||
Widget _buildIcon() => const Icon(
|
||||
Icons.lightbulb_outline,
|
||||
color: kcPrimaryColor,
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
submodule.title ?? '',
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
style: style16P600,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
|
||||
Widget _buildContent() => Text(
|
||||
submodule.description ?? '',
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
style: style14DG400,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
|
||||
List<Widget> _buildExpansionTileChildren(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
[_buildExpansionTileItem(context: context, viewModel: viewModel)];
|
||||
|
||||
Widget _buildExpansionTileItem(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildExpansionTileItemChildren(
|
||||
context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildExpansionTileItemChildren(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
[
|
||||
// _buildProgressRow(),
|
||||
// verticalSpaceSmall,
|
||||
_buildActionButtonWrapper(context: context, viewModel: viewModel)
|
||||
];
|
||||
|
||||
Widget _buildProgressRow() => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _buildProgressChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildProgressChildren() =>
|
||||
[_buildProgressStatusWrapper(), horizontalSpaceSmall, _buildProgress()];
|
||||
|
||||
Widget _buildProgressStatusWrapper() => Expanded(
|
||||
child: _buildProgressStatus(),
|
||||
);
|
||||
|
||||
Widget _buildProgressStatus() => const CustomLinearProgressIndicator(
|
||||
progress: 0.75,
|
||||
activeColor: kcPrimaryColor,
|
||||
backgroundColor: kcVeryLightGrey);
|
||||
|
||||
Widget _buildProgress() => const Text(
|
||||
'2/3',
|
||||
style: TextStyle(color: kcDarkGrey),
|
||||
);
|
||||
|
||||
Widget _buildActionButtonWrapper(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: _buildActionButtons(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildActionButtons(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
Row(
|
||||
children: [
|
||||
_buildLessonButtonWrapper(viewModel),
|
||||
horizontalSpaceSmall,
|
||||
_buildPracticeButtonWrapper(context: context, viewModel: viewModel)
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildLessonButtonWrapper(LearnSubmoduleViewModel viewModel) =>
|
||||
Expanded(
|
||||
child: _buildLessonButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildLessonButton(LearnSubmoduleViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 15,
|
||||
borderRadius: 12,
|
||||
onTap: onLessonTap,
|
||||
text: 'View Module',
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
// onTap: () async => await viewModel.navigateToLearnLesson(
|
||||
// title: title,
|
||||
// topics: topics,
|
||||
// subtitle: subtitle,
|
||||
// practices: practices,
|
||||
// description: description),
|
||||
);
|
||||
|
||||
Widget _buildPracticeButtonWrapper(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
Expanded(
|
||||
child: _buildPracticeButton(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildPracticeButton(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
CustomElevatedButton(
|
||||
height: 15,
|
||||
borderRadius: 12,
|
||||
onTap: onPracticeTap,
|
||||
text: 'View Practices',
|
||||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
// onTap: () async => await viewModel.navigateToLearnPractice(practices),
|
||||
);
|
||||
|
||||
Widget _buildSheet(LearnSubmoduleViewModel viewModel) => FinishPracticeSheet(
|
||||
onTap: viewModel.pop,
|
||||
);
|
||||
|
||||
// Widget _buildContainerShaderState() => status == ProgressStatuses.pending
|
||||
// ? _buildContainerShaderWrapper()
|
||||
// : Container();
|
||||
|
||||
Widget _buildContainerShaderWrapper() => Positioned.fill(
|
||||
child: _buildContainerShader(),
|
||||
);
|
||||
|
||||
Widget _buildContainerShader() => Container(
|
||||
decoration: BoxDecoration(
|
||||
color: kcWhite.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/course.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/views/learn/learn_viewmodel.dart';
|
||||
|
||||
import '../common/app_colors.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
class LearnTile extends ViewModelWidget<LearnViewModel> {
|
||||
final Course course;
|
||||
final GestureTapCallback? onTap;
|
||||
|
||||
const LearnTile({
|
||||
super.key,
|
||||
this.onTap,
|
||||
required this.course,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnViewModel viewModel) =>
|
||||
_buildExpansionTileCard(viewModel);
|
||||
|
||||
Widget _buildExpansionTileCard(LearnViewModel viewModel) => Container(
|
||||
margin: const EdgeInsets.only(bottom: 15),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(color: kcPrimaryColor.withOpacity(0.2)
|
||||
// color: status == ProgressStatuses.started
|
||||
// ? kcPrimaryColor.withOpacity(0.2)
|
||||
// : kcVeryLightGrey,
|
||||
),
|
||||
),
|
||||
child: _buildExpansionTile(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildExpansionTile(LearnViewModel viewModel) => ExpansionTile(
|
||||
enabled: true,
|
||||
textColor: kcDarkGrey,
|
||||
showTrailingIcon: true,
|
||||
title: _buildTitleRow(),
|
||||
initiallyExpanded: false,
|
||||
subtitle: _buildContent(),
|
||||
collapsedIconColor: kcDarkGrey,
|
||||
collapsedTextColor: kcDarkGrey,
|
||||
shape: Border.all(color: kcTransparent),
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
childrenPadding: const EdgeInsets.only(bottom: 15),
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
collapsedBackgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
// enabled: status != ProgressStatuses.pending ? true : false,
|
||||
// status != ProgressStatuses.pending
|
||||
// ? kcPrimaryColor.withOpacity(0.1)
|
||||
// : kcBackgroundColor,
|
||||
// collapsedBackgroundColor: status != ProgressStatuses.pending
|
||||
// ? kcPrimaryColor.withOpacity(0.1)
|
||||
// : kcBackgroundColor,
|
||||
// showTrailingIcon: status != ProgressStatuses.pending ? true : false,
|
||||
// initiallyExpanded: status == ProgressStatuses.started ? true : false,
|
||||
children: _buildExpansionTileChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildExpansionTileChildren(LearnViewModel viewModel) => [
|
||||
_buildDivider(),
|
||||
verticalSpaceTiny,
|
||||
_buildActionButtonWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildTitleRow() => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _buildTitleChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildTitleChildren() => [
|
||||
_buildTitle(),
|
||||
// if (status != ProgressStatuses.pending) horizontalSpaceSmall,
|
||||
// if (status != ProgressStatuses.pending) _buildProgressStatus()
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
course.title ?? '',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
color: kcPrimaryColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
);
|
||||
|
||||
// Widget _buildProgressStatus() => ProgressStatus(
|
||||
// status: status.name.substring(0, 1).toUpperCase() +
|
||||
// status.name.substring(1, status.name.length),
|
||||
// color: kcPrimaryColor,
|
||||
// );
|
||||
|
||||
Widget _buildContent() => Text(
|
||||
course.description ?? '',
|
||||
style: style14DG400,
|
||||
);
|
||||
|
||||
Widget _buildDivider() => const Divider(color: kcVeryLightGrey);
|
||||
|
||||
Widget _buildActionButtonWrapper(LearnViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildActionButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildActionButton(LearnViewModel viewModel) => CustomElevatedButton(
|
||||
height: 15,
|
||||
onTap: onTap,
|
||||
borderRadius: 12,
|
||||
text: 'Start Course',
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
// text: status == ProgressStatuses.completed
|
||||
// ? 'Review Course'
|
||||
// : status == ProgressStatuses.pending
|
||||
// ? 'Start Learning'
|
||||
// : 'Continue Learning',
|
||||
);
|
||||
}
|
||||
|
|
@ -4,16 +4,20 @@ import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
|||
import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart';
|
||||
|
||||
class OverallLearnProgress extends StatelessWidget {
|
||||
final Color color;
|
||||
const OverallLearnProgress({super.key, required this.color});
|
||||
final Color backgroundColor;
|
||||
final Color indicatorBackgroundColor;
|
||||
const OverallLearnProgress(
|
||||
{super.key,
|
||||
required this.backgroundColor,
|
||||
required this.indicatorBackgroundColor});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildContainer();
|
||||
|
||||
Widget _buildContainer() => Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 25),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: _buildProgressSection(),
|
||||
|
|
@ -51,10 +55,10 @@ class OverallLearnProgress extends StatelessWidget {
|
|||
style: style14P400,
|
||||
);
|
||||
|
||||
Widget _buildProgressIndicator() => const CustomLinearProgressIndicator(
|
||||
Widget _buildProgressIndicator() => CustomLinearProgressIndicator(
|
||||
progress: 0.0,
|
||||
activeColor: kcPrimaryColor,
|
||||
backgroundColor: kcVeryLightGrey,
|
||||
backgroundColor: indicatorBackgroundColor,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => const Text(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
|
||||
class ProgressStatus extends StatelessWidget {
|
||||
final Color color;
|
||||
|
|
@ -20,10 +21,6 @@ class ProgressStatus extends StatelessWidget {
|
|||
|
||||
Widget _buildLabel() => Text(
|
||||
status,
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
style: style12RP600.copyWith(color: color),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ class SelectableCoursePracticeQuestion
|
|||
index + 1 < viewModel.coursePracticeQuestions.length
|
||||
? index + 1
|
||||
: index]
|
||||
.questionId ??
|
||||
.id ??
|
||||
0)
|
||||
: null,
|
||||
);
|
||||
|
|
|
|||
92
pubspec.lock
92
pubspec.lock
|
|
@ -5,10 +5,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7"
|
||||
sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "67.0.0"
|
||||
version: "91.0.0"
|
||||
_flutterfire_internals:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -21,10 +21,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d"
|
||||
sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.4.1"
|
||||
version: "8.4.1"
|
||||
ansicolor:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -149,18 +149,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||
sha256: aadd943f4f8cc946882c954c187e6115a84c98c81ad1d9c6cbf0895a8c85da9c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
version: "4.0.5"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
|
||||
sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
version: "1.3.0"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -169,30 +169,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.1"
|
||||
build_resolvers:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
|
||||
sha256: "4425a87d87d0d1303540f867994303f5b141ad2f6ecac7ac2cf8851f41c0cef1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.13"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.3.2"
|
||||
version: "2.14.0"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -357,10 +341,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
|
||||
sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.6"
|
||||
version: "3.1.3"
|
||||
dbus:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -772,18 +756,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: freezed_annotation
|
||||
sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2
|
||||
sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.4"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
version: "3.1.0"
|
||||
get:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1032,14 +1008,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.2"
|
||||
json_annotation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1052,10 +1020,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: json_serializable
|
||||
sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b
|
||||
sha256: c5b2ee75210a0f263c6c7b9eeea80553dbae96ea1bf57f02484e806a3ffdffa3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.8.0"
|
||||
version: "6.11.2"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1100,10 +1068,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: logger
|
||||
sha256: "7ad7215c15420a102ec687bb320a7312afd449bac63bfb1c60d9787c27b9767f"
|
||||
sha256: "25aee487596a6257655a1e091ec2ae66bc30e7af663592cc3a27e6591e05035c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "2.7.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1148,10 +1116,10 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: mockito
|
||||
sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917"
|
||||
sha256: eff30d002f0c8bf073b6f929df4483b543133fcafce056870163587b03f1d422
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.4.4"
|
||||
version: "5.6.4"
|
||||
native_toolchain_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1529,18 +1497,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||
sha256: "732792cfd197d2161a65bb029606a46e0a18ff30ef9e141a7a82172b05ea8ecd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
version: "4.2.2"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
|
||||
sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.5"
|
||||
version: "1.3.8"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1609,10 +1577,10 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: stacked_generator
|
||||
sha256: eaa6447e3fd4d4010b746629b5518364d7fa7f6453ffb6416ad449fd352d1181
|
||||
sha256: "251344e41a090226aeaca5d174882a7a6e4ff834feed6c9520ebee78850164a6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.1"
|
||||
version: "1.6.2"
|
||||
stacked_services:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1693,14 +1661,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.1"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timing
|
||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
toastification:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
name: yimaru_app
|
||||
description: A new Flutter project.
|
||||
version: 0.1.4+6
|
||||
publish_to: 'none'
|
||||
version: 0.1.3+5
|
||||
description: A new Flutter project.
|
||||
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.3 <4.0.0'
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import 'package:yimaru_app/services/smart_auth_service.dart';
|
|||
import 'package:yimaru_app/services/course_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/in_app_update_service.dart';
|
||||
// @stacked-import
|
||||
|
||||
import 'test_helpers.mocks.dart';
|
||||
|
|
@ -42,6 +43,7 @@ import 'test_helpers.mocks.dart';
|
|||
MockSpec<CourseService>(onMissingStub: OnMissingStub.returnDefault),
|
||||
MockSpec<AudioPlayerService>(onMissingStub: OnMissingStub.returnDefault),
|
||||
MockSpec<VoiceRecorderService>(onMissingStub: OnMissingStub.returnDefault),
|
||||
MockSpec<InAppUpdateService>(onMissingStub: OnMissingStub.returnDefault),
|
||||
// @stacked-mock-spec
|
||||
],
|
||||
)
|
||||
|
|
@ -63,6 +65,7 @@ void registerServices() {
|
|||
getAndRegisterCourseService();
|
||||
getAndRegisterAudioPlayerService();
|
||||
getAndRegisterVoiceRecorderService();
|
||||
getAndRegisterInAppUpdateService();
|
||||
// @stacked-mock-register
|
||||
}
|
||||
|
||||
|
|
@ -217,6 +220,13 @@ MockVoiceRecorderService getAndRegisterVoiceRecorderService() {
|
|||
locator.registerSingleton<VoiceRecorderService>(service);
|
||||
return service;
|
||||
}
|
||||
|
||||
MockInAppUpdateService getAndRegisterInAppUpdateService() {
|
||||
_removeRegistrationIfExists<InAppUpdateService>();
|
||||
final service = MockInAppUpdateService();
|
||||
locator.registerSingleton<InAppUpdateService>(service);
|
||||
return service;
|
||||
}
|
||||
// @stacked-mock-create
|
||||
|
||||
void _removeRegistrationIfExists<T extends Object>() {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Mocks generated by Mockito 5.4.4 from annotations
|
||||
// Mocks generated by Mockito 5.4.6 from annotations
|
||||
// in yimaru_app/test/helpers/test_helpers.dart.
|
||||
// Do not manually edit this file.
|
||||
|
||||
|
|
@ -8,42 +8,48 @@ import 'dart:ui' as _i10;
|
|||
|
||||
import 'package:audioplayers/audioplayers.dart' as _i4;
|
||||
import 'package:dio/dio.dart' as _i2;
|
||||
import 'package:firebase_messaging/firebase_messaging.dart' as _i34;
|
||||
import 'package:firebase_messaging/firebase_messaging.dart' as _i39;
|
||||
import 'package:flutter/material.dart' as _i8;
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
import 'package:mockito/src/dummies.dart' as _i7;
|
||||
import 'package:permission_handler/permission_handler.dart' as _i29;
|
||||
import 'package:permission_handler/permission_handler.dart' as _i34;
|
||||
import 'package:stacked_services/stacked_services.dart' as _i6;
|
||||
import 'package:waveform_recorder/waveform_recorder.dart' as _i5;
|
||||
import 'package:yimaru_app/models/category.dart' as _i15;
|
||||
import 'package:yimaru_app/models/course.dart' as _i21;
|
||||
import 'package:yimaru_app/models/course_detail.dart' as _i37;
|
||||
import 'package:yimaru_app/models/course_lesson.dart' as _i18;
|
||||
import 'package:yimaru_app/models/course_progress.dart' as _i17;
|
||||
import 'package:yimaru_app/models/lesson.dart' as _i25;
|
||||
import 'package:yimaru_app/models/level.dart' as _i22;
|
||||
import 'package:yimaru_app/models/module.dart' as _i23;
|
||||
import 'package:yimaru_app/models/practice.dart' as _i19;
|
||||
import 'package:yimaru_app/models/practice_question.dart' as _i20;
|
||||
import 'package:yimaru_app/models/category.dart' as _i21;
|
||||
import 'package:yimaru_app/models/course.dart' as _i26;
|
||||
import 'package:yimaru_app/models/course_detail.dart' as _i42;
|
||||
import 'package:yimaru_app/models/course_lesson.dart' as _i24;
|
||||
import 'package:yimaru_app/models/course_progress.dart' as _i23;
|
||||
import 'package:yimaru_app/models/learn_course.dart' as _i16;
|
||||
import 'package:yimaru_app/models/learn_lesson.dart' as _i19;
|
||||
import 'package:yimaru_app/models/learn_module.dart' as _i18;
|
||||
import 'package:yimaru_app/models/learn_practice.dart' as _i17;
|
||||
import 'package:yimaru_app/models/learn_program.dart' as _i15;
|
||||
import 'package:yimaru_app/models/learn_question.dart' as _i20;
|
||||
import 'package:yimaru_app/models/lesson.dart' as _i30;
|
||||
import 'package:yimaru_app/models/level.dart' as _i27;
|
||||
import 'package:yimaru_app/models/module.dart' as _i28;
|
||||
import 'package:yimaru_app/models/practice.dart' as _i25;
|
||||
import 'package:yimaru_app/models/question.dart' as _i14;
|
||||
import 'package:yimaru_app/models/subcategory.dart' as _i16;
|
||||
import 'package:yimaru_app/models/submodule.dart' as _i24;
|
||||
import 'package:yimaru_app/models/subcategory.dart' as _i22;
|
||||
import 'package:yimaru_app/models/submodule.dart' as _i29;
|
||||
import 'package:yimaru_app/models/user.dart' as _i12;
|
||||
import 'package:yimaru_app/services/api_service.dart' as _i13;
|
||||
import 'package:yimaru_app/services/audio_player_service.dart' as _i38;
|
||||
import 'package:yimaru_app/services/audio_player_service.dart' as _i43;
|
||||
import 'package:yimaru_app/services/authentication_service.dart' as _i11;
|
||||
import 'package:yimaru_app/services/course_service.dart' as _i36;
|
||||
import 'package:yimaru_app/services/dio_service.dart' as _i26;
|
||||
import 'package:yimaru_app/services/google_auth_service.dart' as _i31;
|
||||
import 'package:yimaru_app/services/image_downloader_service.dart' as _i32;
|
||||
import 'package:yimaru_app/services/image_picker_service.dart' as _i30;
|
||||
import 'package:yimaru_app/services/notification_service.dart' as _i33;
|
||||
import 'package:yimaru_app/services/permission_handler_service.dart' as _i28;
|
||||
import 'package:yimaru_app/services/course_service.dart' as _i41;
|
||||
import 'package:yimaru_app/services/dio_service.dart' as _i31;
|
||||
import 'package:yimaru_app/services/google_auth_service.dart' as _i36;
|
||||
import 'package:yimaru_app/services/image_downloader_service.dart' as _i37;
|
||||
import 'package:yimaru_app/services/image_picker_service.dart' as _i35;
|
||||
import 'package:yimaru_app/services/in_app_update_service.dart' as _i46;
|
||||
import 'package:yimaru_app/services/notification_service.dart' as _i38;
|
||||
import 'package:yimaru_app/services/permission_handler_service.dart' as _i33;
|
||||
import 'package:yimaru_app/services/secure_storage_service.dart' as _i3;
|
||||
import 'package:yimaru_app/services/smart_auth_service.dart' as _i35;
|
||||
import 'package:yimaru_app/services/status_checker_service.dart' as _i27;
|
||||
import 'package:yimaru_app/services/voice_recorder_service.dart' as _i39;
|
||||
import 'package:yimaru_app/ui/common/enmus.dart' as _i40;
|
||||
import 'package:yimaru_app/services/smart_auth_service.dart' as _i40;
|
||||
import 'package:yimaru_app/services/status_checker_service.dart' as _i32;
|
||||
import 'package:yimaru_app/services/voice_recorder_service.dart' as _i44;
|
||||
import 'package:yimaru_app/ui/common/enmus.dart' as _i45;
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: avoid_redundant_argument_values
|
||||
|
|
@ -53,10 +59,12 @@ import 'package:yimaru_app/ui/common/enmus.dart' as _i40;
|
|||
// ignore_for_file: deprecated_member_use_from_same_package
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: invalid_use_of_visible_for_testing_member
|
||||
// ignore_for_file: must_be_immutable
|
||||
// ignore_for_file: prefer_const_constructors
|
||||
// ignore_for_file: unnecessary_parenthesis
|
||||
// ignore_for_file: camel_case_types
|
||||
// ignore_for_file: subtype_of_sealed_class
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
class _FakeDio_0 extends _i1.SmartFake implements _i2.Dio {
|
||||
_FakeDio_0(
|
||||
|
|
@ -171,7 +179,7 @@ class MockNavigationService extends _i1.Mock implements _i6.NavigationService {
|
|||
_i9.Future<T?>? navigateWithTransition<T>(
|
||||
_i8.Widget? page, {
|
||||
bool? opaque,
|
||||
String? transition = r'',
|
||||
String? transition = '',
|
||||
Duration? duration,
|
||||
bool? popGesture,
|
||||
int? id,
|
||||
|
|
@ -207,7 +215,7 @@ class MockNavigationService extends _i1.Mock implements _i6.NavigationService {
|
|||
_i9.Future<T?>? replaceWithTransition<T>(
|
||||
_i8.Widget? page, {
|
||||
bool? opaque,
|
||||
String? transition = r'',
|
||||
String? transition = '',
|
||||
Duration? duration,
|
||||
bool? popGesture,
|
||||
int? id,
|
||||
|
|
@ -480,7 +488,7 @@ class MockBottomSheetService extends _i1.Mock
|
|||
_i9.Future<_i6.SheetResponse<dynamic>?> showBottomSheet({
|
||||
required String? title,
|
||||
String? description,
|
||||
String? confirmButtonTitle = r'Ok',
|
||||
String? confirmButtonTitle = 'Ok',
|
||||
String? cancelButtonTitle,
|
||||
bool? enableDrag = true,
|
||||
bool? barrierDismissible = true,
|
||||
|
|
@ -533,7 +541,7 @@ class MockBottomSheetService extends _i1.Mock
|
|||
double? elevation = 1.0,
|
||||
bool? barrierDismissible = true,
|
||||
bool? isScrollControlled = false,
|
||||
String? barrierLabel = r'',
|
||||
String? barrierLabel = '',
|
||||
dynamic customData,
|
||||
R? data,
|
||||
bool? enableDrag = true,
|
||||
|
|
@ -630,7 +638,7 @@ class MockDialogService extends _i1.Mock implements _i6.DialogService {
|
|||
String? description,
|
||||
String? cancelTitle,
|
||||
_i10.Color? cancelTitleColor,
|
||||
String? buttonTitle = r'Ok',
|
||||
String? buttonTitle = 'Ok',
|
||||
_i10.Color? buttonTitleColor,
|
||||
bool? barrierDismissible = false,
|
||||
_i8.RouteSettings? routeSettings,
|
||||
|
|
@ -675,7 +683,7 @@ class MockDialogService extends _i1.Mock implements _i6.DialogService {
|
|||
bool? takesInput = false,
|
||||
_i10.Color? barrierColor = const _i10.Color(2315255808),
|
||||
bool? barrierDismissible = false,
|
||||
String? barrierLabel = r'',
|
||||
String? barrierLabel = '',
|
||||
bool? useSafeArea = true,
|
||||
_i8.RouteSettings? routeSettings,
|
||||
_i8.GlobalKey<_i8.NavigatorState>? navigatorKey,
|
||||
|
|
@ -719,9 +727,9 @@ class MockDialogService extends _i1.Mock implements _i6.DialogService {
|
|||
_i9.Future<_i6.DialogResponse<dynamic>?> showConfirmationDialog({
|
||||
String? title,
|
||||
String? description,
|
||||
String? cancelTitle = r'Cancel',
|
||||
String? cancelTitle = 'Cancel',
|
||||
_i10.Color? cancelTitleColor,
|
||||
String? confirmationTitle = r'Ok',
|
||||
String? confirmationTitle = 'Ok',
|
||||
_i10.Color? confirmationTitleColor,
|
||||
bool? barrierDismissible = false,
|
||||
_i8.RouteSettings? routeSettings,
|
||||
|
|
@ -1128,54 +1136,157 @@ class MockApiService extends _i1.Mock implements _i13.ApiService {
|
|||
) as _i9.Future<List<_i14.Question>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i15.Category>> getCategories() => (super.noSuchMethod(
|
||||
_i9.Future<List<_i15.LearnProgram>> getLearnPrograms() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getLearnPrograms,
|
||||
[],
|
||||
),
|
||||
returnValue:
|
||||
_i9.Future<List<_i15.LearnProgram>>.value(<_i15.LearnProgram>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i15.LearnProgram>>.value(<_i15.LearnProgram>[]),
|
||||
) as _i9.Future<List<_i15.LearnProgram>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i16.LearnCourse>> getLearnCourse(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getLearnCourse,
|
||||
[id],
|
||||
),
|
||||
returnValue:
|
||||
_i9.Future<List<_i16.LearnCourse>>.value(<_i16.LearnCourse>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i16.LearnCourse>>.value(<_i16.LearnCourse>[]),
|
||||
) as _i9.Future<List<_i16.LearnCourse>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i17.LearnPractice>> getLearnCoursePractices(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getLearnCoursePractices,
|
||||
[id],
|
||||
),
|
||||
returnValue:
|
||||
_i9.Future<List<_i17.LearnPractice>>.value(<_i17.LearnPractice>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i17.LearnPractice>>.value(<_i17.LearnPractice>[]),
|
||||
) as _i9.Future<List<_i17.LearnPractice>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i18.LearnModule>> getLearnModules(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getLearnModules,
|
||||
[id],
|
||||
),
|
||||
returnValue:
|
||||
_i9.Future<List<_i18.LearnModule>>.value(<_i18.LearnModule>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i18.LearnModule>>.value(<_i18.LearnModule>[]),
|
||||
) as _i9.Future<List<_i18.LearnModule>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i17.LearnPractice>> getLearnModulePractices(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getLearnModulePractices,
|
||||
[id],
|
||||
),
|
||||
returnValue:
|
||||
_i9.Future<List<_i17.LearnPractice>>.value(<_i17.LearnPractice>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i17.LearnPractice>>.value(<_i17.LearnPractice>[]),
|
||||
) as _i9.Future<List<_i17.LearnPractice>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i19.LearnLesson>> getLearnLessons(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getLearnLessons,
|
||||
[id],
|
||||
),
|
||||
returnValue:
|
||||
_i9.Future<List<_i19.LearnLesson>>.value(<_i19.LearnLesson>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i19.LearnLesson>>.value(<_i19.LearnLesson>[]),
|
||||
) as _i9.Future<List<_i19.LearnLesson>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i17.LearnPractice>> getLearnLessonPractices(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getLearnLessonPractices,
|
||||
[id],
|
||||
),
|
||||
returnValue:
|
||||
_i9.Future<List<_i17.LearnPractice>>.value(<_i17.LearnPractice>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i17.LearnPractice>>.value(<_i17.LearnPractice>[]),
|
||||
) as _i9.Future<List<_i17.LearnPractice>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i20.LearnQuestion>> getLearnQuestions(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getLearnQuestions,
|
||||
[id],
|
||||
),
|
||||
returnValue:
|
||||
_i9.Future<List<_i20.LearnQuestion>>.value(<_i20.LearnQuestion>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i20.LearnQuestion>>.value(<_i20.LearnQuestion>[]),
|
||||
) as _i9.Future<List<_i20.LearnQuestion>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i21.Category>> getCategories() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getCategories,
|
||||
[],
|
||||
),
|
||||
returnValue: _i9.Future<List<_i15.Category>>.value(<_i15.Category>[]),
|
||||
returnValue: _i9.Future<List<_i21.Category>>.value(<_i21.Category>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i15.Category>>.value(<_i15.Category>[]),
|
||||
) as _i9.Future<List<_i15.Category>>);
|
||||
_i9.Future<List<_i21.Category>>.value(<_i21.Category>[]),
|
||||
) as _i9.Future<List<_i21.Category>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i16.Subcategory>> getSubcategories(int? id) =>
|
||||
_i9.Future<List<_i22.Subcategory>> getSubcategories(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getSubcategories,
|
||||
[id],
|
||||
),
|
||||
returnValue:
|
||||
_i9.Future<List<_i16.Subcategory>>.value(<_i16.Subcategory>[]),
|
||||
_i9.Future<List<_i22.Subcategory>>.value(<_i22.Subcategory>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i16.Subcategory>>.value(<_i16.Subcategory>[]),
|
||||
) as _i9.Future<List<_i16.Subcategory>>);
|
||||
_i9.Future<List<_i22.Subcategory>>.value(<_i22.Subcategory>[]),
|
||||
) as _i9.Future<List<_i22.Subcategory>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i17.CourseProgress>> getCourseProgress(int? id) =>
|
||||
_i9.Future<List<_i23.CourseProgress>> getCourseProgress(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getCourseProgress,
|
||||
[id],
|
||||
),
|
||||
returnValue: _i9.Future<List<_i17.CourseProgress>>.value(
|
||||
<_i17.CourseProgress>[]),
|
||||
returnValueForMissingStub: _i9.Future<List<_i17.CourseProgress>>.value(
|
||||
<_i17.CourseProgress>[]),
|
||||
) as _i9.Future<List<_i17.CourseProgress>>);
|
||||
returnValue: _i9.Future<List<_i23.CourseProgress>>.value(
|
||||
<_i23.CourseProgress>[]),
|
||||
returnValueForMissingStub: _i9.Future<List<_i23.CourseProgress>>.value(
|
||||
<_i23.CourseProgress>[]),
|
||||
) as _i9.Future<List<_i23.CourseProgress>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i18.CourseLesson>> getCourseLessons(int? id) =>
|
||||
_i9.Future<List<_i24.CourseLesson>> getCourseLessons(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getCourseLessons,
|
||||
[id],
|
||||
),
|
||||
returnValue:
|
||||
_i9.Future<List<_i18.CourseLesson>>.value(<_i18.CourseLesson>[]),
|
||||
_i9.Future<List<_i24.CourseLesson>>.value(<_i24.CourseLesson>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i18.CourseLesson>>.value(<_i18.CourseLesson>[]),
|
||||
) as _i9.Future<List<_i18.CourseLesson>>);
|
||||
_i9.Future<List<_i24.CourseLesson>>.value(<_i24.CourseLesson>[]),
|
||||
) as _i9.Future<List<_i24.CourseLesson>>);
|
||||
|
||||
@override
|
||||
_i9.Future<Map<String, dynamic>> completeLesson(int? id) =>
|
||||
|
|
@ -1191,30 +1302,28 @@ class MockApiService extends _i1.Mock implements _i13.ApiService {
|
|||
) as _i9.Future<Map<String, dynamic>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i19.Practice>> getCoursePractices(int? id) =>
|
||||
_i9.Future<List<_i25.Practice>> getCoursePractices(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getCoursePractices,
|
||||
[id],
|
||||
),
|
||||
returnValue: _i9.Future<List<_i19.Practice>>.value(<_i19.Practice>[]),
|
||||
returnValue: _i9.Future<List<_i25.Practice>>.value(<_i25.Practice>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i19.Practice>>.value(<_i19.Practice>[]),
|
||||
) as _i9.Future<List<_i19.Practice>>);
|
||||
_i9.Future<List<_i25.Practice>>.value(<_i25.Practice>[]),
|
||||
) as _i9.Future<List<_i25.Practice>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i20.PracticeQuestion>> getCoursePracticeQuestions(int? id) =>
|
||||
_i9.Future<List<_i14.Question>> getCoursePracticeQuestions(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getCoursePracticeQuestions,
|
||||
[id],
|
||||
),
|
||||
returnValue: _i9.Future<List<_i20.PracticeQuestion>>.value(
|
||||
<_i20.PracticeQuestion>[]),
|
||||
returnValue: _i9.Future<List<_i14.Question>>.value(<_i14.Question>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i20.PracticeQuestion>>.value(
|
||||
<_i20.PracticeQuestion>[]),
|
||||
) as _i9.Future<List<_i20.PracticeQuestion>>);
|
||||
_i9.Future<List<_i14.Question>>.value(<_i14.Question>[]),
|
||||
) as _i9.Future<List<_i14.Question>>);
|
||||
|
||||
@override
|
||||
_i9.Future<_i14.Question?> getCoursePracticeQuestion(int? id) =>
|
||||
|
|
@ -1228,73 +1337,95 @@ class MockApiService extends _i1.Mock implements _i13.ApiService {
|
|||
) as _i9.Future<_i14.Question?>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i16.Subcategory>> getLearnSubcategories() =>
|
||||
_i9.Future<List<_i22.Subcategory>> getLearnSubcategories() =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getLearnSubcategories,
|
||||
[],
|
||||
),
|
||||
returnValue:
|
||||
_i9.Future<List<_i16.Subcategory>>.value(<_i16.Subcategory>[]),
|
||||
_i9.Future<List<_i22.Subcategory>>.value(<_i22.Subcategory>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i16.Subcategory>>.value(<_i16.Subcategory>[]),
|
||||
) as _i9.Future<List<_i16.Subcategory>>);
|
||||
_i9.Future<List<_i22.Subcategory>>.value(<_i22.Subcategory>[]),
|
||||
) as _i9.Future<List<_i22.Subcategory>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i21.Course>> getCourses(int? id) => (super.noSuchMethod(
|
||||
_i9.Future<List<_i26.Course>> getCourses(int? id) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getCourses,
|
||||
[id],
|
||||
),
|
||||
returnValue: _i9.Future<List<_i21.Course>>.value(<_i21.Course>[]),
|
||||
returnValue: _i9.Future<List<_i26.Course>>.value(<_i26.Course>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i21.Course>>.value(<_i21.Course>[]),
|
||||
) as _i9.Future<List<_i21.Course>>);
|
||||
_i9.Future<List<_i26.Course>>.value(<_i26.Course>[]),
|
||||
) as _i9.Future<List<_i26.Course>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i22.Level>> getLevels(int? id) => (super.noSuchMethod(
|
||||
_i9.Future<List<_i27.Level>> getLevels(int? id) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getLevels,
|
||||
[id],
|
||||
),
|
||||
returnValue: _i9.Future<List<_i22.Level>>.value(<_i22.Level>[]),
|
||||
returnValue: _i9.Future<List<_i27.Level>>.value(<_i27.Level>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i22.Level>>.value(<_i22.Level>[]),
|
||||
) as _i9.Future<List<_i22.Level>>);
|
||||
_i9.Future<List<_i27.Level>>.value(<_i27.Level>[]),
|
||||
) as _i9.Future<List<_i27.Level>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i23.Module>> getModules(int? id) => (super.noSuchMethod(
|
||||
_i9.Future<List<_i28.Module>> getModules(int? id) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getModules,
|
||||
[id],
|
||||
),
|
||||
returnValue: _i9.Future<List<_i23.Module>>.value(<_i23.Module>[]),
|
||||
returnValue: _i9.Future<List<_i28.Module>>.value(<_i28.Module>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i23.Module>>.value(<_i23.Module>[]),
|
||||
) as _i9.Future<List<_i23.Module>>);
|
||||
_i9.Future<List<_i28.Module>>.value(<_i28.Module>[]),
|
||||
) as _i9.Future<List<_i28.Module>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i24.Submodule>> getSubmodules(int? id) =>
|
||||
_i9.Future<List<_i29.Submodule>> getSubmodules(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getSubmodules,
|
||||
[id],
|
||||
),
|
||||
returnValue: _i9.Future<List<_i24.Submodule>>.value(<_i24.Submodule>[]),
|
||||
returnValue: _i9.Future<List<_i29.Submodule>>.value(<_i29.Submodule>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i24.Submodule>>.value(<_i24.Submodule>[]),
|
||||
) as _i9.Future<List<_i24.Submodule>>);
|
||||
_i9.Future<List<_i29.Submodule>>.value(<_i29.Submodule>[]),
|
||||
) as _i9.Future<List<_i29.Submodule>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i25.Lesson>> getLessons(int? id) => (super.noSuchMethod(
|
||||
_i9.Future<List<_i30.Lesson>> getLessons(int? id) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getLessons,
|
||||
[id],
|
||||
),
|
||||
returnValue: _i9.Future<List<_i25.Lesson>>.value(<_i25.Lesson>[]),
|
||||
returnValue: _i9.Future<List<_i30.Lesson>>.value(<_i30.Lesson>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i25.Lesson>>.value(<_i25.Lesson>[]),
|
||||
) as _i9.Future<List<_i25.Lesson>>);
|
||||
_i9.Future<List<_i30.Lesson>>.value(<_i30.Lesson>[]),
|
||||
) as _i9.Future<List<_i30.Lesson>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i25.Practice>> getPractices(int? id) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getPractices,
|
||||
[id],
|
||||
),
|
||||
returnValue: _i9.Future<List<_i25.Practice>>.value(<_i25.Practice>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i25.Practice>>.value(<_i25.Practice>[]),
|
||||
) as _i9.Future<List<_i25.Practice>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i14.Question>> getQuestions(int? id) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getQuestions,
|
||||
[id],
|
||||
),
|
||||
returnValue: _i9.Future<List<_i14.Question>>.value(<_i14.Question>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i14.Question>>.value(<_i14.Question>[]),
|
||||
) as _i9.Future<List<_i14.Question>>);
|
||||
}
|
||||
|
||||
/// A class which mocks [SecureStorageService].
|
||||
|
|
@ -1397,7 +1528,7 @@ class MockSecureStorageService extends _i1.Mock
|
|||
/// A class which mocks [DioService].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockDioService extends _i1.Mock implements _i26.DioService {
|
||||
class MockDioService extends _i1.Mock implements _i31.DioService {
|
||||
@override
|
||||
_i2.Dio get dio => (super.noSuchMethod(
|
||||
Invocation.getter(#dio),
|
||||
|
|
@ -1416,7 +1547,7 @@ class MockDioService extends _i1.Mock implements _i26.DioService {
|
|||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockStatusCheckerService extends _i1.Mock
|
||||
implements _i27.StatusCheckerService {
|
||||
implements _i32.StatusCheckerService {
|
||||
@override
|
||||
_i3.SecureStorageService get storage => (super.noSuchMethod(
|
||||
Invocation.getter(#storage),
|
||||
|
|
@ -1437,16 +1568,6 @@ class MockStatusCheckerService extends _i1.Mock
|
|||
returnValueForMissingStub: false,
|
||||
) as bool);
|
||||
|
||||
@override
|
||||
_i9.Future<int> getBatteryLevel() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getBatteryLevel,
|
||||
[],
|
||||
),
|
||||
returnValue: _i9.Future<int>.value(0),
|
||||
returnValueForMissingStub: _i9.Future<int>.value(0),
|
||||
) as _i9.Future<int>);
|
||||
|
||||
@override
|
||||
_i9.Future<bool> checkConnection() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
|
|
@ -1456,66 +1577,46 @@ class MockStatusCheckerService extends _i1.Mock
|
|||
returnValue: _i9.Future<bool>.value(false),
|
||||
returnValueForMissingStub: _i9.Future<bool>.value(false),
|
||||
) as _i9.Future<bool>);
|
||||
|
||||
@override
|
||||
_i9.Future<int> getAvailableStorage() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getAvailableStorage,
|
||||
[],
|
||||
),
|
||||
returnValue: _i9.Future<int>.value(0),
|
||||
returnValueForMissingStub: _i9.Future<int>.value(0),
|
||||
) as _i9.Future<int>);
|
||||
|
||||
@override
|
||||
_i9.Future<void> checkAndUpdate() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#checkAndUpdate,
|
||||
[],
|
||||
),
|
||||
returnValue: _i9.Future<void>.value(),
|
||||
returnValueForMissingStub: _i9.Future<void>.value(),
|
||||
) as _i9.Future<void>);
|
||||
}
|
||||
|
||||
/// A class which mocks [PermissionHandlerService].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockPermissionHandlerService extends _i1.Mock
|
||||
implements _i28.PermissionHandlerService {
|
||||
implements _i33.PermissionHandlerService {
|
||||
@override
|
||||
_i9.Future<_i29.PermissionStatus> requestPermission(
|
||||
_i29.Permission? requestedPermission) =>
|
||||
_i9.Future<_i34.PermissionStatus> requestPermission(
|
||||
_i34.Permission? requestedPermission) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#requestPermission,
|
||||
[requestedPermission],
|
||||
),
|
||||
returnValue: _i9.Future<_i29.PermissionStatus>.value(
|
||||
_i29.PermissionStatus.denied),
|
||||
returnValueForMissingStub: _i9.Future<_i29.PermissionStatus>.value(
|
||||
_i29.PermissionStatus.denied),
|
||||
) as _i9.Future<_i29.PermissionStatus>);
|
||||
returnValue: _i9.Future<_i34.PermissionStatus>.value(
|
||||
_i34.PermissionStatus.denied),
|
||||
returnValueForMissingStub: _i9.Future<_i34.PermissionStatus>.value(
|
||||
_i34.PermissionStatus.denied),
|
||||
) as _i9.Future<_i34.PermissionStatus>);
|
||||
|
||||
@override
|
||||
_i9.Future<_i29.PermissionStatus> request(_i29.Permission? permission) =>
|
||||
_i9.Future<_i34.PermissionStatus> request(_i34.Permission? permission) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#request,
|
||||
[permission],
|
||||
),
|
||||
returnValue: _i9.Future<_i29.PermissionStatus>.value(
|
||||
_i29.PermissionStatus.denied),
|
||||
returnValueForMissingStub: _i9.Future<_i29.PermissionStatus>.value(
|
||||
_i29.PermissionStatus.denied),
|
||||
) as _i9.Future<_i29.PermissionStatus>);
|
||||
returnValue: _i9.Future<_i34.PermissionStatus>.value(
|
||||
_i34.PermissionStatus.denied),
|
||||
returnValueForMissingStub: _i9.Future<_i34.PermissionStatus>.value(
|
||||
_i34.PermissionStatus.denied),
|
||||
) as _i9.Future<_i34.PermissionStatus>);
|
||||
}
|
||||
|
||||
/// A class which mocks [ImagePickerService].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockImagePickerService extends _i1.Mock
|
||||
implements _i30.ImagePickerService {
|
||||
implements _i35.ImagePickerService {
|
||||
@override
|
||||
_i9.Future<String?> gallery() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
|
|
@ -1540,7 +1641,7 @@ class MockImagePickerService extends _i1.Mock
|
|||
/// A class which mocks [GoogleAuthService].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockGoogleAuthService extends _i1.Mock implements _i31.GoogleAuthService {
|
||||
class MockGoogleAuthService extends _i1.Mock implements _i36.GoogleAuthService {
|
||||
@override
|
||||
int get listenersCount => (super.noSuchMethod(
|
||||
Invocation.getter(#listenersCount),
|
||||
|
|
@ -1610,7 +1711,7 @@ class MockGoogleAuthService extends _i1.Mock implements _i31.GoogleAuthService {
|
|||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockImageDownloaderService extends _i1.Mock
|
||||
implements _i32.ImageDownloaderService {
|
||||
implements _i37.ImageDownloaderService {
|
||||
@override
|
||||
_i9.Future<String> downloader(String? networkImage) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
|
|
@ -1639,7 +1740,7 @@ class MockImageDownloaderService extends _i1.Mock
|
|||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockNotificationService extends _i1.Mock
|
||||
implements _i33.NotificationService {
|
||||
implements _i38.NotificationService {
|
||||
@override
|
||||
_i9.Future<void> initialize() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
|
|
@ -1661,7 +1762,7 @@ class MockNotificationService extends _i1.Mock
|
|||
) as _i9.Future<void>);
|
||||
|
||||
@override
|
||||
_i9.Future<void> showNotification(_i34.RemoteMessage? message) =>
|
||||
_i9.Future<void> showNotification(_i39.RemoteMessage? message) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#showNotification,
|
||||
|
|
@ -1695,7 +1796,7 @@ class MockNotificationService extends _i1.Mock
|
|||
/// A class which mocks [SmartAuthService].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockSmartAuthService extends _i1.Mock implements _i35.SmartAuthService {
|
||||
class MockSmartAuthService extends _i1.Mock implements _i40.SmartAuthService {
|
||||
@override
|
||||
bool get listenForMultipleSms => (super.noSuchMethod(
|
||||
Invocation.getter(#listenForMultipleSms),
|
||||
|
|
@ -1727,26 +1828,26 @@ class MockSmartAuthService extends _i1.Mock implements _i35.SmartAuthService {
|
|||
/// A class which mocks [CourseService].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockCourseService extends _i1.Mock implements _i36.CourseService {
|
||||
class MockCourseService extends _i1.Mock implements _i41.CourseService {
|
||||
@override
|
||||
_i9.Future<List<_i37.CourseDetail>> getCoursesDetail(int? id) =>
|
||||
_i9.Future<List<_i42.CourseDetail>> getCoursesDetail(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getCoursesDetail,
|
||||
[id],
|
||||
),
|
||||
returnValue:
|
||||
_i9.Future<List<_i37.CourseDetail>>.value(<_i37.CourseDetail>[]),
|
||||
_i9.Future<List<_i42.CourseDetail>>.value(<_i42.CourseDetail>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i37.CourseDetail>>.value(<_i37.CourseDetail>[]),
|
||||
) as _i9.Future<List<_i37.CourseDetail>>);
|
||||
_i9.Future<List<_i42.CourseDetail>>.value(<_i42.CourseDetail>[]),
|
||||
) as _i9.Future<List<_i42.CourseDetail>>);
|
||||
}
|
||||
|
||||
/// A class which mocks [AudioPlayerService].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockAudioPlayerService extends _i1.Mock
|
||||
implements _i38.AudioPlayerService {
|
||||
implements _i43.AudioPlayerService {
|
||||
@override
|
||||
_i4.AudioPlayer get player => (super.noSuchMethod(
|
||||
Invocation.getter(#player),
|
||||
|
|
@ -1870,13 +1971,13 @@ class MockAudioPlayerService extends _i1.Mock
|
|||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockVoiceRecorderService extends _i1.Mock
|
||||
implements _i39.VoiceRecorderService {
|
||||
implements _i44.VoiceRecorderService {
|
||||
@override
|
||||
_i40.VoiceRecordingState get recordingState => (super.noSuchMethod(
|
||||
_i45.VoiceRecordingState get recordingState => (super.noSuchMethod(
|
||||
Invocation.getter(#recordingState),
|
||||
returnValue: _i40.VoiceRecordingState.pending,
|
||||
returnValueForMissingStub: _i40.VoiceRecordingState.pending,
|
||||
) as _i40.VoiceRecordingState);
|
||||
returnValue: _i45.VoiceRecordingState.pending,
|
||||
returnValueForMissingStub: _i45.VoiceRecordingState.pending,
|
||||
) as _i45.VoiceRecordingState);
|
||||
|
||||
@override
|
||||
_i5.WaveformRecorderController get waveController => (super.noSuchMethod(
|
||||
|
|
@ -1965,3 +2066,39 @@ class MockVoiceRecorderService extends _i1.Mock
|
|||
returnValueForMissingStub: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// A class which mocks [InAppUpdateService].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockInAppUpdateService extends _i1.Mock
|
||||
implements _i46.InAppUpdateService {
|
||||
@override
|
||||
_i9.Future<int> getBatteryLevel() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getBatteryLevel,
|
||||
[],
|
||||
),
|
||||
returnValue: _i9.Future<int>.value(0),
|
||||
returnValueForMissingStub: _i9.Future<int>.value(0),
|
||||
) as _i9.Future<int>);
|
||||
|
||||
@override
|
||||
_i9.Future<int> getAvailableStorage() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getAvailableStorage,
|
||||
[],
|
||||
),
|
||||
returnValue: _i9.Future<int>.value(0),
|
||||
returnValueForMissingStub: _i9.Future<int>.value(0),
|
||||
) as _i9.Future<int>);
|
||||
|
||||
@override
|
||||
_i9.Future<void> checkForUpdate() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#checkForUpdate,
|
||||
[],
|
||||
),
|
||||
returnValue: _i9.Future<void>.value(),
|
||||
returnValueForMissingStub: _i9.Future<void>.value(),
|
||||
) as _i9.Future<void>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import 'package:yimaru_app/app/app.locator.dart';
|
|||
import '../helpers/test_helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('LearnViewModel Tests -', () {
|
||||
group('InAppUpdateServiceTest -', () {
|
||||
setUp(() => registerServices());
|
||||
tearDown(() => locator.reset());
|
||||
});
|
||||
|
|
@ -4,7 +4,7 @@ import 'package:yimaru_app/app/app.locator.dart';
|
|||
import '../helpers/test_helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('LearnLevelViewModel Tests -', () {
|
||||
group('LearnCourseViewModel Tests -', () {
|
||||
setUp(() => registerServices());
|
||||
tearDown(() => locator.reset());
|
||||
});
|
||||
|
|
@ -4,7 +4,7 @@ import 'package:yimaru_app/app/app.locator.dart';
|
|||
import '../helpers/test_helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('LearnSubmoduleViewModel Tests -', () {
|
||||
group('LearnProgramViewModel Tests -', () {
|
||||
setUp(() => registerServices());
|
||||
tearDown(() => locator.reset());
|
||||
});
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:yimaru_app/app/app.locator.dart';
|
||||
|
||||
import '../helpers/test_helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('LearnSubcategoryViewModel Tests -', () {
|
||||
setUp(() => registerServices());
|
||||
tearDown(() => locator.reset());
|
||||
});
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user