Merge branch 'release/0.1.9'

-fix(onboarding): Change onboarding assessment implementation
This commit is contained in:
BisratHailu 2026-04-30 10:58:36 +03:00
commit 6b4f87476e
71 changed files with 1076 additions and 792 deletions

View File

@ -25,7 +25,6 @@ import 'package:yimaru_app/services/secure_storage_service.dart';
import 'package:yimaru_app/services/dio_service.dart';
import 'package:yimaru_app/services/status_checker_service.dart';
import 'package:yimaru_app/ui/views/welcome/welcome_view.dart';
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart';
import 'package:yimaru_app/ui/views/learn_lesson/learn_lesson_view.dart';
import 'package:yimaru_app/services/permission_handler_service.dart';
import 'package:yimaru_app/services/image_picker_service.dart';
@ -52,6 +51,7 @@ import 'package:yimaru_app/ui/views/course_practice_question/course_practice_que
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';
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart';
// @stacked-import
@StackedApp(
@ -74,7 +74,6 @@ import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart';
MaterialRoute(page: LoginView),
MaterialRoute(page: LearnModuleView),
MaterialRoute(page: WelcomeView),
MaterialRoute(page: AssessmentView),
MaterialRoute(page: LearnLessonView),
MaterialRoute(page: ForgetPasswordView),
MaterialRoute(page: LearnLessonDetailView),
@ -91,6 +90,7 @@ import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart';
MaterialRoute(page: CoursePracticeQuestionView),
MaterialRoute(page: LearnProgramView),
MaterialRoute(page: LearnCourseView),
MaterialRoute(page: AssessmentView),
// @stacked-route
],
dependencies: [

View File

@ -20,43 +20,43 @@ import 'package:yimaru_app/models/subcategory.dart' as _i45;
import 'package:yimaru_app/ui/common/enmus.dart' as _i41;
import 'package:yimaru_app/ui/views/account_privacy/account_privacy_view.dart'
as _i9;
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart' as _i20;
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart' as _i36;
import 'package:yimaru_app/ui/views/call_support/call_support_view.dart'
as _i12;
import 'package:yimaru_app/ui/views/course/course_view.dart' as _i33;
import 'package:yimaru_app/ui/views/course/course_view.dart' as _i32;
import 'package:yimaru_app/ui/views/course_category/course_category_view.dart'
as _i27;
import 'package:yimaru_app/ui/views/course_lesson/course_lesson_view.dart'
as _i29;
import 'package:yimaru_app/ui/views/course_lesson_detail/course_lesson_detail_view.dart'
as _i30;
import 'package:yimaru_app/ui/views/course_payment/course_payment_view.dart'
as _i26;
import 'package:yimaru_app/ui/views/course_practice/course_practice_view.dart'
import 'package:yimaru_app/ui/views/course_lesson/course_lesson_view.dart'
as _i28;
import 'package:yimaru_app/ui/views/course_lesson_detail/course_lesson_detail_view.dart'
as _i29;
import 'package:yimaru_app/ui/views/course_payment/course_payment_view.dart'
as _i25;
import 'package:yimaru_app/ui/views/course_practice/course_practice_view.dart'
as _i24;
import 'package:yimaru_app/ui/views/course_practice_question/course_practice_question_view.dart'
as _i34;
as _i33;
import 'package:yimaru_app/ui/views/course_subcategory/course_subcategory_view.dart'
as _i32;
as _i31;
import 'package:yimaru_app/ui/views/downloads/downloads_view.dart' as _i7;
import 'package:yimaru_app/ui/views/duolingo/duolingo_view.dart' as _i31;
import 'package:yimaru_app/ui/views/failure/failure_view.dart' as _i28;
import 'package:yimaru_app/ui/views/duolingo/duolingo_view.dart' as _i30;
import 'package:yimaru_app/ui/views/failure/failure_view.dart' as _i27;
import 'package:yimaru_app/ui/views/forget_password/forget_password_view.dart'
as _i22;
as _i21;
import 'package:yimaru_app/ui/views/home/home_view.dart' as _i2;
import 'package:yimaru_app/ui/views/language/language_view.dart' as _i13;
import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart'
as _i36;
as _i35;
import 'package:yimaru_app/ui/views/learn_lesson/learn_lesson_view.dart'
as _i21;
as _i20;
import 'package:yimaru_app/ui/views/learn_lesson_detail/learn_lesson_detail_view.dart'
as _i23;
as _i22;
import 'package:yimaru_app/ui/views/learn_module/learn_module_view.dart'
as _i18;
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_view.dart'
as _i24;
as _i23;
import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart'
as _i35;
as _i34;
import 'package:yimaru_app/ui/views/login/login_view.dart' as _i17;
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.dart' as _i3;
import 'package:yimaru_app/ui/views/privacy_policy/privacy_policy_view.dart'
@ -111,8 +111,6 @@ class Routes {
static const welcomeView = '/welcome-view';
static const assessmentView = '/assessment-view';
static const learnLessonView = '/learn-lesson-view';
static const forgetPasswordView = '/forget-password-view';
@ -145,6 +143,8 @@ class Routes {
static const learnCourseView = '/learn-course-view';
static const assessmentView = '/assessment-view';
static const all = <String>{
homeView,
onboardingView,
@ -164,7 +164,6 @@ class Routes {
loginView,
learnModuleView,
welcomeView,
assessmentView,
learnLessonView,
forgetPasswordView,
learnLessonDetailView,
@ -181,6 +180,7 @@ class Routes {
coursePracticeQuestionView,
learnProgramView,
learnCourseView,
assessmentView,
};
}
@ -258,73 +258,73 @@ class StackedRouter extends _i1.RouterBase {
Routes.welcomeView,
page: _i19.WelcomeView,
),
_i1.RouteDef(
Routes.assessmentView,
page: _i20.AssessmentView,
),
_i1.RouteDef(
Routes.learnLessonView,
page: _i21.LearnLessonView,
page: _i20.LearnLessonView,
),
_i1.RouteDef(
Routes.forgetPasswordView,
page: _i22.ForgetPasswordView,
page: _i21.ForgetPasswordView,
),
_i1.RouteDef(
Routes.learnLessonDetailView,
page: _i23.LearnLessonDetailView,
page: _i22.LearnLessonDetailView,
),
_i1.RouteDef(
Routes.learnPracticeView,
page: _i24.LearnPracticeView,
page: _i23.LearnPracticeView,
),
_i1.RouteDef(
Routes.coursePracticeView,
page: _i25.CoursePracticeView,
page: _i24.CoursePracticeView,
),
_i1.RouteDef(
Routes.coursePaymentView,
page: _i26.CoursePaymentView,
page: _i25.CoursePaymentView,
),
_i1.RouteDef(
Routes.courseCategoryView,
page: _i27.CourseCategoryView,
page: _i26.CourseCategoryView,
),
_i1.RouteDef(
Routes.failureView,
page: _i28.FailureView,
page: _i27.FailureView,
),
_i1.RouteDef(
Routes.courseLessonView,
page: _i29.CourseLessonView,
page: _i28.CourseLessonView,
),
_i1.RouteDef(
Routes.courseLessonDetailView,
page: _i30.CourseLessonDetailView,
page: _i29.CourseLessonDetailView,
),
_i1.RouteDef(
Routes.duolingoView,
page: _i31.DuolingoView,
page: _i30.DuolingoView,
),
_i1.RouteDef(
Routes.courseSubcategoryView,
page: _i32.CourseSubcategoryView,
page: _i31.CourseSubcategoryView,
),
_i1.RouteDef(
Routes.courseView,
page: _i33.CourseView,
page: _i32.CourseView,
),
_i1.RouteDef(
Routes.coursePracticeQuestionView,
page: _i34.CoursePracticeQuestionView,
page: _i33.CoursePracticeQuestionView,
),
_i1.RouteDef(
Routes.learnProgramView,
page: _i35.LearnProgramView,
page: _i34.LearnProgramView,
),
_i1.RouteDef(
Routes.learnCourseView,
page: _i36.LearnCourseView,
page: _i35.LearnCourseView,
),
_i1.RouteDef(
Routes.assessmentView,
page: _i36.AssessmentView,
),
];
@ -490,143 +490,143 @@ class StackedRouter extends _i1.RouterBase {
settings: data,
);
},
_i20.AssessmentView: (data) {
final args = data.getArgs<AssessmentViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) =>
_i20.AssessmentView(key: args.key, data: args.data),
settings: data,
);
},
_i21.LearnLessonView: (data) {
_i20.LearnLessonView: (data) {
final args = data.getArgs<LearnLessonViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) =>
_i21.LearnLessonView(key: args.key, module: args.module),
_i20.LearnLessonView(key: args.key, module: args.module),
settings: data,
);
},
_i22.ForgetPasswordView: (data) {
_i21.ForgetPasswordView: (data) {
final args = data.getArgs<ForgetPasswordViewArguments>(
orElse: () => const ForgetPasswordViewArguments(),
);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) => _i22.ForgetPasswordView(key: args.key),
builder: (context) => _i21.ForgetPasswordView(key: args.key),
settings: data,
);
},
_i23.LearnLessonDetailView: (data) {
_i22.LearnLessonDetailView: (data) {
final args = data.getArgs<LearnLessonDetailViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) =>
_i23.LearnLessonDetailView(key: args.key, lesson: args.lesson),
_i22.LearnLessonDetailView(key: args.key, lesson: args.lesson),
settings: data,
);
},
_i24.LearnPracticeView: (data) {
_i23.LearnPracticeView: (data) {
final args = data.getArgs<LearnPracticeViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) => _i24.LearnPracticeView(
builder: (context) => _i23.LearnPracticeView(
key: args.key, id: args.id, practice: args.practice),
settings: data,
);
},
_i25.CoursePracticeView: (data) {
_i24.CoursePracticeView: (data) {
final args = data.getArgs<CoursePracticeViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) =>
_i25.CoursePracticeView(key: args.key, id: args.id),
_i24.CoursePracticeView(key: args.key, id: args.id),
settings: data,
);
},
_i26.CoursePaymentView: (data) {
_i25.CoursePaymentView: (data) {
final args = data.getArgs<CoursePaymentViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) =>
_i26.CoursePaymentView(key: args.key, course: args.course),
_i25.CoursePaymentView(key: args.key, course: args.course),
settings: data,
);
},
_i27.CourseCategoryView: (data) {
_i26.CourseCategoryView: (data) {
final args = data.getArgs<CourseCategoryViewArguments>(
orElse: () => const CourseCategoryViewArguments(),
);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) => _i27.CourseCategoryView(key: args.key),
builder: (context) => _i26.CourseCategoryView(key: args.key),
settings: data,
);
},
_i28.FailureView: (data) {
_i27.FailureView: (data) {
final args = data.getArgs<FailureViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) =>
_i28.FailureView(key: args.key, label: args.label),
builder: (context) => _i27.FailureView(
key: args.key, onTap: args.onTap, label: args.label),
settings: data,
);
},
_i29.CourseLessonView: (data) {
_i28.CourseLessonView: (data) {
final args = data.getArgs<CourseLessonViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) =>
_i29.CourseLessonView(key: args.key, course: args.course),
_i28.CourseLessonView(key: args.key, course: args.course),
settings: data,
);
},
_i30.CourseLessonDetailView: (data) {
_i29.CourseLessonDetailView: (data) {
final args = data.getArgs<CourseLessonDetailViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) =>
_i30.CourseLessonDetailView(key: args.key, lesson: args.lesson),
_i29.CourseLessonDetailView(key: args.key, lesson: args.lesson),
settings: data,
);
},
_i31.DuolingoView: (data) {
_i30.DuolingoView: (data) {
final args = data.getArgs<DuolingoViewArguments>(
orElse: () => const DuolingoViewArguments(),
);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) => _i31.DuolingoView(key: args.key),
builder: (context) => _i30.DuolingoView(key: args.key),
settings: data,
);
},
_i32.CourseSubcategoryView: (data) {
_i31.CourseSubcategoryView: (data) {
final args = data.getArgs<CourseSubcategoryViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) =>
_i32.CourseSubcategoryView(key: args.key, category: args.category),
_i31.CourseSubcategoryView(key: args.key, category: args.category),
settings: data,
);
},
_i33.CourseView: (data) {
_i32.CourseView: (data) {
final args = data.getArgs<CourseViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) =>
_i33.CourseView(key: args.key, subcategory: args.subcategory),
_i32.CourseView(key: args.key, subcategory: args.subcategory),
settings: data,
);
},
_i34.CoursePracticeQuestionView: (data) {
_i33.CoursePracticeQuestionView: (data) {
final args =
data.getArgs<CoursePracticeQuestionViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) =>
_i34.CoursePracticeQuestionView(key: args.key, id: args.id),
_i33.CoursePracticeQuestionView(key: args.key, id: args.id),
settings: data,
);
},
_i35.LearnProgramView: (data) {
_i34.LearnProgramView: (data) {
final args = data.getArgs<LearnProgramViewArguments>(
orElse: () => const LearnProgramViewArguments(),
);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) => _i35.LearnProgramView(key: args.key),
builder: (context) => _i34.LearnProgramView(key: args.key),
settings: data,
);
},
_i36.LearnCourseView: (data) {
_i35.LearnCourseView: (data) {
final args = data.getArgs<LearnCourseViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) => _i36.LearnCourseView(key: args.key, id: args.id),
builder: (context) => _i35.LearnCourseView(key: args.key, id: args.id),
settings: data,
);
},
_i36.AssessmentView: (data) {
final args = data.getArgs<AssessmentViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>(
builder: (context) =>
_i36.AssessmentView(key: args.key, data: args.data),
settings: data,
);
},
@ -1045,33 +1045,6 @@ class WelcomeViewArguments {
}
}
class AssessmentViewArguments {
const AssessmentViewArguments({
this.key,
required this.data,
});
final _i37.Key? key;
final Map<String, dynamic> data;
@override
String toString() {
return '{"key": "$key", "data": "$data"}';
}
@override
bool operator ==(covariant AssessmentViewArguments other) {
if (identical(this, other)) return true;
return other.key == key && other.data == data;
}
@override
int get hashCode {
return key.hashCode ^ data.hashCode;
}
}
class LearnLessonViewArguments {
const LearnLessonViewArguments({
this.key,
@ -1257,27 +1230,30 @@ class CourseCategoryViewArguments {
class FailureViewArguments {
const FailureViewArguments({
this.key,
required this.onTap,
required this.label,
});
final _i37.Key? key;
final void Function() onTap;
final String label;
@override
String toString() {
return '{"key": "$key", "label": "$label"}';
return '{"key": "$key", "onTap": "$onTap", "label": "$label"}';
}
@override
bool operator ==(covariant FailureViewArguments other) {
if (identical(this, other)) return true;
return other.key == key && other.label == label;
return other.key == key && other.onTap == onTap && other.label == label;
}
@override
int get hashCode {
return key.hashCode ^ label.hashCode;
return key.hashCode ^ onTap.hashCode ^ label.hashCode;
}
}
@ -1487,6 +1463,33 @@ class LearnCourseViewArguments {
}
}
class AssessmentViewArguments {
const AssessmentViewArguments({
this.key,
required this.data,
});
final _i37.Key? key;
final Map<String, dynamic> data;
@override
String toString() {
return '{"key": "$key", "data": "$data"}';
}
@override
bool operator ==(covariant AssessmentViewArguments other) {
if (identical(this, other)) return true;
return other.key == key && other.data == data;
}
@override
int get hashCode {
return key.hashCode ^ data.hashCode;
}
}
extension NavigatorStateExtension on _i46.NavigationService {
Future<dynamic> navigateToHomeView({
_i37.Key? key,
@ -1778,23 +1781,6 @@ extension NavigatorStateExtension on _i46.NavigationService {
transition: transition);
}
Future<dynamic> navigateToAssessmentView({
_i37.Key? key,
required Map<String, dynamic> data,
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
}) async {
return navigateTo<dynamic>(Routes.assessmentView,
arguments: AssessmentViewArguments(key: key, data: data),
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic> navigateToLearnLessonView({
_i37.Key? key,
required _i39.LearnModule module,
@ -1916,6 +1902,7 @@ extension NavigatorStateExtension on _i46.NavigationService {
Future<dynamic> navigateToFailureView({
_i37.Key? key,
required void Function() onTap,
required String label,
int? routerId,
bool preventDuplicates = true,
@ -1924,7 +1911,7 @@ extension NavigatorStateExtension on _i46.NavigationService {
transition,
}) async {
return navigateTo<dynamic>(Routes.failureView,
arguments: FailureViewArguments(key: key, label: label),
arguments: FailureViewArguments(key: key, onTap: onTap, label: label),
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
@ -2065,6 +2052,23 @@ extension NavigatorStateExtension on _i46.NavigationService {
transition: transition);
}
Future<dynamic> navigateToAssessmentView({
_i37.Key? key,
required Map<String, dynamic> data,
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
}) async {
return navigateTo<dynamic>(Routes.assessmentView,
arguments: AssessmentViewArguments(key: key, data: data),
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic> replaceWithHomeView({
_i37.Key? key,
int? routerId,
@ -2355,23 +2359,6 @@ extension NavigatorStateExtension on _i46.NavigationService {
transition: transition);
}
Future<dynamic> replaceWithAssessmentView({
_i37.Key? key,
required Map<String, dynamic> data,
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
}) async {
return replaceWith<dynamic>(Routes.assessmentView,
arguments: AssessmentViewArguments(key: key, data: data),
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic> replaceWithLearnLessonView({
_i37.Key? key,
required _i39.LearnModule module,
@ -2493,6 +2480,7 @@ extension NavigatorStateExtension on _i46.NavigationService {
Future<dynamic> replaceWithFailureView({
_i37.Key? key,
required void Function() onTap,
required String label,
int? routerId,
bool preventDuplicates = true,
@ -2501,7 +2489,7 @@ extension NavigatorStateExtension on _i46.NavigationService {
transition,
}) async {
return replaceWith<dynamic>(Routes.failureView,
arguments: FailureViewArguments(key: key, label: label),
arguments: FailureViewArguments(key: key, onTap: onTap, label: label),
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
@ -2641,4 +2629,21 @@ extension NavigatorStateExtension on _i46.NavigationService {
parameters: parameters,
transition: transition);
}
Future<dynamic> replaceWithAssessmentView({
_i37.Key? key,
required Map<String, dynamic> data,
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
}) async {
return replaceWith<dynamic>(Routes.assessmentView,
arguments: AssessmentViewArguments(key: key, data: data),
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
}

View File

@ -0,0 +1,37 @@
import 'package:json_annotation/json_annotation.dart';
part 'assessment.g.dart';
@JsonSerializable()
class Assessment {
final int? id;
final String? title;
final String? status;
final String? description;
@JsonKey(name: 'set_type')
final String? setType;
@JsonKey(name: 'passing_score')
final int? passingScore;
@JsonKey(name: 'shuffle_questions')
final bool? shuffleQuestions;
const Assessment(
{this.id,
this.title,
this.status,
this.setType,
this.description,
this.passingScore,
this.shuffleQuestions});
factory Assessment.fromJson(Map<String, dynamic> json) =>
_$AssessmentFromJson(json);
Map<String, dynamic> toJson() => _$AssessmentToJson(this);
}

View File

@ -0,0 +1,28 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'assessment.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Assessment _$AssessmentFromJson(Map<String, dynamic> json) => Assessment(
id: (json['id'] as num?)?.toInt(),
title: json['title'] as String?,
status: json['status'] as String?,
setType: json['set_type'] as String?,
description: json['description'] as String?,
passingScore: (json['passing_score'] as num?)?.toInt(),
shuffleQuestions: json['shuffle_questions'] as bool?,
);
Map<String, dynamic> _$AssessmentToJson(Assessment instance) =>
<String, dynamic>{
'id': instance.id,
'title': instance.title,
'status': instance.status,
'description': instance.description,
'set_type': instance.setType,
'passing_score': instance.passingScore,
'shuffle_questions': instance.shuffleQuestions,
};

View File

@ -1,9 +1,9 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:yimaru_app/models/option.dart';
part 'question.g.dart';
part 'assessment_question.g.dart';
@JsonSerializable()
class Question {
class AssessmentQuestion {
final int? id;
final int? points;
@ -18,21 +18,17 @@ class Question {
@JsonKey(name: 'question_text')
final String? questionText;
@JsonKey(name: 'difficulty_level')
final String? difficultyLevel;
const Question({
const AssessmentQuestion({
this.id,
this.points,
this.status,
this.options,
this.questionText,
this.questionType,
this.difficultyLevel,
});
factory Question.fromJson(Map<String, dynamic> json) =>
_$QuestionFromJson(json);
factory AssessmentQuestion.fromJson(Map<String, dynamic> json) =>
_$AssessmentQuestionFromJson(json);
Map<String, dynamic> toJson() => _$QuestionToJson(this);
Map<String, dynamic> toJson() => _$AssessmentQuestionToJson(this);
}

View File

@ -1,12 +1,13 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'question.dart';
part of 'assessment_question.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Question _$QuestionFromJson(Map<String, dynamic> json) => Question(
AssessmentQuestion _$AssessmentQuestionFromJson(Map<String, dynamic> json) =>
AssessmentQuestion(
id: (json['id'] as num?)?.toInt(),
points: (json['points'] as num?)?.toInt(),
status: json['status'] as String?,
@ -15,15 +16,14 @@ Question _$QuestionFromJson(Map<String, dynamic> json) => Question(
.toList(),
questionText: json['question_text'] as String?,
questionType: json['question_type'] as String?,
difficultyLevel: json['difficulty_level'] as String?,
);
Map<String, dynamic> _$QuestionToJson(Question instance) => <String, dynamic>{
Map<String, dynamic> _$AssessmentQuestionToJson(AssessmentQuestion instance) =>
<String, dynamic>{
'id': instance.id,
'points': instance.points,
'status': instance.status,
'options': instance.options,
'question_type': instance.questionType,
'question_text': instance.questionText,
'difficulty_level': instance.difficultyLevel,
};

View File

@ -11,7 +11,10 @@ class Option {
@JsonKey(name: 'option_text')
final String? optionText;
const Option({this.id, this.optionText, this.isCorrect});
@JsonKey(name: 'option_order')
final int? optionOrder;
const Option({this.id, this.optionText, this.isCorrect, this.optionOrder});
factory Option.fromJson(Map<String, dynamic> json) => _$OptionFromJson(json);

View File

@ -10,10 +10,12 @@ Option _$OptionFromJson(Map<String, dynamic> json) => Option(
id: (json['id'] as num?)?.toInt(),
optionText: json['option_text'] as String?,
isCorrect: json['is_correct'] as bool?,
optionOrder: (json['option_order'] as num?)?.toInt(),
);
Map<String, dynamic> _$OptionToJson(Option instance) => <String, dynamic>{
'id': instance.id,
'is_correct': instance.isCorrect,
'option_text': instance.optionText,
'option_order': instance.optionOrder,
};

View File

@ -3,7 +3,7 @@ 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/assessment_question.dart';
import 'package:yimaru_app/models/subcategory.dart';
import 'package:yimaru_app/models/category.dart';
import 'package:yimaru_app/models/course_lesson.dart';
@ -20,6 +20,7 @@ import '../models/learn_module.dart';
import '../models/learn_question.dart';
import '../models/lesson.dart';
import '../models/module.dart';
import '../models/assessment.dart';
import '../models/submodule.dart';
import '../ui/common/enmus.dart';
@ -354,23 +355,47 @@ class ApiService {
}
}
// Get assessments
Future<List<Question>> getAssessments() async {
// Get assessment question sets
Future<List<Assessment>> getAssessments() async {
try {
List<Question> assessments = [];
List<Assessment> assessments = [];
final Response response =
await _service.dio.get('$kBaseUrl/$kAssessmentsUrl');
final Response response = await _service.dio.get(
'$kBaseUrl/api/$kApiVersionUrl/$kQuestionSetsUrl?set_type=INITIAL_ASSESSMENT&limit=10&offset=0');
if (response.statusCode == 200) {
var data = response.data;
var decodedData = data['data']['question_sets'] as List;
assessments = decodedData.map(
(e) {
return Assessment.fromJson(e);
},
).toList();
return assessments;
}
return [];
} catch (e) {
return [];
}
}
// Get assessment questions
Future<List<AssessmentQuestion>> getAssessmentQuestions(int id) async {
try {
List<AssessmentQuestion> 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;
assessments = decodedData.map(
questions = decodedData.map(
(e) {
return Question.fromJson(e);
return AssessmentQuestion.fromJson(e);
},
).toList();
return assessments;
return questions;
}
return [];
} catch (e) {
@ -741,9 +766,9 @@ class ApiService {
}
// Get course practic questions
Future<List<Question>> getCoursePracticeQuestions(int id) async {
Future<List<AssessmentQuestion>> getCoursePracticeQuestions(int id) async {
try {
List<Question> coursePracticeQuestions = [];
List<AssessmentQuestion> coursePracticeQuestions = [];
final Response response = await _service.dio
.get('$kBaseUrl/$kPracticeBaseUrl/$id/$kCoursePracticeQuestions');
@ -753,7 +778,7 @@ class ApiService {
var decodedData = data['data'] as List;
coursePracticeQuestions = decodedData.map(
(e) {
return Question.fromJson(e);
return AssessmentQuestion.fromJson(e);
},
).toList();
return coursePracticeQuestions;
@ -765,13 +790,14 @@ class ApiService {
}
// Get course practice question
Future<Question?> getCoursePracticeQuestion(int id) async {
Future<AssessmentQuestion?> getCoursePracticeQuestion(int id) async {
try {
final Response response =
await _service.dio.get('$kBaseUrl/$kCoursePracticeQuestion/$id');
if (response.statusCode == 200) {
Question question = Question.fromJson(response.data['data']);
AssessmentQuestion question =
AssessmentQuestion.fromJson(response.data['data']);
return question;
}
@ -950,9 +976,9 @@ class ApiService {
}
// Questions
Future<List<Question>> getQuestions(int id) async {
Future<List<AssessmentQuestion>> getQuestions(int id) async {
try {
List<Question> questions = [];
List<AssessmentQuestion> questions = [];
final Response response = await _service.dio.get(
'$kBaseUrl/api/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl');
@ -962,7 +988,7 @@ class ApiService {
var decodedData = data['data'] as List;
questions = decodedData.map(
(e) {
return Question.fromJson(e);
return AssessmentQuestion.fromJson(e);
},
).toList();
return questions;

View File

@ -173,5 +173,6 @@ class AuthenticationService with ListenableServiceMixin {
_user = null;
await _secureService.clear();
await setFirstTimeInstall(firstTimeInstall);
notifyListeners();
}
}

View File

@ -27,8 +27,8 @@ class DioService {
DioService() {
_dio.options
..baseUrl = kBaseUrl
..connectTimeout = const Duration(seconds: 30)
..receiveTimeout = const Duration(seconds: 30);
..connectTimeout = const Duration(seconds: 5)
..receiveTimeout = const Duration(seconds: 15);
_dio.interceptors.add(
InterceptorsWrapper(

View File

@ -16,8 +16,8 @@ enum LearnPractices { course, module, lesson }
// Voice recording state
enum VoiceRecordingState { pending, recording }
// Levels
enum ProficiencyLevels { a1, a2, b1, b2, none }
// // Levels
// enum ProficiencyLevels { a1, a2, b1, b2, none }
// Progress status
enum ProgressStatuses { pending, started, completed }
@ -29,10 +29,11 @@ enum DuolingoAssessments { speaking, reading, writing, listening }
enum StateObjects {
none,
courses,
startupView,
register,
verifyOtp,
resendOtp,
assessments,
startupView,
learnLessons,
learnModules,
learnCourses,
@ -56,6 +57,7 @@ enum StateObjects {
learnPracticeSample,
learnPracticeAnswer,
loginWithPhoneNumber,
assessmentQuestions,
learnPracticeQuestion,
coursePracticeQuestion,
coursePracticeQuestions,

View File

@ -312,6 +312,10 @@ TextStyle style14MG400 = const TextStyle(
TextStyle style14DG500 =
const TextStyle(color: kcDarkGrey, fontWeight: FontWeight.w500);
TextStyle style18MG500 =
const TextStyle(fontSize: 18,color: kcMediumGrey, fontWeight: FontWeight.w500);
TextStyle style14DG400 = const TextStyle(
color: kcDarkGrey,
);

View File

@ -56,7 +56,7 @@ class AccountPrivacyView extends StackedView<AccountPrivacyViewModel> {
Widget _buildAppbar(AccountPrivacyViewModel viewModel) => SmallAppBar(
showBackButton: true,
onTap: viewModel.pop,
onPop: viewModel.pop,
title: 'Account Privacy',
);

View File

@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:yimaru_app/ui/common/enmus.dart';
import 'package:yimaru_app/ui/views/assessment/screens/assessment_questions_screen.dart';
import 'package:yimaru_app/ui/views/assessment/screens/assessment_intro_screen.dart';
import 'package:yimaru_app/ui/views/assessment/screens/assessment_result_screen.dart';
import 'package:yimaru_app/ui/views/assessment/screens/start_lesson_screen.dart';
import '../../widgets/assessment_loading_screen.dart';
import 'assessment_viewmodel.dart';
class AssessmentView extends StackedView<AssessmentViewModel> {
@ -39,16 +41,29 @@ class AssessmentView extends StackedView<AssessmentViewModel> {
Widget _buildAssessmentScreens(AssessmentViewModel viewModel) => IndexedStack(
index: viewModel.currentPage,
children: _buildScreens(),
children: _buildScreens(viewModel),
);
List<Widget> _buildScreens() => [
_buildAssessmentIntro(),
List<Widget> _buildScreens(AssessmentViewModel viewModel) => [
_buildAssessmentIntroWrapper(viewModel),
_buildAssessment(),
_buildAssessmentResult(),
_buildStartLesson(),
];
Widget _buildAssessmentIntroWrapper(AssessmentViewModel viewModel) =>
viewModel.busy(StateObjects.assessments) || viewModel.assessments.isEmpty
? _buildPageLoadingIndicator(viewModel)
: _buildAssessmentIntro();
Widget _buildPageLoadingIndicator(AssessmentViewModel viewModel) =>
AssessmentLoadingScreen(
isEmpty: viewModel.assessments.isEmpty,
onTap: () async => await viewModel.getAssessments(),
isLoading: viewModel.busy(StateObjects.assessments),
onPop: viewModel.assessments.isEmpty ? viewModel.pop : null,
);
Widget _buildAssessmentIntro() => const AssessmentIntroScreen();
Widget _buildAssessment() => const AssessmentQuestionsScreen();

View File

@ -1,13 +1,14 @@
import 'package:flutter/cupertino.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:yimaru_app/models/assessment_question.dart';
import 'package:yimaru_app/models/option.dart';
import 'package:yimaru_app/services/status_checker_service.dart';
import 'package:yimaru_app/ui/common/enmus.dart';
import '../../../app/app.locator.dart';
import '../../../app/app.router.dart';
import '../../../models/question.dart';
import '../../../models/assessment.dart';
import '../../../services/api_service.dart';
import '../../common/app_colors.dart';
import '../../common/ui_helpers.dart';
@ -33,82 +34,78 @@ class AssessmentViewModel extends BaseViewModel {
PageController get pageController => _pageController;
// Assessment
int _currentQuestion = 0;
int get currentQuestion => _currentQuestion;
List<Assessment> _assessments = [];
List<Question> _assessments = [];
List<Assessment> get assessments => _assessments;
List<Question> get assessments => _assessments;
Assessment? _currentAssessment;
ProficiencyLevels _proficiencyLevel = ProficiencyLevels.none;
Assessment? get currentAssessment => _currentAssessment;
ProficiencyLevels get proficiencyLevel => _proficiencyLevel;
int _currentQuestionIndex = 0;
int get currentQuestionIndex => _currentQuestionIndex;
int _currentAssessmentIndex = 0;
int get currentAssessmentIndex => _currentAssessmentIndex;
String? _proficiencyLevel;
String? get proficiencyLevel => _proficiencyLevel;
final Map<String, dynamic> _selectedAnswers = {};
Map<String, dynamic> get selectedAnswers => _selectedAnswers;
List<AssessmentQuestion> _assessmentQuestions = [];
List<AssessmentQuestion> get assessmentQuestions => _assessmentQuestions;
// User data
final Map<String, dynamic> _userData = {};
Map<String, dynamic> get userData => _userData;
// Assessment
Future<void> setFirstAssessment()async{
_proficiencyLevel = null;
_selectedAnswers.clear();
_currentQuestionIndex = 0;
_pageController.jumpToPage(_currentQuestionIndex);
_currentAssessment = assessments[currentAssessmentIndex];
await getAssessmentQuestions(
_currentAssessment?.id ?? 0);
next();
}
Map<String, dynamic> evaluateAssessment() {
if (_currentQuestion == 5) {
// A1
final correctCount = countCorrectAnswersUntil(5);
if (correctCount > 3) {
return {'continue': true, 'level': ProficiencyLevels.a1};
} else {
return {'continue': false, 'level': ProficiencyLevels.a1};
}
} else if (_currentQuestion == 10) {
// A2
final correctCount = countCorrectAnswersUntil(10);
if (correctCount > 3) {
return {'continue': true, 'level': ProficiencyLevels.a2};
} else {
return {'continue': false, 'level': ProficiencyLevels.a2};
}
} else if (_currentQuestion == 16) {
// B1
final correctCount = countCorrectAnswersUntil(16);
if (correctCount > 4) {
return {'continue': true, 'level': ProficiencyLevels.b1};
} else {
return {'continue': false, 'level': ProficiencyLevels.b1};
}
} else if (_currentQuestion == 22) {
final correctCount = countCorrectAnswersUntil(16);
if (correctCount > 4) {
return {'continue': false, 'level': ProficiencyLevels.b2};
} else {
return {'continue': false, 'level': ProficiencyLevels.b2};
}
bool levelPassed = canPassLevel();
if (levelPassed) {
return {'passed': true, 'level': _currentAssessment?.description};
} else {
return {'continue': true, 'level': ProficiencyLevels.none};
return {'passed': false, 'level': _currentAssessment?.description};
}
}
int countCorrectAnswersUntil(int untilQuestion) {
bool canPassLevel() {
int count = 0;
for (int i = 1; i <= untilQuestion; i++) {
for (int i = 1; i <= _assessmentQuestions.length; i++) {
final answer = _selectedAnswers[i.toString()];
if (answer is Map<String, dynamic> && answer['correct'] == true) {
count++;
}
}
print('COUNT: $count');
print('ASSESSMENT: ${_currentAssessment?.passingScore}');
if (count >= (_currentAssessment?.passingScore ?? 0)) {
return true;
}
return count;
return false;
}
bool isSelectedAnswer({required int question, required String answer}) {
@ -125,7 +122,7 @@ class AssessmentViewModel extends BaseViewModel {
question.toString(): {
'correct': correct,
'option': option?.optionText,
'answer': _assessments[question - 1]
'answer': _assessmentQuestions[question - 1]
.options
?.firstWhere((e) => e.isCorrect ?? false)
.optionText
@ -152,32 +149,41 @@ class AssessmentViewModel extends BaseViewModel {
}
// Question navigation
void nextQuestion() {
_currentQuestion++;
Future<void> nextQuestion() async {
_currentQuestionIndex++;
Map<String, dynamic> response = evaluateAssessment();
if (_currentQuestion == _assessments.length) {
_proficiencyLevel = response['level'];
next();
} else {
if (response['level'] == ProficiencyLevels.none) {
_pageController.jumpToPage(_currentQuestion);
print('LEVEL: $response');
print('LENGTH: ${_assessmentQuestions.length}');
print('INDEX: $_currentQuestionIndex');
if (_currentQuestionIndex == _assessmentQuestions.length) {
_currentAssessmentIndex = _currentAssessmentIndex + 1;
if (_currentAssessmentIndex == _assessments.length) {
_proficiencyLevel = response['level'];
next();
} else {
if (response['continue']) {
_pageController.jumpToPage(_currentQuestion);
if (response['passed']) {
_selectedAnswers.clear();
_currentQuestionIndex = 0;
_proficiencyLevel = response['level'];
_currentAssessment = assessments[currentAssessmentIndex];
await getAssessmentQuestions(
_currentAssessment?.id ?? 0);
_pageController.jumpToPage(_currentQuestionIndex);
} else {
_proficiencyLevel = response['level'];
next();
}
}
} else {
_pageController.jumpToPage(_currentQuestionIndex);
}
rebuildUi();
}
void previousQuestion() {
if (_currentQuestion != 0) {
_currentQuestion--;
if (_currentQuestionIndex != 0) {
_currentQuestionIndex--;
_pageController.previousPage(
duration: const Duration(microseconds: 100), curve: Curves.linear);
rebuildUi();
@ -193,7 +199,7 @@ class AssessmentViewModel extends BaseViewModel {
_currentPage = 0;
rebuildUi();
} else if (_currentPage == 3) {
if (_proficiencyLevel != ProficiencyLevels.none) {
if (_proficiencyLevel != null) {
_currentPage--;
} else {
_currentPage = 0;
@ -240,12 +246,12 @@ class AssessmentViewModel extends BaseViewModel {
// Navigation
void pop() => _navigationService.back();
Future<void> replaceWithStartUp() async =>
await _navigationService.clearStackAndShow(Routes.startupView);
Future<void> navigateToLanguage() async =>
await _navigationService.navigateToLanguageView();
Future<void> replaceWittStartUp() async =>
await _navigationService.clearStackAndShow(Routes.startupView);
// Remote api call
// Complete profile
@ -259,7 +265,7 @@ class AssessmentViewModel extends BaseViewModel {
await _apiService.completeProfile(_userData);
if (response['status'] == ResponseStatus.success) {
clearUserData();
await replaceWithStartUp();
await replaceWittStartUp();
showSuccessToast(response['message']);
} else {
showErrorToast(response['message']);
@ -268,11 +274,23 @@ class AssessmentViewModel extends BaseViewModel {
}
// Assessments
Future<void> getAssessments() async =>
await runBusyFuture(_getAssessments(),
busyObject: StateObjects.assessments);
Future<void> _getAssessments() async {
if (await _statusChecker.checkConnection()) {
_assessments = await _apiService.getAssessments();
}
}
Future<void> getAssessments() async => await runBusyFuture(_getAssessments());
Future<void> getAssessmentQuestions(int id) async =>
await runBusyFuture(_getAssessmentQuestions(id),
busyObject: StateObjects.assessmentQuestions);
Future<void> _getAssessmentQuestions(int id) async {
if (await _statusChecker.checkConnection()) {
_assessmentQuestions = await _apiService.getAssessmentQuestions(id);
}
}
}

View File

@ -10,6 +10,9 @@ import '../assessment_viewmodel.dart';
class AssessmentIntroScreen extends ViewModelWidget<AssessmentViewModel> {
const AssessmentIntroScreen({super.key});
Future<void> _next(AssessmentViewModel viewModel) async =>
viewModel.setFirstAssessment();
@override
Widget build(BuildContext context, AssessmentViewModel viewModel) =>
_buildScaffoldWrapper(viewModel);
@ -95,8 +98,8 @@ class AssessmentIntroScreen extends ViewModelWidget<AssessmentViewModel> {
text: 'Continue',
borderRadius: 12,
foregroundColor: kcWhite,
onTap: () => viewModel.next(),
backgroundColor: kcPrimaryColor,
onTap: () async => await _next(viewModel),
);
Widget _buildSkipButtonWrapper(AssessmentViewModel viewModel) => Padding(

View File

@ -6,28 +6,15 @@ import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
import '../../../widgets/assessment_loading_screen.dart';
import '../assessment_viewmodel.dart';
import 'assessment_loading_screen.dart';
class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
const AssessmentQuestionsScreen({super.key});
@override
Widget build(BuildContext context, AssessmentViewModel viewModel) =>
_buildAssessmentScreens(viewModel);
Widget _buildAssessmentScreens(AssessmentViewModel viewModel) =>
viewModel.isBusy || viewModel.assessments.isEmpty
? _buildPageLoadingIndicator(viewModel)
: _buildAssessmentScreensWrapper(viewModel);
Widget _buildPageLoadingIndicator(AssessmentViewModel viewModel) =>
AssessmentLoadingScreen(
isLoading: viewModel.isBusy,
isEmpty: viewModel.assessments.isEmpty,
onTap: () async => await viewModel.getAssessments(),
onPop: viewModel.assessments.isEmpty ? viewModel.pop : null,
);
_buildAssessmentScreensWrapper(viewModel);
Widget _buildAssessmentScreensWrapper(AssessmentViewModel viewModel) =>
PopScope(
@ -52,7 +39,7 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
onClose: viewModel.abort,
showLanguageSelection: false,
onPop: viewModel.previousQuestion,
showBackButton: viewModel.currentQuestion == 0 ? false : true,
showBackButton: viewModel.currentQuestionIndex == 0 ? false : true,
);
Widget _buildExpandedBody(AssessmentViewModel viewModel) =>
@ -65,7 +52,7 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
Widget _buildAssessment(AssessmentViewModel viewModel) => PageView.builder(
controller: viewModel.pageController,
itemCount: viewModel.assessments.length,
itemCount: viewModel.assessmentQuestions.length,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (cotext, index) =>
_buildBodyScroller(index: index, viewModel: viewModel),
@ -98,7 +85,7 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
Widget _buildTitle(
{required int index, required AssessmentViewModel viewModel}) =>
Text(
'Q${index + 1}. ${viewModel.assessments[index].questionText} ',
'Q${index + 1}. ${viewModel.assessmentQuestions[index].questionText} ',
style: style16DG600,
);
@ -107,15 +94,18 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: viewModel.assessments[index].options?.length,
itemCount: viewModel.assessmentQuestions[index].options?.length,
itemBuilder: (context, inner) => _buildAnswer(
onTap: () => viewModel.setSelectedAnswer(
question: index + 1,
option: viewModel.assessments[index].options?[inner]),
title: viewModel.assessments[index].options?[inner].optionText ?? '',
option: viewModel.assessmentQuestions[index].options?[inner]),
title:
viewModel.assessmentQuestions[index].options?[inner].optionText ??
'',
selected: viewModel.isSelectedAnswer(
question: index + 1,
answer: viewModel.assessments[index].options?[inner].optionText ??
answer: viewModel
.assessmentQuestions[index].options?[inner].optionText ??
''),
),
);
@ -150,8 +140,8 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
onTap: viewModel.selectedAnswers.containsKey(question.toString())
? () => viewModel.nextQuestion()
: null,
text: viewModel.currentQuestion == viewModel.assessments.length - 1
? 'Finish'
text: viewModel.currentQuestionIndex == viewModel.assessmentQuestions.length - 1
? 'Finish Level'
: 'Continue',
);
}

View File

@ -75,7 +75,7 @@ class AssessmentResultScreen extends ViewModelWidget<AssessmentViewModel> {
];
Widget _buildTitle(AssessmentViewModel viewModel) => Text(
'Youre likely a ${viewModel.proficiencyLevel.name.toUpperCase()} speaker!',
'Youre likely a ${viewModel.proficiencyLevel?.toUpperCase()} speaker!',
style: style25DG600,
textAlign: TextAlign.center,
);
@ -87,12 +87,12 @@ class AssessmentResultScreen extends ViewModelWidget<AssessmentViewModel> {
);
Widget _buildIconWrapper(AssessmentViewModel viewModel) =>
viewModel.proficiencyLevel != ProficiencyLevels.none
viewModel.proficiencyLevel != null
? _buildIcon(viewModel)
: Container();
Widget _buildIcon(AssessmentViewModel viewModel) => SvgPicture.asset(
'assets/icons/${viewModel.proficiencyLevel.name.substring(0, 1)}_${viewModel.proficiencyLevel.name.substring(1)}.svg');
'assets/icons/${viewModel.proficiencyLevel?.substring(0, 1).toLowerCase()}_${viewModel.proficiencyLevel?.substring(1).toLowerCase()}.svg');
Widget _buildSecondarySubtitle() => Text(
'Let\'s start your practice',

View File

@ -14,9 +14,9 @@ class StartLessonScreen extends ViewModelWidget<AssessmentViewModel> {
const StartLessonScreen({super.key});
Future<void> _start(AssessmentViewModel viewModel) async {
if (viewModel.proficiencyLevel != ProficiencyLevels.none) {
if (viewModel.proficiencyLevel != null) {
Map<String, dynamic> data = {
'knowledge_level': viewModel.proficiencyLevel.name.toUpperCase()
'knowledge_level': viewModel.proficiencyLevel?.toUpperCase()
};
viewModel.addUserData(data);

View File

@ -49,7 +49,7 @@ class CallSupportView extends StackedView<CallSupportViewModel> {
Widget _buildAppbar(CallSupportViewModel viewModel) => SmallAppBar(
showBackButton: true,
onTap: viewModel.pop,
onPop: viewModel.pop,
title: 'Call Support',
);

View File

@ -56,7 +56,7 @@ class CourseView extends StackedView<CourseViewModel> {
);
Widget _buildAppBar(CourseViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop,
onPop: viewModel.pop,
showBackButton: true,
);

View File

@ -57,7 +57,7 @@ class CourseLessonView extends StackedView<CourseLessonViewModel> {
);
Widget _buildAppBar(CourseLessonViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop,
onPop: viewModel.pop,
showBackButton: true,
title: 'Course Detail',
);

View File

@ -57,7 +57,7 @@ class CourseLessonDetailView extends StackedView<CourseLessonDetailViewModel> {
child: _buildAppBar(viewModel));
Widget _buildAppBar(CourseLessonDetailViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop,
onPop: viewModel.pop,
showBackButton: true,
title: lesson.title ?? '',
);

View File

@ -50,7 +50,7 @@ class CoursePaymentView extends StackedView<CoursePaymentViewModel> {
);
Widget _buildAppBar(CoursePaymentViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop,
onPop: viewModel.pop,
showBackButton: true,
);

View File

@ -53,7 +53,7 @@ class CoursePracticeView extends StackedView<CoursePracticeViewModel> {
);
Widget _buildAppBar(CoursePracticeViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop,
onPop: viewModel.pop,
showBackButton: true,
);

View File

@ -4,7 +4,7 @@ import 'package:stacked_services/stacked_services.dart';
import '../../../app/app.locator.dart';
import '../../../models/option.dart';
import '../../../models/question.dart';
import '../../../models/assessment_question.dart';
import '../../../services/api_service.dart';
import '../../../services/status_checker_service.dart';
import '../../common/app_colors.dart';
@ -38,13 +38,14 @@ class CoursePracticeQuestionViewModel extends FormViewModel {
bool get focusAnswer => _focusAnswer;
Question? _currentQuestion;
AssessmentQuestion? _currentQuestion;
Question? get currentQuestion => _currentQuestion;
AssessmentQuestion? get currentQuestion => _currentQuestion;
List<Question> _coursePracticeQuestions = [];
List<AssessmentQuestion> _coursePracticeQuestions = [];
List<Question> get coursePracticeQuestions => _coursePracticeQuestions;
List<AssessmentQuestion> get coursePracticeQuestions =>
_coursePracticeQuestions;
int _currentQuestionIndex = 0;

View File

@ -59,7 +59,7 @@ class CourseSubcategoryView extends StackedView<CourseSubcategoryViewModel> {
);
Widget _buildAppBar(CourseSubcategoryViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop,
onPop: viewModel.pop,
showBackButton: true,
);

View File

@ -53,7 +53,7 @@ class DownloadsView extends StackedView<DownloadsViewModel> {
];
Widget _buildAppbar(DownloadsViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop,
onPop: viewModel.pop,
showBackButton: true,
title: 'Offline Downloads',
);

View File

@ -9,8 +9,10 @@ import 'failure_viewmodel.dart';
class FailureView extends StackedView<FailureViewModel> {
final String label;
final GestureTapCallback onTap;
const FailureView({Key? key, required this.label}) : super(key: key);
const FailureView({Key? key, required this.onTap, required this.label})
: super(key: key);
@override
FailureViewModel viewModelBuilder(BuildContext context) => FailureViewModel();
@ -48,19 +50,38 @@ class FailureView extends StackedView<FailureViewModel> {
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: _buildUpperColumnChildren(),
children: _buildColumnChildren(),
);
List<Widget> _buildUpperColumnChildren() =>
List<Widget> _buildColumnChildren() =>
[_buildIconWrapper(), _buildSafeWrapper()];
Widget _buildSafeWrapper() => SafeArea(child: _buildLoadingTextContainer());
Widget _buildLoadingTextContainer() => Padding(
padding: const EdgeInsets.only(bottom: 50),
child: _buildLoadingTextWrapper(),
Widget _buildIconWrapper() => Padding(
padding: const EdgeInsets.only(top: 100),
child: _buildIcon(),
);
Widget _buildIcon() => SvgPicture.asset('assets/icons/logo.svg', height: 50);
Widget _buildSafeWrapper() => SafeArea(child: _buildBottomSectionWrapper());
Widget _buildBottomSectionWrapper() => Padding(
padding: const EdgeInsets.only(bottom: 50),
child: _buildBottomSectionColumn(),
);
Widget _buildBottomSectionColumn() => Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: _buildBottomSectionChildren(),
);
List<Widget> _buildBottomSectionChildren() => [
_buildLoadingTextWrapper(),
verticalSpaceSmall,
_buildRetryButtonWrapper()
];
Widget _buildLoadingTextWrapper() => Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
@ -85,10 +106,14 @@ class FailureView extends StackedView<FailureViewModel> {
Widget _buildIndicator() =>
const CustomCircularProgressIndicator(color: kcWhite);
Widget _buildIconWrapper() => Padding(
padding: const EdgeInsets.only(top: 100),
child: _buildIcon(),
Widget _buildRetryButtonWrapper() => GestureDetector(
onTap: onTap,
child: _buildRetryButton(),
);
Widget _buildIcon() => SvgPicture.asset('assets/icons/logo.svg', height: 50);
Widget _buildRetryButton() => Text(
'Retry',
style: style16W600.copyWith(
fontStyle: FontStyle.italic, decoration: TextDecoration.underline),
);
}

View File

@ -1,16 +1,21 @@
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/enmus.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 '../../common/enmus.dart';
import '../../widgets/page_loading_indicator.dart';
import 'home_viewmodel.dart';
class HomeView extends StackedView<HomeViewModel> {
const HomeView({Key? key}) : super(key: key);
@override
void onViewModelReady(HomeViewModel viewModel) async {
await viewModel.inAppUpdate();
super.onViewModelReady(viewModel);
}
@override
HomeViewModel viewModelBuilder(BuildContext context) => HomeViewModel();
@ -20,8 +25,6 @@ class HomeView extends StackedView<HomeViewModel> {
BuildContext context, HomeViewModel viewModel, Widget? child) =>
_buildScaffold(viewModel);
Widget _buildScaffold(HomeViewModel viewModel) => Scaffold(
body: getViewForIndex(viewModel.currentPage),
bottomNavigationBar: _buildBottomNav(viewModel),
@ -56,22 +59,22 @@ class HomeView extends StackedView<HomeViewModel> {
label: 'Profile',
icon: _buildProfileIcon(),
);
}
Widget _buildLearnIcon() => const Icon(Icons.school);
Widget _buildLearnIcon() => const Icon(Icons.school);
Widget _buildCourseIcon() => const Icon(Icons.book);
Widget _buildCourseIcon() => const Icon(Icons.book);
Widget _buildProfileIcon() => const Icon(Icons.person);
Widget _buildProfileIcon() => const Icon(Icons.person);
Widget getViewForIndex(int index) {
switch (index) {
case 0:
return const LearnProgramView();
case 1:
return const ComingSoon();
Widget getViewForIndex(int index) {
switch (index) {
case 0:
return const LearnProgramView();
case 1:
return const ComingSoon();
default:
return const ProfileView();
default:
return const ProfileView();
}
}
}

View File

@ -1,21 +1,19 @@
import 'package:yimaru_app/app/app.bottomsheets.dart';
import 'package:yimaru_app/app/app.locator.dart';
import 'package:yimaru_app/app/app.router.dart';
import 'package:yimaru_app/models/user.dart';
import 'package:yimaru_app/services/status_checker_service.dart';
import 'package:yimaru_app/ui/common/app_strings.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import '../../../services/api_service.dart';
import '../../../services/authentication_service.dart';
import '../../../services/image_downloader_service.dart';
import '../../common/enmus.dart';
import '../../common/ui_helpers.dart';
import '../../../services/in_app_update_service.dart';
class HomeViewModel extends ReactiveViewModel {
// Dependency injection
final _statusChecker = locator<StatusCheckerService>();
final _bottomSheetService = locator<BottomSheetService>();
final _inAppUpdateService = locator<InAppUpdateService>();
final _authenticationService = locator<AuthenticationService>();
@override
@ -46,9 +44,12 @@ class HomeViewModel extends ReactiveViewModel {
rebuildUi();
}
// Remote api calls
// In-app update
Future<void> inAppUpdate() async {
if (await _statusChecker.checkConnection()) {
await _inAppUpdateService.checkForUpdate();
}
}
}

View File

@ -73,7 +73,7 @@ class LanguageView extends StackedView<LanguageViewModel> {
Widget _buildAppbar(LanguageViewModel viewModel) => SmallAppBar(
showBackButton: true,
onTap: viewModel.pop,
onPop: viewModel.pop,
title: 'Language Preference',
);

View File

@ -55,7 +55,7 @@ class LearnCourseView extends StackedView<LearnCourseViewModel> {
);
Widget _buildAppBar(LearnCourseViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop,
onPop: viewModel.pop,
showBackButton: true,
);

View File

@ -77,7 +77,7 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
);
Widget _buildAppBar(LearnLessonViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop,
onPop: viewModel.pop,
showBackButton: true,
);

View File

@ -17,7 +17,7 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
Future<void> _navigate(LearnLessonDetailViewModel viewModel) async {
await viewModel.pause();
await viewModel.navigateToLearnPractice();
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
}
@override
@ -60,7 +60,7 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
child: _buildAppBar(viewModel));
Widget _buildAppBar(LearnLessonDetailViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop,
onPop: viewModel.pop,
showBackButton: true,
);

View File

@ -4,6 +4,7 @@ import 'package:stacked_services/stacked_services.dart';
import 'package:yimaru_app/ui/common/enmus.dart';
import '../../../app/app.locator.dart';
import '../../../app/app.router.dart';
import '../../../services/status_checker_service.dart';
class LearnLessonDetailViewModel extends BaseViewModel {
@ -44,6 +45,6 @@ class LearnLessonDetailViewModel extends BaseViewModel {
// Navigation
void pop() => _navigationService.back();
Future<void> navigateToLearnPractice() async {}
// await _navigationService.navigateToLearnPracticeView();
Future<void> navigateToLearnPractice(int id) async => await _navigationService
.navigateToLearnPracticeView(id: id, practice: LearnPractices.lesson);
}

View File

@ -19,7 +19,7 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
@override
void onViewModelReady(LearnModuleViewModel viewModel) async {
await viewModel.getLearnModules(1);
await viewModel.getLearnModules(course.id ?? 0);
super.onViewModelReady(viewModel);
}
@ -58,7 +58,7 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
);
Widget _buildAppBar(LearnModuleViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop,
onPop: viewModel.pop,
showBackButton: true,
);
@ -116,10 +116,10 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => _buildTile(
module: viewModel.modules[index],
onModuleTap: () async =>
await viewModel.navigateToLearnLesson(viewModel.modules[index]),
onPracticeTap: () async => await viewModel
.navigateToLearnPractice(viewModel.modules[index].id ?? 0),
onModuleTap: () async =>
await viewModel.navigateToLearnLesson(viewModel.modules[index]),
),
);

View File

@ -2,6 +2,7 @@ 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_loading_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_practice_questions_screen.dart';
@ -78,7 +79,20 @@ class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
Widget _buildBodyState(LearnPracticeViewModel viewModel) =>
viewModel.busy(StateObjects.learnPractices)
? const PageLoadingIndicator()
: _buildBody(viewModel);
: viewModel.practices.isEmpty || viewModel.questions.isEmpty
? _buildPageLoadingIndicator(viewModel)
: _buildBody(viewModel);
Widget _buildPageLoadingIndicator(LearnPracticeViewModel viewModel) =>
LearnLoadingScreen(
isLoading: viewModel.busy(StateObjects.learnPractices),
onTap: () async =>
await viewModel.getLearnPractices(id: id, practice: practice),
onPop: viewModel.practices.isEmpty || viewModel.questions.isEmpty
? viewModel.pop
: null,
isEmpty: viewModel.practices.isEmpty || viewModel.questions.isEmpty,
);
Widget _buildBody(LearnPracticeViewModel viewModel) => IndexedStack(
index: viewModel.currentPage, children: _buildScreens(viewModel));

View File

@ -149,6 +149,10 @@ class LearnPracticeViewModel extends ReactiveViewModel {
await _audioPlayerService.playUrl(question.voicePrompt ?? '');
}
Future<void> replayVoicePrompt(LearnQuestion question) async {
await _audioPlayerService.playUrl(question.voicePrompt ?? '');
}
Future<void> playResult(
{required Map<String, dynamic> answer, required Voice voice}) async {
setBusyObject(playing: voice, object: answer['busy_object']);
@ -222,6 +226,7 @@ class LearnPracticeViewModel extends ReactiveViewModel {
_questionSetController.nextPage(
duration: const Duration(milliseconds: 350),
curve: Curves.easeInOutCubic);
await playVoicePrompt(_questions[index]);
} else {
goTo(3);
}

View File

@ -50,7 +50,7 @@ class FinishLearnPracticeScreen
Widget _buildAppBar(LearnPracticeViewModel viewModel) => SmallAppBar(
showBackButton: true,
onTap: viewModel.goBack,
onPop: viewModel.goBack,
title: 'Practice Speaking',
);

View File

@ -30,8 +30,8 @@ class InteractLearnPracticeScreen
viewModel.stopRecording();
}
void _start(LearnPracticeViewModel viewModel) =>
viewModel.playVoicePrompt(question);
void _reply(LearnPracticeViewModel viewModel) =>
viewModel.replayVoicePrompt(question);
Future<void> _stop(LearnPracticeViewModel viewModel) async =>
await viewModel.nextQuestion(index: index, question: question);
@ -116,7 +116,7 @@ class InteractLearnPracticeScreen
required LearnPracticeViewModel viewModel}) =>
SmallAppBar(
showBackButton: true,
onTap: () async =>
onPop: () async =>
await _showSheet(context: context, viewModel: viewModel),
title: 'Practice Speaking ($index/${viewModel.questions.length})');
@ -263,7 +263,7 @@ class InteractLearnPracticeScreen
: kcLightGrey,
onTap: viewModel.recordingState == VoiceRecordingState.pending &&
viewModel.player.state != PlayerState.playing
? () => _start(viewModel)
? () => _reply(viewModel)
: null,
);
@ -272,15 +272,15 @@ class InteractLearnPracticeScreen
Widget _buildMicButton(LearnPracticeViewModel viewModel) => ElevatedButton(
style: ButtonStyle(
shadowColor: const WidgetStatePropertyAll(kcWhite),
shape: const WidgetStatePropertyAll(CircleBorder()),
padding: const WidgetStatePropertyAll(EdgeInsets.all(15)),
backgroundColor: WidgetStatePropertyAll(
viewModel.player.state == PlayerState.playing ||
viewModel.busy(StateObjects.recordLearnPracticeAnswer)
? kcVeryLightGrey
: kcPrimaryColor,
),
shadowColor: const WidgetStatePropertyAll(kcWhite),
shape: const WidgetStatePropertyAll(CircleBorder()),
padding: const WidgetStatePropertyAll(EdgeInsets.all(15)),
),
onPressed: () async => viewModel.player.state == PlayerState.playing ||
viewModel.busy(StateObjects.recordLearnPracticeAnswer)

View File

@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import 'package:yimaru_app/ui/widgets/no_data_indicator.dart';
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
import 'package:yimaru_app/ui/widgets/small_app_bar.dart';
import '../../../common/app_colors.dart';
import '../../../common/ui_helpers.dart';
import '../../../widgets/large_app_bar.dart';
import '../../../widgets/refresh_button.dart';
class LearnLoadingScreen extends StatelessWidget {
final bool isEmpty;
final bool isLoading;
final GestureTapCallback? onPop;
final GestureTapCallback? onTap;
const LearnLoadingScreen(
{super.key,
this.onTap,
this.onPop,
required this.isEmpty,
required this.isLoading});
@override
Widget build(BuildContext context) => _buildScaffoldWrapper();
Widget _buildScaffoldWrapper() => Scaffold(
backgroundColor: kcBackgroundColor,
body: _buildScaffold(),
);
Widget _buildScaffold() => SafeArea(child: _buildStack());
Widget _buildStack() => Stack(
children: [
_buildColumn(),
if (isEmpty) _buildRefreshButtonWrapper(),
if (isLoading) _buildPageIndicator()
],
);
Widget _buildColumn() => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _buildColumnChildren(),
);
List<Widget> _buildColumnChildren() =>
[verticalSpaceMedium, _buildAppBarWrapper(), _buildBody()];
Widget _buildAppBarWrapper() => Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: _buildAppBar(),
);
Widget _buildAppBar() => SmallAppBar(onPop: onPop, showBackButton: true);
Widget _buildBody() => Expanded(child: Container());
Widget _buildPageIndicator() => const PageLoadingIndicator();
Widget _buildRefreshButtonWrapper() => Align(
alignment: Alignment.center,
child:_buildRefreshButton());
Widget _buildRefreshButton()=> NoDataIndicator(title:'No practice available!' ,onTap: onTap,);
}

View File

@ -72,7 +72,7 @@ class LearnPracticeIntroScreen extends ViewModelWidget<LearnPracticeViewModel> {
SmallAppBar(
showBackButton: true,
title: 'Practice Speaking',
onTap: () async =>
onPop: () async =>
await _showSheet(context: context, viewModel: viewModel),
);

View File

@ -40,7 +40,7 @@ class LearnPracticeQuestionsScreen
required LearnQuestion question,
}) =>
[
_buildStartLearnPracticeScreen(index: index, question: question),
if(index ==1) _buildStartLearnPracticeScreen(index: index, question: question),
_buildInteractLearnPracticeScreen(index: index, question: question)
];

View File

@ -86,7 +86,7 @@ class LearnPracticeResultScreen
SmallAppBar(
title: 'Result',
showBackButton: true,
onTap: () async =>
onPop: () async =>
await _showSheet(context: context, viewModel: viewModel),
);

View File

@ -97,7 +97,7 @@ class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
required LearnPracticeViewModel viewModel}) =>
SmallAppBar(
showBackButton: true,
onTap: () async =>
onPop: () async =>
await _showSheet(context: context, viewModel: viewModel),
title: 'Practice Speaking ($index/${viewModel.questions.length})');

View File

@ -155,7 +155,6 @@ class LoginViewModel extends ReactiveViewModel
// Navigation
Future<void> navigateToRegister() async =>
await _navigationService.navigateToRegisterView();
@ -165,9 +164,6 @@ class LoginViewModel extends ReactiveViewModel
Future<void> replaceWithStartUp() async =>
await _navigationService.clearStackAndShow(Routes.startupView);
// Remote api calls
// Login with email

View File

@ -36,6 +36,7 @@ class OnboardingView extends StackedView<OnboardingViewModel>
void _initClearData() {
topicController.clear();
regionController.clear();
fullNameController.clear();
challengeController.clear();
occupationController.clear();

View File

@ -191,7 +191,7 @@ class OnboardingViewModel extends ReactiveViewModel
final List<String> _topics = [
'Food & Cooking',
' Hobbies, Sports, Music',
'Hobbies, Sports, Music',
'Tech, News, Business',
'Travel, Places, Culture',
'Other'
@ -547,6 +547,7 @@ class OnboardingViewModel extends ReactiveViewModel
// Reset country region form screen
void resetCountryRegionFormScreen() {
_focusRegion = false;
_selectedCountry = 'Ethiopia';
rebuildUi();
}
@ -613,6 +614,4 @@ class OnboardingViewModel extends ReactiveViewModel
Future<void> navigateToAssessment() async =>
await _navigationService.navigateToAssessmentView(data: _userData);
}

View File

@ -55,7 +55,7 @@ class PrivacyPolicyView extends StackedView<PrivacyPolicyViewModel> {
Widget _buildAppbar(PrivacyPolicyViewModel viewModel) => SmallAppBar(
showBackButton: true,
onTap: viewModel.pop,
onPop: viewModel.pop,
title: 'Privacy Policy',
);

View File

@ -3,6 +3,7 @@ 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/common/ui_helpers.dart';
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
import 'package:yimaru_app/ui/widgets/profile_card.dart';
import 'package:yimaru_app/ui/widgets/profile_image.dart';
import 'package:yimaru_app/ui/widgets/view_profile_button.dart';
@ -155,7 +156,7 @@ class ProfileView extends StackedView<ProfileViewModel> {
children: _buildSettingsChildren(viewModel));
List<Widget> _buildSettingsChildren(ProfileViewModel viewModel) => [
_buildDownloadsCard(viewModel),
// _buildDownloadsCard(viewModel),
_buildProgressCard(viewModel),
_buildAccountCard(viewModel),
_buildSupportCard(viewModel)
@ -191,7 +192,7 @@ class ProfileView extends StackedView<ProfileViewModel> {
Widget _buildLogOutButton(ProfileViewModel viewModel) => CustomElevatedButton(
height: 55,
text: 'Log Out',
text: 'Logout',
borderRadius: 12,
foregroundColor: kcRed,
backgroundColor: kcRed.withOpacity(0.25),

View File

@ -61,13 +61,6 @@ class ProfileViewModel extends ReactiveViewModel {
}
}
// Logout
Future<void> _logout() async {
await _googleAuthService.logout();
await _authenticationService.logout();
await _navigationService.replaceWithLoginView();
}
// Dialog
Future<bool?> showAbortDialog() async {
DialogResponse? response = await _dialogService.showDialog(
@ -82,13 +75,6 @@ class ProfileViewModel extends ReactiveViewModel {
return response?.confirmed;
}
Future<void> logout() async {
bool? response = await showAbortDialog();
if (response != null && response) {
await _logout();
}
}
// Navigation
void pop() => _navigationService.back();
@ -107,6 +93,9 @@ class ProfileViewModel extends ReactiveViewModel {
Future<void> navigateToSupport() async =>
await _navigationService.navigateToSupportView();
Future<void> navigateToLogin() async =>
await _navigationService.clearStackAndShow(Routes.loginView);
// Remote api call
// Update profile
@ -115,10 +104,21 @@ class ProfileViewModel extends ReactiveViewModel {
Future<void> _updateProfilePicture(String image) async {
if (await _statusChecker.checkConnection()) {
Map<String, dynamic> data = {
'profile_picture_url': image,
};
Map<String, dynamic> data = {'profile_picture_url': image};
await _apiService.updateProfileImage(data: data, userId: _user?.userId);
}
}
Future<void> logout() async {
bool? response = await showAbortDialog();
if (response != null && response) {
await _logout();
}
}
Future<void> _logout() async {
await _googleAuthService.logout();
await _authenticationService.logout();
await navigateToLogin();
}
}

View File

@ -34,13 +34,13 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
Future<void> _update(ProfileDetailViewModel viewModel) async {
Map<String, dynamic> data = {
'region':regionController.text,
'region': regionController.text,
'gender': viewModel.selectedGender,
'last_name': lastNameController.text,
'country': viewModel.selectedCountry,
'first_name': firstNameController.text,
'occupation': occupationController.text,
'birth_day': DateFormat('d MMM, yyyy').format(DateTime.now()),
'birth_day': DateFormat('yyyy-MM-dd').format(DateTime.now()),
};
viewModel.addUserData(data);
@ -75,7 +75,6 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
viewModel.clearUserData();
viewModel.setGender(viewModel.user?.gender ?? '');
viewModel.setSelectedCountry(viewModel.user?.country ?? 'Ethiopia');
}
@override
@ -147,7 +146,7 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
];
Widget _buildAppbar(ProfileDetailViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop,
onPop: viewModel.pop,
showBackButton: true,
title: 'Edit Profile',
);
@ -192,9 +191,8 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
verticalSpaceSmall,
_buildCountryDropdown(viewModel),
verticalSpaceMedium,
_buildRegionFormFieldWrapper(viewModel),
_buildRegionFormFieldWrapper(viewModel),
verticalSpaceMedium,
_buildOccupationDropdownWrapper(viewModel),
verticalSpaceLarge,
_buildLowerColumn(viewModel)
@ -533,9 +531,10 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
);
List<Widget> _buildRegionFormFieldChildren(
ProfileDetailViewModel viewModel) =>
ProfileDetailViewModel viewModel) =>
[
_buildRegionFormFieldLabel(),verticalSpaceSmall,
_buildRegionFormFieldLabel(),
verticalSpaceSmall,
_buildRegionFormField(viewModel),
if (viewModel.hasRegionValidationMessage && viewModel.focusRegion)
verticalSpaceTiny,

View File

@ -47,7 +47,6 @@ class ProfileDetailViewModel extends ReactiveViewModel
String? get selectedGender => _selectedGender;
// First name
bool _focusPhoneNumber = false;
@ -63,7 +62,6 @@ class ProfileDetailViewModel extends ReactiveViewModel
String get selectedCountry => _selectedCountry;
// Occupation
bool _focusOccupation = false;
@ -97,7 +95,6 @@ class ProfileDetailViewModel extends ReactiveViewModel
rebuildUi();
}
// Phone number
void setPhoneNumberFocus() {
_focusPhoneNumber = true;
@ -113,167 +110,165 @@ class ProfileDetailViewModel extends ReactiveViewModel
// Country
// Country
List<String> getCountries() => [
"Afghanistan",
"Albania",
"Algeria",
"Andorra",
"Angola",
"Argentina",
"Armenia",
"Australia",
"Austria",
"Azerbaijan",
"Bahrain",
"Bangladesh",
"Belarus",
"Belgium",
"Belize",
"Benin",
"Bhutan",
"Bolivia",
"Bosnia and Herzegovina",
"Botswana",
"Brazil",
"Brunei",
"Bulgaria",
"Burkina Faso",
"Burundi",
"Cambodia",
"Cameroon",
"Canada",
"Chad",
"Chile",
"China",
"Colombia",
"Comoros",
"Congo",
"Costa Rica",
"Croatia",
"Cuba",
"Cyprus",
"Czech Republic",
"Denmark",
"Djibouti",
"Dominican Republic",
"Ecuador",
"Egypt",
"El Salvador",
"Eritrea",
"Estonia",
"Eswatini",
"Ethiopia",
"Finland",
"France",
"Gabon",
"Gambia",
"Georgia",
"Germany",
"Ghana",
"Greece",
"Guatemala",
"Guinea",
"Haiti",
"Honduras",
"Hungary",
"Iceland",
"India",
"Indonesia",
"Iran",
"Iraq",
"Ireland",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Jordan",
"Kazakhstan",
"Kenya",
"Kuwait",
"Kyrgyzstan",
"Laos",
"Latvia",
"Lebanon",
"Liberia",
"Libya",
"Lithuania",
"Luxembourg",
"Madagascar",
"Malawi",
"Malaysia",
"Maldives",
"Mali",
"Malta",
"Mexico",
"Moldova",
"Monaco",
"Mongolia",
"Morocco",
"Mozambique",
"Myanmar",
"Namibia",
"Nepal",
"Netherlands",
"New Zealand",
"Nicaragua",
"Niger",
"Nigeria",
"North Korea",
"Norway",
"Oman",
"Pakistan",
"Panama",
"Paraguay",
"Peru",
"Philippines",
"Poland",
"Portugal",
"Qatar",
"Romania",
"Russia",
"Rwanda",
"Saudi Arabia",
"Senegal",
"Serbia",
"Singapore",
"Slovakia",
"Slovenia",
"Somalia",
"South Africa",
"South Korea",
"Spain",
"Sri Lanka",
"Sudan",
"Sweden",
"Switzerland",
"Syria",
"Taiwan",
"Tajikistan",
"Tanzania",
"Thailand",
"Tunisia",
"Turkey",
"Uganda",
"Ukraine",
"United Arab Emirates",
"United Kingdom",
"United States",
"Uruguay",
"Uzbekistan",
"Venezuela",
"Vietnam",
"Yemen",
"Zambia",
"Zimbabwe"
];
"Afghanistan",
"Albania",
"Algeria",
"Andorra",
"Angola",
"Argentina",
"Armenia",
"Australia",
"Austria",
"Azerbaijan",
"Bahrain",
"Bangladesh",
"Belarus",
"Belgium",
"Belize",
"Benin",
"Bhutan",
"Bolivia",
"Bosnia and Herzegovina",
"Botswana",
"Brazil",
"Brunei",
"Bulgaria",
"Burkina Faso",
"Burundi",
"Cambodia",
"Cameroon",
"Canada",
"Chad",
"Chile",
"China",
"Colombia",
"Comoros",
"Congo",
"Costa Rica",
"Croatia",
"Cuba",
"Cyprus",
"Czech Republic",
"Denmark",
"Djibouti",
"Dominican Republic",
"Ecuador",
"Egypt",
"El Salvador",
"Eritrea",
"Estonia",
"Eswatini",
"Ethiopia",
"Finland",
"France",
"Gabon",
"Gambia",
"Georgia",
"Germany",
"Ghana",
"Greece",
"Guatemala",
"Guinea",
"Haiti",
"Honduras",
"Hungary",
"Iceland",
"India",
"Indonesia",
"Iran",
"Iraq",
"Ireland",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Jordan",
"Kazakhstan",
"Kenya",
"Kuwait",
"Kyrgyzstan",
"Laos",
"Latvia",
"Lebanon",
"Liberia",
"Libya",
"Lithuania",
"Luxembourg",
"Madagascar",
"Malawi",
"Malaysia",
"Maldives",
"Mali",
"Malta",
"Mexico",
"Moldova",
"Monaco",
"Mongolia",
"Morocco",
"Mozambique",
"Myanmar",
"Namibia",
"Nepal",
"Netherlands",
"New Zealand",
"Nicaragua",
"Niger",
"Nigeria",
"North Korea",
"Norway",
"Oman",
"Pakistan",
"Panama",
"Paraguay",
"Peru",
"Philippines",
"Poland",
"Portugal",
"Qatar",
"Romania",
"Russia",
"Rwanda",
"Saudi Arabia",
"Senegal",
"Serbia",
"Singapore",
"Slovakia",
"Slovenia",
"Somalia",
"South Africa",
"South Korea",
"Spain",
"Sri Lanka",
"Sudan",
"Sweden",
"Switzerland",
"Syria",
"Taiwan",
"Tajikistan",
"Tanzania",
"Thailand",
"Tunisia",
"Turkey",
"Uganda",
"Ukraine",
"United Arab Emirates",
"United Kingdom",
"United States",
"Uruguay",
"Uzbekistan",
"Venezuela",
"Vietnam",
"Yemen",
"Zambia",
"Zimbabwe"
];
void setSelectedCountry(String value) {
_selectedCountry = value;
rebuildUi();
}
// Occupation
void setOccupationFocus() {
_focusOccupation = true;

View File

@ -56,7 +56,7 @@ class ProgressView extends StackedView<ProgressViewModel> {
Widget _buildAppbar(ProgressViewModel viewModel) => SmallAppBar(
title: 'My Progress',
showBackButton: true,
onTap: viewModel.pop,
onPop: viewModel.pop,
);
Widget _buildContentScrollViewWrapper(ProgressViewModel viewModel) =>

View File

@ -11,7 +11,6 @@ import 'package:yimaru_app/ui/common/ui_helpers.dart';
import '../../../app/app.locator.dart';
import '../../../models/user.dart';
import '../../../services/google_auth_service.dart';
import '../../../services/smart_auth_service.dart';
import '../../../services/status_checker_service.dart';
class RegisterViewModel extends ReactiveViewModel

View File

@ -51,8 +51,8 @@ class StartupView extends StackedView<StartupViewModel> {
Widget _buildColumn() => Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _buildUpperColumnChildren(),
);

View File

@ -1,7 +1,6 @@
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';
@ -10,14 +9,12 @@ import '../../../services/api_service.dart';
import '../../../services/image_downloader_service.dart';
import '../../../services/status_checker_service.dart';
import '../../common/enmus.dart';
import '../../common/ui_helpers.dart';
class StartupViewModel extends ReactiveViewModel {
// Dependency injection
final _apiService = locator<ApiService>();
final _statusChecker = locator<StatusCheckerService>();
final _navigationService = locator<NavigationService>();
final _inAppUpdateService = locator<InAppUpdateService>();
final _authenticationService = locator<AuthenticationService>();
final _imageDownloaderService = locator<ImageDownloaderService>();
@ -30,15 +27,12 @@ class StartupViewModel extends ReactiveViewModel {
User? get user => _user;
// Place anything here that needs to happen before we get into the application
// Main startup and navigation logic
Future runStartupLogic() async {
final loggedIn = await _authenticationService.userLoggedIn();
final firstTimeInstall = await _authenticationService.isFirstTimeInstall();
await _inAppUpdate();
if (firstTimeInstall) {
await _navigationService.replaceWithWelcomeView();
} else {
@ -52,63 +46,45 @@ class StartupViewModel extends ReactiveViewModel {
}
}
// Navigation
Future<void> replaceWithFailure() async => await _navigationService
.replaceWithFailureView(label: 'Check you internet connection');
Future<void> replaceWithFailure() async => await _navigationService.replaceWithFailureView(
label: 'Check you internet connection',
onTap: () async => await _getProfileStatus());
Future<void> replaceWithOnboarding() async =>
await _navigationService.replaceWithOnboardingView();
Future<void> replaceWithHome() async =>
await _navigationService.replaceWithHomeView();
// Remote api calls
// In-app update
Future<void> _inAppUpdate() async {
if (await _statusChecker.checkConnection()) {
await _inAppUpdateService.checkForUpdate();
}
}
// Get profile status
Future<void> _getProfileStatus() async {
final bool? profileCompleted = _user?.profileCompleted;
Map<String, dynamic> response = {};
if (_user?.profileCompleted == null) {
if (profileCompleted == null) {
if (await _statusChecker.checkConnection()) {
print('PATH: 1');
response = await _apiService.getProfileStatus(_user);
} else {
print('PATH: 2');
await Future.delayed(kDuration);
await replaceWithFailure();
}
} else if (!(_user?.profileCompleted ?? false)) {
print('PATH: 3');
} else if (!profileCompleted) {
response = {'data': false, 'status': ResponseStatus.success};
} else {
print('PATH: 4');
response = {'data': true, 'status': ResponseStatus.success};
}
if (response['status'] == ResponseStatus.success && !response['data']) {
print('PATH: 5');
await replaceWithOnboarding();
} else if (response['status'] == ResponseStatus.success &&
response['data']) {
print('PATH: 6');
await saveProfileStatus(response['data']);
await _getProfileData();
await replaceWithHome();
} else {
await replaceWithFailure();
}
}
@ -118,26 +94,24 @@ class StartupViewModel extends ReactiveViewModel {
// Get profile data
Future<void> _getProfileData() async {
if (!(_user?.userInfoLoaded ?? false)) {
bool? infoLoaded = _user?.userInfoLoaded ?? false;
bool? profileCompleted = _user?.profileCompleted ?? false;
if (!infoLoaded) {
Map<String, dynamic> response = {};
if (_user?.profileCompleted != null &&
(_user?.profileCompleted ?? false)) {
if (await _statusChecker.checkConnection()) {
response = await _apiService.getProfileData(_user?.userId);
if (profileCompleted) {
response = await _apiService.getProfileData(_user?.userId);
if (response['status'] == ResponseStatus.success) {
User user = response['data'] as User;
if (response['status'] == ResponseStatus.success) {
User user = response['data'] as User;
await _authenticationService.saveUserData(user);
await _authenticationService.saveUserData(user);
String image =
await _imageDownloaderService.downloader(user.profilePicture);
String image =
await _imageDownloaderService.downloader(user.profilePicture);
await _authenticationService.saveProfilePicture(image);
}
} else {
await replaceWithFailure();
await _authenticationService.saveProfilePicture(image);
}
}
}

View File

@ -53,7 +53,7 @@ class SupportView extends StackedView<SupportViewModel> {
Widget _buildAppbar(SupportViewModel viewModel) => SmallAppBar(
title: 'Need Help?',
showBackButton: true,
onTap: viewModel.pop,
onPop: viewModel.pop,
);
Widget _buildContentWrapper(SupportViewModel viewModel) =>

View File

@ -49,7 +49,7 @@ class TelegramSupportView extends StackedView<TelegramSupportViewModel> {
);
Widget _buildAppbar(TelegramSupportViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop,
onPop: viewModel.pop,
showBackButton: true,
title: 'Telegram Support',
);

View File

@ -57,7 +57,7 @@ class TermsAndConditionsView extends StackedView<TermsAndConditionsViewModel> {
);
Widget _buildAppbar(TermsAndConditionsViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop,
onPop: viewModel.pop,
showBackButton: true,
title: 'Terms and Conditions',
);

View File

@ -1,9 +1,9 @@
import 'package:flutter/material.dart';
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
import 'package:yimaru_app/ui/widgets/refresh_button.dart';
import '../../../common/app_colors.dart';
import '../../../widgets/large_app_bar.dart';
import '../../../widgets/refresh_button.dart';
import '../common/app_colors.dart';
import 'large_app_bar.dart';
class AssessmentLoadingScreen extends StatelessWidget {
final bool isEmpty;

View File

@ -55,7 +55,7 @@ class LearnLessonTile extends ViewModelWidget<LearnLessonViewModel> {
expandedCrossAxisAlignment: CrossAxisAlignment.start,
collapsedBackgroundColor: kcPrimaryColor.withOpacity(0.1),
childrenPadding: const EdgeInsets.fromLTRB(15, 0, 15, 15),
// enabled: status != ProgressStatuses.pending,
// enabled: (lesson.access?.isAccessible ?? false),
// backgroundColor: ProgressStatuses.pending == status
// ? kcPrimaryColor.withOpacity(0.1)
// : kcGreen.withOpacity(0.1),

View File

@ -56,9 +56,7 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
{required BuildContext context,
required LearnModuleViewModel viewModel}) =>
ExpansionTile(
enabled: true,
textColor: kcDarkGrey,
showTrailingIcon: true,
initiallyExpanded: true,
subtitle: _buildContent(),
title: _buildTitleWrapper(),
@ -69,13 +67,12 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
shape: Border.all(color: kcTransparent),
expandedAlignment: Alignment.centerLeft,
collapsedBackgroundColor: kcBackgroundColor,
enabled: (module.access?.isAccessible ?? false),
controlAffinity: ListTileControlAffinity.trailing,
expandedCrossAxisAlignment: CrossAxisAlignment.start,
tilePadding: const EdgeInsets.symmetric(horizontal: 15),
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,
showTrailingIcon: (module.access?.isAccessible ?? false) ? true : false,
children:
_buildExpansionTileChildren(context: context, viewModel: viewModel),
);
@ -209,9 +206,9 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
onTap: viewModel.pop,
);
// Widget _buildContainerShaderState() => status == ProgressStatuses.pending
// ? _buildContainerShaderWrapper()
// : Container();
Widget _buildContainerShaderState() => !(module.access?.isAccessible ?? false)
? _buildContainerShaderWrapper()
: Container();
Widget _buildContainerShaderWrapper() => Positioned.fill(
child: _buildContainerShader(),

View File

@ -4,7 +4,6 @@ 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 ViewModelWidget<LearnPracticeViewModel> {
const LearnPracticeTipSection({super.key});

View File

@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:yimaru_app/ui/common/app_colors.dart';
import '../common/ui_helpers.dart';
class NoDataIndicator extends StatelessWidget {
final String title;
final GestureTapCallback? onTap;
const NoDataIndicator({super.key, this.onTap, required this.title});
@override
Widget build(BuildContext context) => _buildColumn();
Widget _buildColumn() => Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: _buildColumnChildren(),
);
List<Widget> _buildColumnChildren() => [
_buildIconWrapper(),
verticalSpaceMedium,
_buildTitle(),
];
Widget _buildTitle() => Text(
title,
style: style16P600,
);
Widget _buildIconWrapper() => GestureDetector(
onTap: onTap,
child: _buildIcon(),
);
Widget _buildIcon() => const Icon(
Icons.replay,
size: 75,
color: kcPrimaryColor,
);
}

View File

@ -5,10 +5,10 @@ import 'package:yimaru_app/ui/widgets/custom_back_button.dart';
class SmallAppBar extends StatelessWidget {
final String? title;
final bool showBackButton;
final GestureTapCallback? onTap;
final GestureTapCallback? onPop;
const SmallAppBar(
{super.key, this.onTap, this.title, required this.showBackButton});
{super.key, this.onPop, this.title, required this.showBackButton});
@override
Widget build(BuildContext context) => _buildAppBar();
@ -28,7 +28,7 @@ class SmallAppBar extends StatelessWidget {
child: _buildBackButton(),
);
Widget _buildBackButton() => CustomBackButton(onTap: onTap);
Widget _buildBackButton() => CustomBackButton(onTap: onPop);
Widget _buildTitleWrapper() => Align(
alignment: Alignment.center,

View File

@ -1,5 +1,5 @@
name: yimaru_app
version: 0.1.8+10
version: 0.1.9+11
publish_to: 'none'
description: A new Flutter project.

View File

@ -8,48 +8,49 @@ 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 _i39;
import 'package:firebase_messaging/firebase_messaging.dart' as _i40;
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 _i34;
import 'package:permission_handler/permission_handler.dart' as _i35;
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 _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 _i22;
import 'package:yimaru_app/models/submodule.dart' as _i29;
import 'package:yimaru_app/models/assessment.dart' as _i14;
import 'package:yimaru_app/models/assessment_question.dart' as _i15;
import 'package:yimaru_app/models/category.dart' as _i22;
import 'package:yimaru_app/models/course.dart' as _i27;
import 'package:yimaru_app/models/course_detail.dart' as _i43;
import 'package:yimaru_app/models/course_lesson.dart' as _i25;
import 'package:yimaru_app/models/course_progress.dart' as _i24;
import 'package:yimaru_app/models/learn_course.dart' as _i17;
import 'package:yimaru_app/models/learn_lesson.dart' as _i20;
import 'package:yimaru_app/models/learn_module.dart' as _i19;
import 'package:yimaru_app/models/learn_practice.dart' as _i18;
import 'package:yimaru_app/models/learn_program.dart' as _i16;
import 'package:yimaru_app/models/learn_question.dart' as _i21;
import 'package:yimaru_app/models/lesson.dart' as _i31;
import 'package:yimaru_app/models/level.dart' as _i28;
import 'package:yimaru_app/models/module.dart' as _i29;
import 'package:yimaru_app/models/practice.dart' as _i26;
import 'package:yimaru_app/models/subcategory.dart' as _i23;
import 'package:yimaru_app/models/submodule.dart' as _i30;
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 _i43;
import 'package:yimaru_app/services/audio_player_service.dart' as _i44;
import 'package:yimaru_app/services/authentication_service.dart' as _i11;
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/course_service.dart' as _i42;
import 'package:yimaru_app/services/dio_service.dart' as _i32;
import 'package:yimaru_app/services/google_auth_service.dart' as _i37;
import 'package:yimaru_app/services/image_downloader_service.dart' as _i38;
import 'package:yimaru_app/services/image_picker_service.dart' as _i36;
import 'package:yimaru_app/services/in_app_update_service.dart' as _i47;
import 'package:yimaru_app/services/notification_service.dart' as _i39;
import 'package:yimaru_app/services/permission_handler_service.dart' as _i34;
import 'package:yimaru_app/services/secure_storage_service.dart' as _i3;
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;
import 'package:yimaru_app/services/smart_auth_service.dart' as _i41;
import 'package:yimaru_app/services/status_checker_service.dart' as _i33;
import 'package:yimaru_app/services/voice_recorder_service.dart' as _i45;
import 'package:yimaru_app/ui/common/enmus.dart' as _i46;
// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
@ -1125,168 +1126,183 @@ class MockApiService extends _i1.Mock implements _i13.ApiService {
) as _i9.Future<Map<String, dynamic>>);
@override
_i9.Future<List<_i14.Question>> getAssessments() => (super.noSuchMethod(
_i9.Future<List<_i14.Assessment>> getAssessments() => (super.noSuchMethod(
Invocation.method(
#getAssessments,
[],
),
returnValue: _i9.Future<List<_i14.Question>>.value(<_i14.Question>[]),
returnValue:
_i9.Future<List<_i14.Assessment>>.value(<_i14.Assessment>[]),
returnValueForMissingStub:
_i9.Future<List<_i14.Question>>.value(<_i14.Question>[]),
) as _i9.Future<List<_i14.Question>>);
_i9.Future<List<_i14.Assessment>>.value(<_i14.Assessment>[]),
) as _i9.Future<List<_i14.Assessment>>);
@override
_i9.Future<List<_i15.LearnProgram>> getLearnPrograms() => (super.noSuchMethod(
_i9.Future<List<_i15.AssessmentQuestion>> getAssessmentQuestions(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getAssessmentQuestions,
[id],
),
returnValue: _i9.Future<List<_i15.AssessmentQuestion>>.value(
<_i15.AssessmentQuestion>[]),
returnValueForMissingStub:
_i9.Future<List<_i15.AssessmentQuestion>>.value(
<_i15.AssessmentQuestion>[]),
) as _i9.Future<List<_i15.AssessmentQuestion>>);
@override
_i9.Future<List<_i16.LearnProgram>> getLearnPrograms() => (super.noSuchMethod(
Invocation.method(
#getLearnPrograms,
[],
),
returnValue:
_i9.Future<List<_i15.LearnProgram>>.value(<_i15.LearnProgram>[]),
_i9.Future<List<_i16.LearnProgram>>.value(<_i16.LearnProgram>[]),
returnValueForMissingStub:
_i9.Future<List<_i15.LearnProgram>>.value(<_i15.LearnProgram>[]),
) as _i9.Future<List<_i15.LearnProgram>>);
_i9.Future<List<_i16.LearnProgram>>.value(<_i16.LearnProgram>[]),
) as _i9.Future<List<_i16.LearnProgram>>);
@override
_i9.Future<List<_i16.LearnCourse>> getLearnCourse(int? id) =>
_i9.Future<List<_i17.LearnCourse>> getLearnCourse(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getLearnCourse,
[id],
),
returnValue:
_i9.Future<List<_i16.LearnCourse>>.value(<_i16.LearnCourse>[]),
_i9.Future<List<_i17.LearnCourse>>.value(<_i17.LearnCourse>[]),
returnValueForMissingStub:
_i9.Future<List<_i16.LearnCourse>>.value(<_i16.LearnCourse>[]),
) as _i9.Future<List<_i16.LearnCourse>>);
_i9.Future<List<_i17.LearnCourse>>.value(<_i17.LearnCourse>[]),
) as _i9.Future<List<_i17.LearnCourse>>);
@override
_i9.Future<List<_i17.LearnPractice>> getLearnCoursePractices(int? id) =>
_i9.Future<List<_i18.LearnPractice>> getLearnCoursePractices(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getLearnCoursePractices,
[id],
),
returnValue:
_i9.Future<List<_i17.LearnPractice>>.value(<_i17.LearnPractice>[]),
_i9.Future<List<_i18.LearnPractice>>.value(<_i18.LearnPractice>[]),
returnValueForMissingStub:
_i9.Future<List<_i17.LearnPractice>>.value(<_i17.LearnPractice>[]),
) as _i9.Future<List<_i17.LearnPractice>>);
_i9.Future<List<_i18.LearnPractice>>.value(<_i18.LearnPractice>[]),
) as _i9.Future<List<_i18.LearnPractice>>);
@override
_i9.Future<List<_i18.LearnModule>> getLearnModules(int? id) =>
_i9.Future<List<_i19.LearnModule>> getLearnModules(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getLearnModules,
[id],
),
returnValue:
_i9.Future<List<_i18.LearnModule>>.value(<_i18.LearnModule>[]),
_i9.Future<List<_i19.LearnModule>>.value(<_i19.LearnModule>[]),
returnValueForMissingStub:
_i9.Future<List<_i18.LearnModule>>.value(<_i18.LearnModule>[]),
) as _i9.Future<List<_i18.LearnModule>>);
_i9.Future<List<_i19.LearnModule>>.value(<_i19.LearnModule>[]),
) as _i9.Future<List<_i19.LearnModule>>);
@override
_i9.Future<List<_i17.LearnPractice>> getLearnModulePractices(int? id) =>
_i9.Future<List<_i18.LearnPractice>> getLearnModulePractices(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getLearnModulePractices,
[id],
),
returnValue:
_i9.Future<List<_i17.LearnPractice>>.value(<_i17.LearnPractice>[]),
_i9.Future<List<_i18.LearnPractice>>.value(<_i18.LearnPractice>[]),
returnValueForMissingStub:
_i9.Future<List<_i17.LearnPractice>>.value(<_i17.LearnPractice>[]),
) as _i9.Future<List<_i17.LearnPractice>>);
_i9.Future<List<_i18.LearnPractice>>.value(<_i18.LearnPractice>[]),
) as _i9.Future<List<_i18.LearnPractice>>);
@override
_i9.Future<List<_i19.LearnLesson>> getLearnLessons(int? id) =>
_i9.Future<List<_i20.LearnLesson>> getLearnLessons(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getLearnLessons,
[id],
),
returnValue:
_i9.Future<List<_i19.LearnLesson>>.value(<_i19.LearnLesson>[]),
_i9.Future<List<_i20.LearnLesson>>.value(<_i20.LearnLesson>[]),
returnValueForMissingStub:
_i9.Future<List<_i19.LearnLesson>>.value(<_i19.LearnLesson>[]),
) as _i9.Future<List<_i19.LearnLesson>>);
_i9.Future<List<_i20.LearnLesson>>.value(<_i20.LearnLesson>[]),
) as _i9.Future<List<_i20.LearnLesson>>);
@override
_i9.Future<List<_i17.LearnPractice>> getLearnLessonPractices(int? id) =>
_i9.Future<List<_i18.LearnPractice>> getLearnLessonPractices(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getLearnLessonPractices,
[id],
),
returnValue:
_i9.Future<List<_i17.LearnPractice>>.value(<_i17.LearnPractice>[]),
_i9.Future<List<_i18.LearnPractice>>.value(<_i18.LearnPractice>[]),
returnValueForMissingStub:
_i9.Future<List<_i17.LearnPractice>>.value(<_i17.LearnPractice>[]),
) as _i9.Future<List<_i17.LearnPractice>>);
_i9.Future<List<_i18.LearnPractice>>.value(<_i18.LearnPractice>[]),
) as _i9.Future<List<_i18.LearnPractice>>);
@override
_i9.Future<List<_i20.LearnQuestion>> getLearnQuestions(int? id) =>
_i9.Future<List<_i21.LearnQuestion>> getLearnQuestions(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getLearnQuestions,
[id],
),
returnValue:
_i9.Future<List<_i20.LearnQuestion>>.value(<_i20.LearnQuestion>[]),
_i9.Future<List<_i21.LearnQuestion>>.value(<_i21.LearnQuestion>[]),
returnValueForMissingStub:
_i9.Future<List<_i20.LearnQuestion>>.value(<_i20.LearnQuestion>[]),
) as _i9.Future<List<_i20.LearnQuestion>>);
_i9.Future<List<_i21.LearnQuestion>>.value(<_i21.LearnQuestion>[]),
) as _i9.Future<List<_i21.LearnQuestion>>);
@override
_i9.Future<List<_i21.Category>> getCategories() => (super.noSuchMethod(
_i9.Future<List<_i22.Category>> getCategories() => (super.noSuchMethod(
Invocation.method(
#getCategories,
[],
),
returnValue: _i9.Future<List<_i21.Category>>.value(<_i21.Category>[]),
returnValue: _i9.Future<List<_i22.Category>>.value(<_i22.Category>[]),
returnValueForMissingStub:
_i9.Future<List<_i21.Category>>.value(<_i21.Category>[]),
) as _i9.Future<List<_i21.Category>>);
_i9.Future<List<_i22.Category>>.value(<_i22.Category>[]),
) as _i9.Future<List<_i22.Category>>);
@override
_i9.Future<List<_i22.Subcategory>> getSubcategories(int? id) =>
_i9.Future<List<_i23.Subcategory>> getSubcategories(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getSubcategories,
[id],
),
returnValue:
_i9.Future<List<_i22.Subcategory>>.value(<_i22.Subcategory>[]),
_i9.Future<List<_i23.Subcategory>>.value(<_i23.Subcategory>[]),
returnValueForMissingStub:
_i9.Future<List<_i22.Subcategory>>.value(<_i22.Subcategory>[]),
) as _i9.Future<List<_i22.Subcategory>>);
_i9.Future<List<_i23.Subcategory>>.value(<_i23.Subcategory>[]),
) as _i9.Future<List<_i23.Subcategory>>);
@override
_i9.Future<List<_i23.CourseProgress>> getCourseProgress(int? id) =>
_i9.Future<List<_i24.CourseProgress>> getCourseProgress(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getCourseProgress,
[id],
),
returnValue: _i9.Future<List<_i23.CourseProgress>>.value(
<_i23.CourseProgress>[]),
returnValueForMissingStub: _i9.Future<List<_i23.CourseProgress>>.value(
<_i23.CourseProgress>[]),
) as _i9.Future<List<_i23.CourseProgress>>);
returnValue: _i9.Future<List<_i24.CourseProgress>>.value(
<_i24.CourseProgress>[]),
returnValueForMissingStub: _i9.Future<List<_i24.CourseProgress>>.value(
<_i24.CourseProgress>[]),
) as _i9.Future<List<_i24.CourseProgress>>);
@override
_i9.Future<List<_i24.CourseLesson>> getCourseLessons(int? id) =>
_i9.Future<List<_i25.CourseLesson>> getCourseLessons(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getCourseLessons,
[id],
),
returnValue:
_i9.Future<List<_i24.CourseLesson>>.value(<_i24.CourseLesson>[]),
_i9.Future<List<_i25.CourseLesson>>.value(<_i25.CourseLesson>[]),
returnValueForMissingStub:
_i9.Future<List<_i24.CourseLesson>>.value(<_i24.CourseLesson>[]),
) as _i9.Future<List<_i24.CourseLesson>>);
_i9.Future<List<_i25.CourseLesson>>.value(<_i25.CourseLesson>[]),
) as _i9.Future<List<_i25.CourseLesson>>);
@override
_i9.Future<Map<String, dynamic>> completeLesson(int? id) =>
@ -1302,130 +1318,136 @@ class MockApiService extends _i1.Mock implements _i13.ApiService {
) as _i9.Future<Map<String, dynamic>>);
@override
_i9.Future<List<_i25.Practice>> getCoursePractices(int? id) =>
_i9.Future<List<_i26.Practice>> getCoursePractices(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getCoursePractices,
[id],
),
returnValue: _i9.Future<List<_i25.Practice>>.value(<_i25.Practice>[]),
returnValue: _i9.Future<List<_i26.Practice>>.value(<_i26.Practice>[]),
returnValueForMissingStub:
_i9.Future<List<_i25.Practice>>.value(<_i25.Practice>[]),
) as _i9.Future<List<_i25.Practice>>);
_i9.Future<List<_i26.Practice>>.value(<_i26.Practice>[]),
) as _i9.Future<List<_i26.Practice>>);
@override
_i9.Future<List<_i14.Question>> getCoursePracticeQuestions(int? id) =>
_i9.Future<List<_i15.AssessmentQuestion>> getCoursePracticeQuestions(
int? id) =>
(super.noSuchMethod(
Invocation.method(
#getCoursePracticeQuestions,
[id],
),
returnValue: _i9.Future<List<_i14.Question>>.value(<_i14.Question>[]),
returnValue: _i9.Future<List<_i15.AssessmentQuestion>>.value(
<_i15.AssessmentQuestion>[]),
returnValueForMissingStub:
_i9.Future<List<_i14.Question>>.value(<_i14.Question>[]),
) as _i9.Future<List<_i14.Question>>);
_i9.Future<List<_i15.AssessmentQuestion>>.value(
<_i15.AssessmentQuestion>[]),
) as _i9.Future<List<_i15.AssessmentQuestion>>);
@override
_i9.Future<_i14.Question?> getCoursePracticeQuestion(int? id) =>
_i9.Future<_i15.AssessmentQuestion?> getCoursePracticeQuestion(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getCoursePracticeQuestion,
[id],
),
returnValue: _i9.Future<_i14.Question?>.value(),
returnValueForMissingStub: _i9.Future<_i14.Question?>.value(),
) as _i9.Future<_i14.Question?>);
returnValue: _i9.Future<_i15.AssessmentQuestion?>.value(),
returnValueForMissingStub: _i9.Future<_i15.AssessmentQuestion?>.value(),
) as _i9.Future<_i15.AssessmentQuestion?>);
@override
_i9.Future<List<_i22.Subcategory>> getLearnSubcategories() =>
_i9.Future<List<_i23.Subcategory>> getLearnSubcategories() =>
(super.noSuchMethod(
Invocation.method(
#getLearnSubcategories,
[],
),
returnValue:
_i9.Future<List<_i22.Subcategory>>.value(<_i22.Subcategory>[]),
_i9.Future<List<_i23.Subcategory>>.value(<_i23.Subcategory>[]),
returnValueForMissingStub:
_i9.Future<List<_i22.Subcategory>>.value(<_i22.Subcategory>[]),
) as _i9.Future<List<_i22.Subcategory>>);
_i9.Future<List<_i23.Subcategory>>.value(<_i23.Subcategory>[]),
) as _i9.Future<List<_i23.Subcategory>>);
@override
_i9.Future<List<_i26.Course>> getCourses(int? id) => (super.noSuchMethod(
_i9.Future<List<_i27.Course>> getCourses(int? id) => (super.noSuchMethod(
Invocation.method(
#getCourses,
[id],
),
returnValue: _i9.Future<List<_i26.Course>>.value(<_i26.Course>[]),
returnValue: _i9.Future<List<_i27.Course>>.value(<_i27.Course>[]),
returnValueForMissingStub:
_i9.Future<List<_i26.Course>>.value(<_i26.Course>[]),
) as _i9.Future<List<_i26.Course>>);
_i9.Future<List<_i27.Course>>.value(<_i27.Course>[]),
) as _i9.Future<List<_i27.Course>>);
@override
_i9.Future<List<_i27.Level>> getLevels(int? id) => (super.noSuchMethod(
_i9.Future<List<_i28.Level>> getLevels(int? id) => (super.noSuchMethod(
Invocation.method(
#getLevels,
[id],
),
returnValue: _i9.Future<List<_i27.Level>>.value(<_i27.Level>[]),
returnValue: _i9.Future<List<_i28.Level>>.value(<_i28.Level>[]),
returnValueForMissingStub:
_i9.Future<List<_i27.Level>>.value(<_i27.Level>[]),
) as _i9.Future<List<_i27.Level>>);
_i9.Future<List<_i28.Level>>.value(<_i28.Level>[]),
) as _i9.Future<List<_i28.Level>>);
@override
_i9.Future<List<_i28.Module>> getModules(int? id) => (super.noSuchMethod(
_i9.Future<List<_i29.Module>> getModules(int? id) => (super.noSuchMethod(
Invocation.method(
#getModules,
[id],
),
returnValue: _i9.Future<List<_i28.Module>>.value(<_i28.Module>[]),
returnValue: _i9.Future<List<_i29.Module>>.value(<_i29.Module>[]),
returnValueForMissingStub:
_i9.Future<List<_i28.Module>>.value(<_i28.Module>[]),
) as _i9.Future<List<_i28.Module>>);
_i9.Future<List<_i29.Module>>.value(<_i29.Module>[]),
) as _i9.Future<List<_i29.Module>>);
@override
_i9.Future<List<_i29.Submodule>> getSubmodules(int? id) =>
_i9.Future<List<_i30.Submodule>> getSubmodules(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getSubmodules,
[id],
),
returnValue: _i9.Future<List<_i29.Submodule>>.value(<_i29.Submodule>[]),
returnValue: _i9.Future<List<_i30.Submodule>>.value(<_i30.Submodule>[]),
returnValueForMissingStub:
_i9.Future<List<_i29.Submodule>>.value(<_i29.Submodule>[]),
) as _i9.Future<List<_i29.Submodule>>);
_i9.Future<List<_i30.Submodule>>.value(<_i30.Submodule>[]),
) as _i9.Future<List<_i30.Submodule>>);
@override
_i9.Future<List<_i30.Lesson>> getLessons(int? id) => (super.noSuchMethod(
_i9.Future<List<_i31.Lesson>> getLessons(int? id) => (super.noSuchMethod(
Invocation.method(
#getLessons,
[id],
),
returnValue: _i9.Future<List<_i30.Lesson>>.value(<_i30.Lesson>[]),
returnValue: _i9.Future<List<_i31.Lesson>>.value(<_i31.Lesson>[]),
returnValueForMissingStub:
_i9.Future<List<_i30.Lesson>>.value(<_i30.Lesson>[]),
) as _i9.Future<List<_i30.Lesson>>);
_i9.Future<List<_i31.Lesson>>.value(<_i31.Lesson>[]),
) as _i9.Future<List<_i31.Lesson>>);
@override
_i9.Future<List<_i25.Practice>> getPractices(int? id) => (super.noSuchMethod(
_i9.Future<List<_i26.Practice>> getPractices(int? id) => (super.noSuchMethod(
Invocation.method(
#getPractices,
[id],
),
returnValue: _i9.Future<List<_i25.Practice>>.value(<_i25.Practice>[]),
returnValue: _i9.Future<List<_i26.Practice>>.value(<_i26.Practice>[]),
returnValueForMissingStub:
_i9.Future<List<_i25.Practice>>.value(<_i25.Practice>[]),
) as _i9.Future<List<_i25.Practice>>);
_i9.Future<List<_i26.Practice>>.value(<_i26.Practice>[]),
) as _i9.Future<List<_i26.Practice>>);
@override
_i9.Future<List<_i14.Question>> getQuestions(int? id) => (super.noSuchMethod(
_i9.Future<List<_i15.AssessmentQuestion>> getQuestions(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getQuestions,
[id],
),
returnValue: _i9.Future<List<_i14.Question>>.value(<_i14.Question>[]),
returnValue: _i9.Future<List<_i15.AssessmentQuestion>>.value(
<_i15.AssessmentQuestion>[]),
returnValueForMissingStub:
_i9.Future<List<_i14.Question>>.value(<_i14.Question>[]),
) as _i9.Future<List<_i14.Question>>);
_i9.Future<List<_i15.AssessmentQuestion>>.value(
<_i15.AssessmentQuestion>[]),
) as _i9.Future<List<_i15.AssessmentQuestion>>);
}
/// A class which mocks [SecureStorageService].
@ -1528,7 +1550,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 _i31.DioService {
class MockDioService extends _i1.Mock implements _i32.DioService {
@override
_i2.Dio get dio => (super.noSuchMethod(
Invocation.getter(#dio),
@ -1547,7 +1569,7 @@ class MockDioService extends _i1.Mock implements _i31.DioService {
///
/// See the documentation for Mockito's code generation for more information.
class MockStatusCheckerService extends _i1.Mock
implements _i32.StatusCheckerService {
implements _i33.StatusCheckerService {
@override
_i3.SecureStorageService get storage => (super.noSuchMethod(
Invocation.getter(#storage),
@ -1583,40 +1605,40 @@ class MockStatusCheckerService extends _i1.Mock
///
/// See the documentation for Mockito's code generation for more information.
class MockPermissionHandlerService extends _i1.Mock
implements _i33.PermissionHandlerService {
implements _i34.PermissionHandlerService {
@override
_i9.Future<_i34.PermissionStatus> requestPermission(
_i34.Permission? requestedPermission) =>
_i9.Future<_i35.PermissionStatus> requestPermission(
_i35.Permission? requestedPermission) =>
(super.noSuchMethod(
Invocation.method(
#requestPermission,
[requestedPermission],
),
returnValue: _i9.Future<_i34.PermissionStatus>.value(
_i34.PermissionStatus.denied),
returnValueForMissingStub: _i9.Future<_i34.PermissionStatus>.value(
_i34.PermissionStatus.denied),
) as _i9.Future<_i34.PermissionStatus>);
returnValue: _i9.Future<_i35.PermissionStatus>.value(
_i35.PermissionStatus.denied),
returnValueForMissingStub: _i9.Future<_i35.PermissionStatus>.value(
_i35.PermissionStatus.denied),
) as _i9.Future<_i35.PermissionStatus>);
@override
_i9.Future<_i34.PermissionStatus> request(_i34.Permission? permission) =>
_i9.Future<_i35.PermissionStatus> request(_i35.Permission? permission) =>
(super.noSuchMethod(
Invocation.method(
#request,
[permission],
),
returnValue: _i9.Future<_i34.PermissionStatus>.value(
_i34.PermissionStatus.denied),
returnValueForMissingStub: _i9.Future<_i34.PermissionStatus>.value(
_i34.PermissionStatus.denied),
) as _i9.Future<_i34.PermissionStatus>);
returnValue: _i9.Future<_i35.PermissionStatus>.value(
_i35.PermissionStatus.denied),
returnValueForMissingStub: _i9.Future<_i35.PermissionStatus>.value(
_i35.PermissionStatus.denied),
) as _i9.Future<_i35.PermissionStatus>);
}
/// A class which mocks [ImagePickerService].
///
/// See the documentation for Mockito's code generation for more information.
class MockImagePickerService extends _i1.Mock
implements _i35.ImagePickerService {
implements _i36.ImagePickerService {
@override
_i9.Future<String?> gallery() => (super.noSuchMethod(
Invocation.method(
@ -1641,7 +1663,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 _i36.GoogleAuthService {
class MockGoogleAuthService extends _i1.Mock implements _i37.GoogleAuthService {
@override
int get listenersCount => (super.noSuchMethod(
Invocation.getter(#listenersCount),
@ -1711,7 +1733,7 @@ class MockGoogleAuthService extends _i1.Mock implements _i36.GoogleAuthService {
///
/// See the documentation for Mockito's code generation for more information.
class MockImageDownloaderService extends _i1.Mock
implements _i37.ImageDownloaderService {
implements _i38.ImageDownloaderService {
@override
_i9.Future<String> downloader(String? networkImage) => (super.noSuchMethod(
Invocation.method(
@ -1740,7 +1762,7 @@ class MockImageDownloaderService extends _i1.Mock
///
/// See the documentation for Mockito's code generation for more information.
class MockNotificationService extends _i1.Mock
implements _i38.NotificationService {
implements _i39.NotificationService {
@override
_i9.Future<void> initialize() => (super.noSuchMethod(
Invocation.method(
@ -1762,7 +1784,7 @@ class MockNotificationService extends _i1.Mock
) as _i9.Future<void>);
@override
_i9.Future<void> showNotification(_i39.RemoteMessage? message) =>
_i9.Future<void> showNotification(_i40.RemoteMessage? message) =>
(super.noSuchMethod(
Invocation.method(
#showNotification,
@ -1796,7 +1818,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 _i40.SmartAuthService {
class MockSmartAuthService extends _i1.Mock implements _i41.SmartAuthService {
@override
bool get listenForMultipleSms => (super.noSuchMethod(
Invocation.getter(#listenForMultipleSms),
@ -1828,26 +1850,26 @@ class MockSmartAuthService extends _i1.Mock implements _i40.SmartAuthService {
/// A class which mocks [CourseService].
///
/// See the documentation for Mockito's code generation for more information.
class MockCourseService extends _i1.Mock implements _i41.CourseService {
class MockCourseService extends _i1.Mock implements _i42.CourseService {
@override
_i9.Future<List<_i42.CourseDetail>> getCoursesDetail(int? id) =>
_i9.Future<List<_i43.CourseDetail>> getCoursesDetail(int? id) =>
(super.noSuchMethod(
Invocation.method(
#getCoursesDetail,
[id],
),
returnValue:
_i9.Future<List<_i42.CourseDetail>>.value(<_i42.CourseDetail>[]),
_i9.Future<List<_i43.CourseDetail>>.value(<_i43.CourseDetail>[]),
returnValueForMissingStub:
_i9.Future<List<_i42.CourseDetail>>.value(<_i42.CourseDetail>[]),
) as _i9.Future<List<_i42.CourseDetail>>);
_i9.Future<List<_i43.CourseDetail>>.value(<_i43.CourseDetail>[]),
) as _i9.Future<List<_i43.CourseDetail>>);
}
/// A class which mocks [AudioPlayerService].
///
/// See the documentation for Mockito's code generation for more information.
class MockAudioPlayerService extends _i1.Mock
implements _i43.AudioPlayerService {
implements _i44.AudioPlayerService {
@override
_i4.AudioPlayer get player => (super.noSuchMethod(
Invocation.getter(#player),
@ -1971,13 +1993,13 @@ class MockAudioPlayerService extends _i1.Mock
///
/// See the documentation for Mockito's code generation for more information.
class MockVoiceRecorderService extends _i1.Mock
implements _i44.VoiceRecorderService {
implements _i45.VoiceRecorderService {
@override
_i45.VoiceRecordingState get recordingState => (super.noSuchMethod(
_i46.VoiceRecordingState get recordingState => (super.noSuchMethod(
Invocation.getter(#recordingState),
returnValue: _i45.VoiceRecordingState.pending,
returnValueForMissingStub: _i45.VoiceRecordingState.pending,
) as _i45.VoiceRecordingState);
returnValue: _i46.VoiceRecordingState.pending,
returnValueForMissingStub: _i46.VoiceRecordingState.pending,
) as _i46.VoiceRecordingState);
@override
_i5.WaveformRecorderController get waveController => (super.noSuchMethod(
@ -2078,7 +2100,7 @@ class MockVoiceRecorderService extends _i1.Mock
///
/// See the documentation for Mockito's code generation for more information.
class MockInAppUpdateService extends _i1.Mock
implements _i46.InAppUpdateService {
implements _i47.InAppUpdateService {
@override
_i9.Future<int> getBatteryLevel() => (super.noSuchMethod(
Invocation.method(

View File

@ -0,0 +1,11 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:yimaru_app/app/app.locator.dart';
import '../helpers/test_helpers.dart';
void main() {
group('AssessmentQuestionViewModel Tests -', () {
setUp(() => registerServices());
tearDown(() => locator.reset());
});
}