Compare commits

...

3 Commits

Author SHA1 Message Date
6b4f87476e Merge branch 'release/0.1.9'
-fix(onboarding): Change onboarding assessment implementation
2026-04-30 10:58:36 +03:00
6a5e531a68 fix(onboarding): Change onboarding assessment implementation 2026-04-30 10:57:45 +03:00
85a09151e3 Merge tag '0.1.8' into develop
-fix(onboarding): Add full name validation to fix looping issue
2026-04-27 17:49:32 +03:00
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/dio_service.dart';
import 'package:yimaru_app/services/status_checker_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/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/ui/views/learn_lesson/learn_lesson_view.dart';
import 'package:yimaru_app/services/permission_handler_service.dart'; import 'package:yimaru_app/services/permission_handler_service.dart';
import 'package:yimaru_app/services/image_picker_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/services/in_app_update_service.dart';
import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart'; import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart';
import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart'; import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart';
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart';
// @stacked-import // @stacked-import
@StackedApp( @StackedApp(
@ -74,7 +74,6 @@ import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart';
MaterialRoute(page: LoginView), MaterialRoute(page: LoginView),
MaterialRoute(page: LearnModuleView), MaterialRoute(page: LearnModuleView),
MaterialRoute(page: WelcomeView), MaterialRoute(page: WelcomeView),
MaterialRoute(page: AssessmentView),
MaterialRoute(page: LearnLessonView), MaterialRoute(page: LearnLessonView),
MaterialRoute(page: ForgetPasswordView), MaterialRoute(page: ForgetPasswordView),
MaterialRoute(page: LearnLessonDetailView), MaterialRoute(page: LearnLessonDetailView),
@ -91,6 +90,7 @@ import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart';
MaterialRoute(page: CoursePracticeQuestionView), MaterialRoute(page: CoursePracticeQuestionView),
MaterialRoute(page: LearnProgramView), MaterialRoute(page: LearnProgramView),
MaterialRoute(page: LearnCourseView), MaterialRoute(page: LearnCourseView),
MaterialRoute(page: AssessmentView),
// @stacked-route // @stacked-route
], ],
dependencies: [ 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/common/enmus.dart' as _i41;
import 'package:yimaru_app/ui/views/account_privacy/account_privacy_view.dart' import 'package:yimaru_app/ui/views/account_privacy/account_privacy_view.dart'
as _i9; 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' import 'package:yimaru_app/ui/views/call_support/call_support_view.dart'
as _i12; 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' 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; 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; 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' 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' 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/downloads/downloads_view.dart' as _i7;
import 'package:yimaru_app/ui/views/duolingo/duolingo_view.dart' as _i31; import 'package:yimaru_app/ui/views/duolingo/duolingo_view.dart' as _i30;
import 'package:yimaru_app/ui/views/failure/failure_view.dart' as _i28; import 'package:yimaru_app/ui/views/failure/failure_view.dart' as _i27;
import 'package:yimaru_app/ui/views/forget_password/forget_password_view.dart' 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/home/home_view.dart' as _i2;
import 'package:yimaru_app/ui/views/language/language_view.dart' as _i13; import 'package:yimaru_app/ui/views/language/language_view.dart' as _i13;
import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart' 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' 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' 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' import 'package:yimaru_app/ui/views/learn_module/learn_module_view.dart'
as _i18; as _i18;
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_view.dart' 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' 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/login/login_view.dart' as _i17;
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.dart' as _i3; import 'package:yimaru_app/ui/views/onboarding/onboarding_view.dart' as _i3;
import 'package:yimaru_app/ui/views/privacy_policy/privacy_policy_view.dart' 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 welcomeView = '/welcome-view';
static const assessmentView = '/assessment-view';
static const learnLessonView = '/learn-lesson-view'; static const learnLessonView = '/learn-lesson-view';
static const forgetPasswordView = '/forget-password-view'; static const forgetPasswordView = '/forget-password-view';
@ -145,6 +143,8 @@ class Routes {
static const learnCourseView = '/learn-course-view'; static const learnCourseView = '/learn-course-view';
static const assessmentView = '/assessment-view';
static const all = <String>{ static const all = <String>{
homeView, homeView,
onboardingView, onboardingView,
@ -164,7 +164,6 @@ class Routes {
loginView, loginView,
learnModuleView, learnModuleView,
welcomeView, welcomeView,
assessmentView,
learnLessonView, learnLessonView,
forgetPasswordView, forgetPasswordView,
learnLessonDetailView, learnLessonDetailView,
@ -181,6 +180,7 @@ class Routes {
coursePracticeQuestionView, coursePracticeQuestionView,
learnProgramView, learnProgramView,
learnCourseView, learnCourseView,
assessmentView,
}; };
} }
@ -258,73 +258,73 @@ class StackedRouter extends _i1.RouterBase {
Routes.welcomeView, Routes.welcomeView,
page: _i19.WelcomeView, page: _i19.WelcomeView,
), ),
_i1.RouteDef(
Routes.assessmentView,
page: _i20.AssessmentView,
),
_i1.RouteDef( _i1.RouteDef(
Routes.learnLessonView, Routes.learnLessonView,
page: _i21.LearnLessonView, page: _i20.LearnLessonView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.forgetPasswordView, Routes.forgetPasswordView,
page: _i22.ForgetPasswordView, page: _i21.ForgetPasswordView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.learnLessonDetailView, Routes.learnLessonDetailView,
page: _i23.LearnLessonDetailView, page: _i22.LearnLessonDetailView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.learnPracticeView, Routes.learnPracticeView,
page: _i24.LearnPracticeView, page: _i23.LearnPracticeView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.coursePracticeView, Routes.coursePracticeView,
page: _i25.CoursePracticeView, page: _i24.CoursePracticeView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.coursePaymentView, Routes.coursePaymentView,
page: _i26.CoursePaymentView, page: _i25.CoursePaymentView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.courseCategoryView, Routes.courseCategoryView,
page: _i27.CourseCategoryView, page: _i26.CourseCategoryView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.failureView, Routes.failureView,
page: _i28.FailureView, page: _i27.FailureView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.courseLessonView, Routes.courseLessonView,
page: _i29.CourseLessonView, page: _i28.CourseLessonView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.courseLessonDetailView, Routes.courseLessonDetailView,
page: _i30.CourseLessonDetailView, page: _i29.CourseLessonDetailView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.duolingoView, Routes.duolingoView,
page: _i31.DuolingoView, page: _i30.DuolingoView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.courseSubcategoryView, Routes.courseSubcategoryView,
page: _i32.CourseSubcategoryView, page: _i31.CourseSubcategoryView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.courseView, Routes.courseView,
page: _i33.CourseView, page: _i32.CourseView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.coursePracticeQuestionView, Routes.coursePracticeQuestionView,
page: _i34.CoursePracticeQuestionView, page: _i33.CoursePracticeQuestionView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.learnProgramView, Routes.learnProgramView,
page: _i35.LearnProgramView, page: _i34.LearnProgramView,
), ),
_i1.RouteDef( _i1.RouteDef(
Routes.learnCourseView, 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, settings: data,
); );
}, },
_i20.AssessmentView: (data) { _i20.LearnLessonView: (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) {
final args = data.getArgs<LearnLessonViewArguments>(nullOk: false); final args = data.getArgs<LearnLessonViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => builder: (context) =>
_i21.LearnLessonView(key: args.key, module: args.module), _i20.LearnLessonView(key: args.key, module: args.module),
settings: data, settings: data,
); );
}, },
_i22.ForgetPasswordView: (data) { _i21.ForgetPasswordView: (data) {
final args = data.getArgs<ForgetPasswordViewArguments>( final args = data.getArgs<ForgetPasswordViewArguments>(
orElse: () => const ForgetPasswordViewArguments(), orElse: () => const ForgetPasswordViewArguments(),
); );
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => _i22.ForgetPasswordView(key: args.key), builder: (context) => _i21.ForgetPasswordView(key: args.key),
settings: data, settings: data,
); );
}, },
_i23.LearnLessonDetailView: (data) { _i22.LearnLessonDetailView: (data) {
final args = data.getArgs<LearnLessonDetailViewArguments>(nullOk: false); final args = data.getArgs<LearnLessonDetailViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => builder: (context) =>
_i23.LearnLessonDetailView(key: args.key, lesson: args.lesson), _i22.LearnLessonDetailView(key: args.key, lesson: args.lesson),
settings: data, settings: data,
); );
}, },
_i24.LearnPracticeView: (data) { _i23.LearnPracticeView: (data) {
final args = data.getArgs<LearnPracticeViewArguments>(nullOk: false); final args = data.getArgs<LearnPracticeViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => _i24.LearnPracticeView( builder: (context) => _i23.LearnPracticeView(
key: args.key, id: args.id, practice: args.practice), key: args.key, id: args.id, practice: args.practice),
settings: data, settings: data,
); );
}, },
_i25.CoursePracticeView: (data) { _i24.CoursePracticeView: (data) {
final args = data.getArgs<CoursePracticeViewArguments>(nullOk: false); final args = data.getArgs<CoursePracticeViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => builder: (context) =>
_i25.CoursePracticeView(key: args.key, id: args.id), _i24.CoursePracticeView(key: args.key, id: args.id),
settings: data, settings: data,
); );
}, },
_i26.CoursePaymentView: (data) { _i25.CoursePaymentView: (data) {
final args = data.getArgs<CoursePaymentViewArguments>(nullOk: false); final args = data.getArgs<CoursePaymentViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => builder: (context) =>
_i26.CoursePaymentView(key: args.key, course: args.course), _i25.CoursePaymentView(key: args.key, course: args.course),
settings: data, settings: data,
); );
}, },
_i27.CourseCategoryView: (data) { _i26.CourseCategoryView: (data) {
final args = data.getArgs<CourseCategoryViewArguments>( final args = data.getArgs<CourseCategoryViewArguments>(
orElse: () => const CourseCategoryViewArguments(), orElse: () => const CourseCategoryViewArguments(),
); );
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => _i27.CourseCategoryView(key: args.key), builder: (context) => _i26.CourseCategoryView(key: args.key),
settings: data, settings: data,
); );
}, },
_i28.FailureView: (data) { _i27.FailureView: (data) {
final args = data.getArgs<FailureViewArguments>(nullOk: false); final args = data.getArgs<FailureViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => builder: (context) => _i27.FailureView(
_i28.FailureView(key: args.key, label: args.label), key: args.key, onTap: args.onTap, label: args.label),
settings: data, settings: data,
); );
}, },
_i29.CourseLessonView: (data) { _i28.CourseLessonView: (data) {
final args = data.getArgs<CourseLessonViewArguments>(nullOk: false); final args = data.getArgs<CourseLessonViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => builder: (context) =>
_i29.CourseLessonView(key: args.key, course: args.course), _i28.CourseLessonView(key: args.key, course: args.course),
settings: data, settings: data,
); );
}, },
_i30.CourseLessonDetailView: (data) { _i29.CourseLessonDetailView: (data) {
final args = data.getArgs<CourseLessonDetailViewArguments>(nullOk: false); final args = data.getArgs<CourseLessonDetailViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => builder: (context) =>
_i30.CourseLessonDetailView(key: args.key, lesson: args.lesson), _i29.CourseLessonDetailView(key: args.key, lesson: args.lesson),
settings: data, settings: data,
); );
}, },
_i31.DuolingoView: (data) { _i30.DuolingoView: (data) {
final args = data.getArgs<DuolingoViewArguments>( final args = data.getArgs<DuolingoViewArguments>(
orElse: () => const DuolingoViewArguments(), orElse: () => const DuolingoViewArguments(),
); );
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => _i31.DuolingoView(key: args.key), builder: (context) => _i30.DuolingoView(key: args.key),
settings: data, settings: data,
); );
}, },
_i32.CourseSubcategoryView: (data) { _i31.CourseSubcategoryView: (data) {
final args = data.getArgs<CourseSubcategoryViewArguments>(nullOk: false); final args = data.getArgs<CourseSubcategoryViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => builder: (context) =>
_i32.CourseSubcategoryView(key: args.key, category: args.category), _i31.CourseSubcategoryView(key: args.key, category: args.category),
settings: data, settings: data,
); );
}, },
_i33.CourseView: (data) { _i32.CourseView: (data) {
final args = data.getArgs<CourseViewArguments>(nullOk: false); final args = data.getArgs<CourseViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => builder: (context) =>
_i33.CourseView(key: args.key, subcategory: args.subcategory), _i32.CourseView(key: args.key, subcategory: args.subcategory),
settings: data, settings: data,
); );
}, },
_i34.CoursePracticeQuestionView: (data) { _i33.CoursePracticeQuestionView: (data) {
final args = final args =
data.getArgs<CoursePracticeQuestionViewArguments>(nullOk: false); data.getArgs<CoursePracticeQuestionViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => builder: (context) =>
_i34.CoursePracticeQuestionView(key: args.key, id: args.id), _i33.CoursePracticeQuestionView(key: args.key, id: args.id),
settings: data, settings: data,
); );
}, },
_i35.LearnProgramView: (data) { _i34.LearnProgramView: (data) {
final args = data.getArgs<LearnProgramViewArguments>( final args = data.getArgs<LearnProgramViewArguments>(
orElse: () => const LearnProgramViewArguments(), orElse: () => const LearnProgramViewArguments(),
); );
return _i37.MaterialPageRoute<dynamic>( return _i37.MaterialPageRoute<dynamic>(
builder: (context) => _i35.LearnProgramView(key: args.key), builder: (context) => _i34.LearnProgramView(key: args.key),
settings: data, settings: data,
); );
}, },
_i36.LearnCourseView: (data) { _i35.LearnCourseView: (data) {
final args = data.getArgs<LearnCourseViewArguments>(nullOk: false); final args = data.getArgs<LearnCourseViewArguments>(nullOk: false);
return _i37.MaterialPageRoute<dynamic>( 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, 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 { class LearnLessonViewArguments {
const LearnLessonViewArguments({ const LearnLessonViewArguments({
this.key, this.key,
@ -1257,27 +1230,30 @@ class CourseCategoryViewArguments {
class FailureViewArguments { class FailureViewArguments {
const FailureViewArguments({ const FailureViewArguments({
this.key, this.key,
required this.onTap,
required this.label, required this.label,
}); });
final _i37.Key? key; final _i37.Key? key;
final void Function() onTap;
final String label; final String label;
@override @override
String toString() { String toString() {
return '{"key": "$key", "label": "$label"}'; return '{"key": "$key", "onTap": "$onTap", "label": "$label"}';
} }
@override @override
bool operator ==(covariant FailureViewArguments other) { bool operator ==(covariant FailureViewArguments other) {
if (identical(this, other)) return true; if (identical(this, other)) return true;
return other.key == key && other.label == label; return other.key == key && other.onTap == onTap && other.label == label;
} }
@override @override
int get hashCode { 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 { extension NavigatorStateExtension on _i46.NavigationService {
Future<dynamic> navigateToHomeView({ Future<dynamic> navigateToHomeView({
_i37.Key? key, _i37.Key? key,
@ -1778,23 +1781,6 @@ extension NavigatorStateExtension on _i46.NavigationService {
transition: transition); 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({ Future<dynamic> navigateToLearnLessonView({
_i37.Key? key, _i37.Key? key,
required _i39.LearnModule module, required _i39.LearnModule module,
@ -1916,6 +1902,7 @@ extension NavigatorStateExtension on _i46.NavigationService {
Future<dynamic> navigateToFailureView({ Future<dynamic> navigateToFailureView({
_i37.Key? key, _i37.Key? key,
required void Function() onTap,
required String label, required String label,
int? routerId, int? routerId,
bool preventDuplicates = true, bool preventDuplicates = true,
@ -1924,7 +1911,7 @@ extension NavigatorStateExtension on _i46.NavigationService {
transition, transition,
}) async { }) async {
return navigateTo<dynamic>(Routes.failureView, return navigateTo<dynamic>(Routes.failureView,
arguments: FailureViewArguments(key: key, label: label), arguments: FailureViewArguments(key: key, onTap: onTap, label: label),
id: routerId, id: routerId,
preventDuplicates: preventDuplicates, preventDuplicates: preventDuplicates,
parameters: parameters, parameters: parameters,
@ -2065,6 +2052,23 @@ extension NavigatorStateExtension on _i46.NavigationService {
transition: transition); 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({ Future<dynamic> replaceWithHomeView({
_i37.Key? key, _i37.Key? key,
int? routerId, int? routerId,
@ -2355,23 +2359,6 @@ extension NavigatorStateExtension on _i46.NavigationService {
transition: transition); 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({ Future<dynamic> replaceWithLearnLessonView({
_i37.Key? key, _i37.Key? key,
required _i39.LearnModule module, required _i39.LearnModule module,
@ -2493,6 +2480,7 @@ extension NavigatorStateExtension on _i46.NavigationService {
Future<dynamic> replaceWithFailureView({ Future<dynamic> replaceWithFailureView({
_i37.Key? key, _i37.Key? key,
required void Function() onTap,
required String label, required String label,
int? routerId, int? routerId,
bool preventDuplicates = true, bool preventDuplicates = true,
@ -2501,7 +2489,7 @@ extension NavigatorStateExtension on _i46.NavigationService {
transition, transition,
}) async { }) async {
return replaceWith<dynamic>(Routes.failureView, return replaceWith<dynamic>(Routes.failureView,
arguments: FailureViewArguments(key: key, label: label), arguments: FailureViewArguments(key: key, onTap: onTap, label: label),
id: routerId, id: routerId,
preventDuplicates: preventDuplicates, preventDuplicates: preventDuplicates,
parameters: parameters, parameters: parameters,
@ -2641,4 +2629,21 @@ extension NavigatorStateExtension on _i46.NavigationService {
parameters: parameters, parameters: parameters,
transition: transition); 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:json_annotation/json_annotation.dart';
import 'package:yimaru_app/models/option.dart'; import 'package:yimaru_app/models/option.dart';
part 'question.g.dart'; part 'assessment_question.g.dart';
@JsonSerializable() @JsonSerializable()
class Question { class AssessmentQuestion {
final int? id; final int? id;
final int? points; final int? points;
@ -18,21 +18,17 @@ class Question {
@JsonKey(name: 'question_text') @JsonKey(name: 'question_text')
final String? questionText; final String? questionText;
@JsonKey(name: 'difficulty_level') const AssessmentQuestion({
final String? difficultyLevel;
const Question({
this.id, this.id,
this.points, this.points,
this.status, this.status,
this.options, this.options,
this.questionText, this.questionText,
this.questionType, this.questionType,
this.difficultyLevel,
}); });
factory Question.fromJson(Map<String, dynamic> json) => factory AssessmentQuestion.fromJson(Map<String, dynamic> json) =>
_$QuestionFromJson(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 // GENERATED CODE - DO NOT MODIFY BY HAND
part of 'question.dart'; part of 'assessment_question.dart';
// ************************************************************************** // **************************************************************************
// JsonSerializableGenerator // JsonSerializableGenerator
// ************************************************************************** // **************************************************************************
Question _$QuestionFromJson(Map<String, dynamic> json) => Question( AssessmentQuestion _$AssessmentQuestionFromJson(Map<String, dynamic> json) =>
AssessmentQuestion(
id: (json['id'] as num?)?.toInt(), id: (json['id'] as num?)?.toInt(),
points: (json['points'] as num?)?.toInt(), points: (json['points'] as num?)?.toInt(),
status: json['status'] as String?, status: json['status'] as String?,
@ -15,15 +16,14 @@ Question _$QuestionFromJson(Map<String, dynamic> json) => Question(
.toList(), .toList(),
questionText: json['question_text'] as String?, questionText: json['question_text'] as String?,
questionType: json['question_type'] 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, 'id': instance.id,
'points': instance.points, 'points': instance.points,
'status': instance.status, 'status': instance.status,
'options': instance.options, 'options': instance.options,
'question_type': instance.questionType, 'question_type': instance.questionType,
'question_text': instance.questionText, 'question_text': instance.questionText,
'difficulty_level': instance.difficultyLevel,
}; };

View File

@ -11,7 +11,10 @@ class Option {
@JsonKey(name: 'option_text') @JsonKey(name: 'option_text')
final String? optionText; 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); 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(), id: (json['id'] as num?)?.toInt(),
optionText: json['option_text'] as String?, optionText: json['option_text'] as String?,
isCorrect: json['is_correct'] as bool?, isCorrect: json['is_correct'] as bool?,
optionOrder: (json['option_order'] as num?)?.toInt(),
); );
Map<String, dynamic> _$OptionToJson(Option instance) => <String, dynamic>{ Map<String, dynamic> _$OptionToJson(Option instance) => <String, dynamic>{
'id': instance.id, 'id': instance.id,
'is_correct': instance.isCorrect, 'is_correct': instance.isCorrect,
'option_text': instance.optionText, '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_practice.dart';
import 'package:yimaru_app/models/learn_program.dart'; import 'package:yimaru_app/models/learn_program.dart';
import 'package:yimaru_app/models/level.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/subcategory.dart';
import 'package:yimaru_app/models/category.dart'; import 'package:yimaru_app/models/category.dart';
import 'package:yimaru_app/models/course_lesson.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/learn_question.dart';
import '../models/lesson.dart'; import '../models/lesson.dart';
import '../models/module.dart'; import '../models/module.dart';
import '../models/assessment.dart';
import '../models/submodule.dart'; import '../models/submodule.dart';
import '../ui/common/enmus.dart'; import '../ui/common/enmus.dart';
@ -354,23 +355,47 @@ class ApiService {
} }
} }
// Get assessments // Get assessment question sets
Future<List<Question>> getAssessments() async { Future<List<Assessment>> getAssessments() async {
try { try {
List<Question> assessments = []; List<Assessment> assessments = [];
final Response response = final Response response = await _service.dio.get(
await _service.dio.get('$kBaseUrl/$kAssessmentsUrl'); '$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) { if (response.statusCode == 200) {
var data = response.data; var data = response.data;
var decodedData = data['data'] as List; var decodedData = data['data'] as List;
assessments = decodedData.map( questions = decodedData.map(
(e) { (e) {
return Question.fromJson(e); return AssessmentQuestion.fromJson(e);
}, },
).toList(); ).toList();
return assessments; return questions;
} }
return []; return [];
} catch (e) { } catch (e) {
@ -741,9 +766,9 @@ class ApiService {
} }
// Get course practic questions // Get course practic questions
Future<List<Question>> getCoursePracticeQuestions(int id) async { Future<List<AssessmentQuestion>> getCoursePracticeQuestions(int id) async {
try { try {
List<Question> coursePracticeQuestions = []; List<AssessmentQuestion> coursePracticeQuestions = [];
final Response response = await _service.dio final Response response = await _service.dio
.get('$kBaseUrl/$kPracticeBaseUrl/$id/$kCoursePracticeQuestions'); .get('$kBaseUrl/$kPracticeBaseUrl/$id/$kCoursePracticeQuestions');
@ -753,7 +778,7 @@ class ApiService {
var decodedData = data['data'] as List; var decodedData = data['data'] as List;
coursePracticeQuestions = decodedData.map( coursePracticeQuestions = decodedData.map(
(e) { (e) {
return Question.fromJson(e); return AssessmentQuestion.fromJson(e);
}, },
).toList(); ).toList();
return coursePracticeQuestions; return coursePracticeQuestions;
@ -765,13 +790,14 @@ class ApiService {
} }
// Get course practice question // Get course practice question
Future<Question?> getCoursePracticeQuestion(int id) async { Future<AssessmentQuestion?> getCoursePracticeQuestion(int id) async {
try { try {
final Response response = final Response response =
await _service.dio.get('$kBaseUrl/$kCoursePracticeQuestion/$id'); await _service.dio.get('$kBaseUrl/$kCoursePracticeQuestion/$id');
if (response.statusCode == 200) { if (response.statusCode == 200) {
Question question = Question.fromJson(response.data['data']); AssessmentQuestion question =
AssessmentQuestion.fromJson(response.data['data']);
return question; return question;
} }
@ -950,9 +976,9 @@ class ApiService {
} }
// Questions // Questions
Future<List<Question>> getQuestions(int id) async { Future<List<AssessmentQuestion>> getQuestions(int id) async {
try { try {
List<Question> questions = []; List<AssessmentQuestion> questions = [];
final Response response = await _service.dio.get( final Response response = await _service.dio.get(
'$kBaseUrl/api/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl'); '$kBaseUrl/api/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl');
@ -962,7 +988,7 @@ class ApiService {
var decodedData = data['data'] as List; var decodedData = data['data'] as List;
questions = decodedData.map( questions = decodedData.map(
(e) { (e) {
return Question.fromJson(e); return AssessmentQuestion.fromJson(e);
}, },
).toList(); ).toList();
return questions; return questions;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import 'package:yimaru_app/ui/common/enmus.dart';
import 'package:yimaru_app/ui/views/assessment/screens/assessment_questions_screen.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_intro_screen.dart';
import 'package:yimaru_app/ui/views/assessment/screens/assessment_result_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 'package:yimaru_app/ui/views/assessment/screens/start_lesson_screen.dart';
import '../../widgets/assessment_loading_screen.dart';
import 'assessment_viewmodel.dart'; import 'assessment_viewmodel.dart';
class AssessmentView extends StackedView<AssessmentViewModel> { class AssessmentView extends StackedView<AssessmentViewModel> {
@ -39,16 +41,29 @@ class AssessmentView extends StackedView<AssessmentViewModel> {
Widget _buildAssessmentScreens(AssessmentViewModel viewModel) => IndexedStack( Widget _buildAssessmentScreens(AssessmentViewModel viewModel) => IndexedStack(
index: viewModel.currentPage, index: viewModel.currentPage,
children: _buildScreens(), children: _buildScreens(viewModel),
); );
List<Widget> _buildScreens() => [ List<Widget> _buildScreens(AssessmentViewModel viewModel) => [
_buildAssessmentIntro(), _buildAssessmentIntroWrapper(viewModel),
_buildAssessment(), _buildAssessment(),
_buildAssessmentResult(), _buildAssessmentResult(),
_buildStartLesson(), _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 _buildAssessmentIntro() => const AssessmentIntroScreen();
Widget _buildAssessment() => const AssessmentQuestionsScreen(); Widget _buildAssessment() => const AssessmentQuestionsScreen();

View File

@ -1,13 +1,14 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart'; import 'package:stacked_services/stacked_services.dart';
import 'package:yimaru_app/models/assessment_question.dart';
import 'package:yimaru_app/models/option.dart'; import 'package:yimaru_app/models/option.dart';
import 'package:yimaru_app/services/status_checker_service.dart'; import 'package:yimaru_app/services/status_checker_service.dart';
import 'package:yimaru_app/ui/common/enmus.dart'; import 'package:yimaru_app/ui/common/enmus.dart';
import '../../../app/app.locator.dart'; import '../../../app/app.locator.dart';
import '../../../app/app.router.dart'; import '../../../app/app.router.dart';
import '../../../models/question.dart'; import '../../../models/assessment.dart';
import '../../../services/api_service.dart'; import '../../../services/api_service.dart';
import '../../common/app_colors.dart'; import '../../common/app_colors.dart';
import '../../common/ui_helpers.dart'; import '../../common/ui_helpers.dart';
@ -33,82 +34,78 @@ class AssessmentViewModel extends BaseViewModel {
PageController get pageController => _pageController; PageController get pageController => _pageController;
// Assessment // 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 = {}; final Map<String, dynamic> _selectedAnswers = {};
Map<String, dynamic> get selectedAnswers => _selectedAnswers; Map<String, dynamic> get selectedAnswers => _selectedAnswers;
List<AssessmentQuestion> _assessmentQuestions = [];
List<AssessmentQuestion> get assessmentQuestions => _assessmentQuestions;
// User data // User data
final Map<String, dynamic> _userData = {}; final Map<String, dynamic> _userData = {};
Map<String, dynamic> get userData => _userData; Map<String, dynamic> get userData => _userData;
// Assessment // 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() { Map<String, dynamic> evaluateAssessment() {
if (_currentQuestion == 5) { bool levelPassed = canPassLevel();
// A1 if (levelPassed) {
final correctCount = countCorrectAnswersUntil(5); return {'passed': true, 'level': _currentAssessment?.description};
if (correctCount > 3) {
return {'continue': true, 'level': ProficiencyLevels.a1};
} else { } else {
return {'continue': false, 'level': ProficiencyLevels.a1}; return {'passed': false, 'level': _currentAssessment?.description};
}
} 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};
}
} else {
return {'continue': true, 'level': ProficiencyLevels.none};
} }
} }
int countCorrectAnswersUntil(int untilQuestion) { bool canPassLevel() {
int count = 0; int count = 0;
for (int i = 1; i <= untilQuestion; i++) { for (int i = 1; i <= _assessmentQuestions.length; i++) {
final answer = _selectedAnswers[i.toString()]; final answer = _selectedAnswers[i.toString()];
if (answer is Map<String, dynamic> && answer['correct'] == true) { if (answer is Map<String, dynamic> && answer['correct'] == true) {
count++; 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}) { bool isSelectedAnswer({required int question, required String answer}) {
@ -125,7 +122,7 @@ class AssessmentViewModel extends BaseViewModel {
question.toString(): { question.toString(): {
'correct': correct, 'correct': correct,
'option': option?.optionText, 'option': option?.optionText,
'answer': _assessments[question - 1] 'answer': _assessmentQuestions[question - 1]
.options .options
?.firstWhere((e) => e.isCorrect ?? false) ?.firstWhere((e) => e.isCorrect ?? false)
.optionText .optionText
@ -152,32 +149,41 @@ class AssessmentViewModel extends BaseViewModel {
} }
// Question navigation // Question navigation
void nextQuestion() { Future<void> nextQuestion() async {
_currentQuestion++; _currentQuestionIndex++;
Map<String, dynamic> response = evaluateAssessment(); Map<String, dynamic> response = evaluateAssessment();
print('LEVEL: $response');
if (_currentQuestion == _assessments.length) { print('LENGTH: ${_assessmentQuestions.length}');
print('INDEX: $_currentQuestionIndex');
if (_currentQuestionIndex == _assessmentQuestions.length) {
_currentAssessmentIndex = _currentAssessmentIndex + 1;
if (_currentAssessmentIndex == _assessments.length) {
_proficiencyLevel = response['level']; _proficiencyLevel = response['level'];
next(); next();
} else { } else {
if (response['level'] == ProficiencyLevels.none) { if (response['passed']) {
_pageController.jumpToPage(_currentQuestion); _selectedAnswers.clear();
} else { _currentQuestionIndex = 0;
if (response['continue']) { _proficiencyLevel = response['level'];
_pageController.jumpToPage(_currentQuestion); _currentAssessment = assessments[currentAssessmentIndex];
await getAssessmentQuestions(
_currentAssessment?.id ?? 0);
_pageController.jumpToPage(_currentQuestionIndex);
} else { } else {
_proficiencyLevel = response['level']; _proficiencyLevel = response['level'];
next(); next();
} }
} }
} else {
_pageController.jumpToPage(_currentQuestionIndex);
} }
rebuildUi(); rebuildUi();
} }
void previousQuestion() { void previousQuestion() {
if (_currentQuestion != 0) { if (_currentQuestionIndex != 0) {
_currentQuestion--; _currentQuestionIndex--;
_pageController.previousPage( _pageController.previousPage(
duration: const Duration(microseconds: 100), curve: Curves.linear); duration: const Duration(microseconds: 100), curve: Curves.linear);
rebuildUi(); rebuildUi();
@ -193,7 +199,7 @@ class AssessmentViewModel extends BaseViewModel {
_currentPage = 0; _currentPage = 0;
rebuildUi(); rebuildUi();
} else if (_currentPage == 3) { } else if (_currentPage == 3) {
if (_proficiencyLevel != ProficiencyLevels.none) { if (_proficiencyLevel != null) {
_currentPage--; _currentPage--;
} else { } else {
_currentPage = 0; _currentPage = 0;
@ -240,12 +246,12 @@ class AssessmentViewModel extends BaseViewModel {
// Navigation // Navigation
void pop() => _navigationService.back(); void pop() => _navigationService.back();
Future<void> replaceWithStartUp() async =>
await _navigationService.clearStackAndShow(Routes.startupView);
Future<void> navigateToLanguage() async => Future<void> navigateToLanguage() async =>
await _navigationService.navigateToLanguageView(); await _navigationService.navigateToLanguageView();
Future<void> replaceWittStartUp() async =>
await _navigationService.clearStackAndShow(Routes.startupView);
// Remote api call // Remote api call
// Complete profile // Complete profile
@ -259,7 +265,7 @@ class AssessmentViewModel extends BaseViewModel {
await _apiService.completeProfile(_userData); await _apiService.completeProfile(_userData);
if (response['status'] == ResponseStatus.success) { if (response['status'] == ResponseStatus.success) {
clearUserData(); clearUserData();
await replaceWithStartUp(); await replaceWittStartUp();
showSuccessToast(response['message']); showSuccessToast(response['message']);
} else { } else {
showErrorToast(response['message']); showErrorToast(response['message']);
@ -268,11 +274,23 @@ class AssessmentViewModel extends BaseViewModel {
} }
// Assessments // Assessments
Future<void> getAssessments() async =>
await runBusyFuture(_getAssessments(),
busyObject: StateObjects.assessments);
Future<void> _getAssessments() async { Future<void> _getAssessments() async {
if (await _statusChecker.checkConnection()) { if (await _statusChecker.checkConnection()) {
_assessments = await _apiService.getAssessments(); _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> { class AssessmentIntroScreen extends ViewModelWidget<AssessmentViewModel> {
const AssessmentIntroScreen({super.key}); const AssessmentIntroScreen({super.key});
Future<void> _next(AssessmentViewModel viewModel) async =>
viewModel.setFirstAssessment();
@override @override
Widget build(BuildContext context, AssessmentViewModel viewModel) => Widget build(BuildContext context, AssessmentViewModel viewModel) =>
_buildScaffoldWrapper(viewModel); _buildScaffoldWrapper(viewModel);
@ -95,8 +98,8 @@ class AssessmentIntroScreen extends ViewModelWidget<AssessmentViewModel> {
text: 'Continue', text: 'Continue',
borderRadius: 12, borderRadius: 12,
foregroundColor: kcWhite, foregroundColor: kcWhite,
onTap: () => viewModel.next(),
backgroundColor: kcPrimaryColor, backgroundColor: kcPrimaryColor,
onTap: () async => await _next(viewModel),
); );
Widget _buildSkipButtonWrapper(AssessmentViewModel viewModel) => Padding( 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/custom_small_radio_button.dart';
import 'package:yimaru_app/ui/widgets/large_app_bar.dart'; import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
import '../../../widgets/assessment_loading_screen.dart';
import '../assessment_viewmodel.dart'; import '../assessment_viewmodel.dart';
import 'assessment_loading_screen.dart';
class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> { class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
const AssessmentQuestionsScreen({super.key}); const AssessmentQuestionsScreen({super.key});
@override @override
Widget build(BuildContext context, AssessmentViewModel viewModel) => Widget build(BuildContext context, AssessmentViewModel viewModel) =>
_buildAssessmentScreens(viewModel); _buildAssessmentScreensWrapper(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,
);
Widget _buildAssessmentScreensWrapper(AssessmentViewModel viewModel) => Widget _buildAssessmentScreensWrapper(AssessmentViewModel viewModel) =>
PopScope( PopScope(
@ -52,7 +39,7 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
onClose: viewModel.abort, onClose: viewModel.abort,
showLanguageSelection: false, showLanguageSelection: false,
onPop: viewModel.previousQuestion, onPop: viewModel.previousQuestion,
showBackButton: viewModel.currentQuestion == 0 ? false : true, showBackButton: viewModel.currentQuestionIndex == 0 ? false : true,
); );
Widget _buildExpandedBody(AssessmentViewModel viewModel) => Widget _buildExpandedBody(AssessmentViewModel viewModel) =>
@ -65,7 +52,7 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
Widget _buildAssessment(AssessmentViewModel viewModel) => PageView.builder( Widget _buildAssessment(AssessmentViewModel viewModel) => PageView.builder(
controller: viewModel.pageController, controller: viewModel.pageController,
itemCount: viewModel.assessments.length, itemCount: viewModel.assessmentQuestions.length,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
itemBuilder: (cotext, index) => itemBuilder: (cotext, index) =>
_buildBodyScroller(index: index, viewModel: viewModel), _buildBodyScroller(index: index, viewModel: viewModel),
@ -98,7 +85,7 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
Widget _buildTitle( Widget _buildTitle(
{required int index, required AssessmentViewModel viewModel}) => {required int index, required AssessmentViewModel viewModel}) =>
Text( Text(
'Q${index + 1}. ${viewModel.assessments[index].questionText} ', 'Q${index + 1}. ${viewModel.assessmentQuestions[index].questionText} ',
style: style16DG600, style: style16DG600,
); );
@ -107,15 +94,18 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
ListView.builder( ListView.builder(
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
itemCount: viewModel.assessments[index].options?.length, itemCount: viewModel.assessmentQuestions[index].options?.length,
itemBuilder: (context, inner) => _buildAnswer( itemBuilder: (context, inner) => _buildAnswer(
onTap: () => viewModel.setSelectedAnswer( onTap: () => viewModel.setSelectedAnswer(
question: index + 1, question: index + 1,
option: viewModel.assessments[index].options?[inner]), option: viewModel.assessmentQuestions[index].options?[inner]),
title: viewModel.assessments[index].options?[inner].optionText ?? '', title:
viewModel.assessmentQuestions[index].options?[inner].optionText ??
'',
selected: viewModel.isSelectedAnswer( selected: viewModel.isSelectedAnswer(
question: index + 1, 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()) onTap: viewModel.selectedAnswers.containsKey(question.toString())
? () => viewModel.nextQuestion() ? () => viewModel.nextQuestion()
: null, : null,
text: viewModel.currentQuestion == viewModel.assessments.length - 1 text: viewModel.currentQuestionIndex == viewModel.assessmentQuestions.length - 1
? 'Finish' ? 'Finish Level'
: 'Continue', : 'Continue',
); );
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@ import 'package:stacked_services/stacked_services.dart';
import '../../../app/app.locator.dart'; import '../../../app/app.locator.dart';
import '../../../models/option.dart'; import '../../../models/option.dart';
import '../../../models/question.dart'; import '../../../models/assessment_question.dart';
import '../../../services/api_service.dart'; import '../../../services/api_service.dart';
import '../../../services/status_checker_service.dart'; import '../../../services/status_checker_service.dart';
import '../../common/app_colors.dart'; import '../../common/app_colors.dart';
@ -38,13 +38,14 @@ class CoursePracticeQuestionViewModel extends FormViewModel {
bool get focusAnswer => _focusAnswer; 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; int _currentQuestionIndex = 0;

View File

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

View File

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

View File

@ -9,8 +9,10 @@ import 'failure_viewmodel.dart';
class FailureView extends StackedView<FailureViewModel> { class FailureView extends StackedView<FailureViewModel> {
final String label; 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 @override
FailureViewModel viewModelBuilder(BuildContext context) => FailureViewModel(); FailureViewModel viewModelBuilder(BuildContext context) => FailureViewModel();
@ -48,19 +50,38 @@ class FailureView extends StackedView<FailureViewModel> {
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: _buildUpperColumnChildren(), children: _buildColumnChildren(),
); );
List<Widget> _buildUpperColumnChildren() => List<Widget> _buildColumnChildren() =>
[_buildIconWrapper(), _buildSafeWrapper()]; [_buildIconWrapper(), _buildSafeWrapper()];
Widget _buildSafeWrapper() => SafeArea(child: _buildLoadingTextContainer()); Widget _buildIconWrapper() => Padding(
padding: const EdgeInsets.only(top: 100),
Widget _buildLoadingTextContainer() => Padding( child: _buildIcon(),
padding: const EdgeInsets.only(bottom: 50),
child: _buildLoadingTextWrapper(),
); );
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( Widget _buildLoadingTextWrapper() => Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@ -85,10 +106,14 @@ class FailureView extends StackedView<FailureViewModel> {
Widget _buildIndicator() => Widget _buildIndicator() =>
const CustomCircularProgressIndicator(color: kcWhite); const CustomCircularProgressIndicator(color: kcWhite);
Widget _buildIconWrapper() => Padding( Widget _buildRetryButtonWrapper() => GestureDetector(
padding: const EdgeInsets.only(top: 100), onTap: onTap,
child: _buildIcon(), 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:flutter/material.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import 'package:yimaru_app/ui/common/app_colors.dart'; import 'package:yimaru_app/ui/common/app_colors.dart';
import 'package:yimaru_app/ui/common/enmus.dart';
import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart'; import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart';
import 'package:yimaru_app/ui/views/profile/profile_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 'package:yimaru_app/ui/widgets/coming_soon.dart';
import '../../common/enmus.dart';
import '../../widgets/page_loading_indicator.dart';
import 'home_viewmodel.dart'; import 'home_viewmodel.dart';
class HomeView extends StackedView<HomeViewModel> { class HomeView extends StackedView<HomeViewModel> {
const HomeView({Key? key}) : super(key: key); const HomeView({Key? key}) : super(key: key);
@override
void onViewModelReady(HomeViewModel viewModel) async {
await viewModel.inAppUpdate();
super.onViewModelReady(viewModel);
}
@override @override
HomeViewModel viewModelBuilder(BuildContext context) => HomeViewModel(); HomeViewModel viewModelBuilder(BuildContext context) => HomeViewModel();
@ -20,8 +25,6 @@ class HomeView extends StackedView<HomeViewModel> {
BuildContext context, HomeViewModel viewModel, Widget? child) => BuildContext context, HomeViewModel viewModel, Widget? child) =>
_buildScaffold(viewModel); _buildScaffold(viewModel);
Widget _buildScaffold(HomeViewModel viewModel) => Scaffold( Widget _buildScaffold(HomeViewModel viewModel) => Scaffold(
body: getViewForIndex(viewModel.currentPage), body: getViewForIndex(viewModel.currentPage),
bottomNavigationBar: _buildBottomNav(viewModel), bottomNavigationBar: _buildBottomNav(viewModel),
@ -56,15 +59,14 @@ class HomeView extends StackedView<HomeViewModel> {
label: 'Profile', label: 'Profile',
icon: _buildProfileIcon(), 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) { Widget getViewForIndex(int index) {
switch (index) { switch (index) {
case 0: case 0:
return const LearnProgramView(); return const LearnProgramView();
@ -74,4 +76,5 @@ Widget getViewForIndex(int index) {
default: default:
return const ProfileView(); return const ProfileView();
} }
}
} }

View File

@ -1,21 +1,19 @@
import 'package:yimaru_app/app/app.bottomsheets.dart'; import 'package:yimaru_app/app/app.bottomsheets.dart';
import 'package:yimaru_app/app/app.locator.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/models/user.dart';
import 'package:yimaru_app/services/status_checker_service.dart'; import 'package:yimaru_app/services/status_checker_service.dart';
import 'package:yimaru_app/ui/common/app_strings.dart'; import 'package:yimaru_app/ui/common/app_strings.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart'; import 'package:stacked_services/stacked_services.dart';
import '../../../services/api_service.dart';
import '../../../services/authentication_service.dart'; import '../../../services/authentication_service.dart';
import '../../../services/image_downloader_service.dart'; import '../../../services/in_app_update_service.dart';
import '../../common/enmus.dart';
import '../../common/ui_helpers.dart';
class HomeViewModel extends ReactiveViewModel { class HomeViewModel extends ReactiveViewModel {
// Dependency injection
final _statusChecker = locator<StatusCheckerService>();
final _bottomSheetService = locator<BottomSheetService>(); final _bottomSheetService = locator<BottomSheetService>();
final _inAppUpdateService = locator<InAppUpdateService>();
final _authenticationService = locator<AuthenticationService>(); final _authenticationService = locator<AuthenticationService>();
@override @override
@ -46,9 +44,12 @@ class HomeViewModel extends ReactiveViewModel {
rebuildUi(); 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( Widget _buildAppbar(LanguageViewModel viewModel) => SmallAppBar(
showBackButton: true, showBackButton: true,
onTap: viewModel.pop, onPop: viewModel.pop,
title: 'Language Preference', title: 'Language Preference',
); );

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,7 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
@override @override
void onViewModelReady(LearnModuleViewModel viewModel) async { void onViewModelReady(LearnModuleViewModel viewModel) async {
await viewModel.getLearnModules(1); await viewModel.getLearnModules(course.id ?? 0);
super.onViewModelReady(viewModel); super.onViewModelReady(viewModel);
} }
@ -58,7 +58,7 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
); );
Widget _buildAppBar(LearnModuleViewModel viewModel) => SmallAppBar( Widget _buildAppBar(LearnModuleViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop, onPop: viewModel.pop,
showBackButton: true, showBackButton: true,
); );
@ -116,10 +116,10 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => _buildTile( itemBuilder: (context, index) => _buildTile(
module: viewModel.modules[index], module: viewModel.modules[index],
onModuleTap: () async =>
await viewModel.navigateToLearnLesson(viewModel.modules[index]),
onPracticeTap: () async => await viewModel onPracticeTap: () async => await viewModel
.navigateToLearnPractice(viewModel.modules[index].id ?? 0), .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:stacked/stacked.dart';
import 'package:yimaru_app/ui/common/enmus.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/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_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_result_screen.dart';
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_questions_screen.dart'; import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_questions_screen.dart';
@ -78,8 +79,21 @@ class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
Widget _buildBodyState(LearnPracticeViewModel viewModel) => Widget _buildBodyState(LearnPracticeViewModel viewModel) =>
viewModel.busy(StateObjects.learnPractices) viewModel.busy(StateObjects.learnPractices)
? const PageLoadingIndicator() ? const PageLoadingIndicator()
: viewModel.practices.isEmpty || viewModel.questions.isEmpty
? _buildPageLoadingIndicator(viewModel)
: _buildBody(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( Widget _buildBody(LearnPracticeViewModel viewModel) => IndexedStack(
index: viewModel.currentPage, children: _buildScreens(viewModel)); index: viewModel.currentPage, children: _buildScreens(viewModel));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -55,7 +55,7 @@ class PrivacyPolicyView extends StackedView<PrivacyPolicyViewModel> {
Widget _buildAppbar(PrivacyPolicyViewModel viewModel) => SmallAppBar( Widget _buildAppbar(PrivacyPolicyViewModel viewModel) => SmallAppBar(
showBackButton: true, showBackButton: true,
onTap: viewModel.pop, onPop: viewModel.pop,
title: 'Privacy Policy', 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/app_colors.dart';
import 'package:yimaru_app/ui/common/enmus.dart'; import 'package:yimaru_app/ui/common/enmus.dart';
import 'package:yimaru_app/ui/common/ui_helpers.dart'; import 'package:yimaru_app/ui/common/ui_helpers.dart';
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
import 'package:yimaru_app/ui/widgets/profile_card.dart'; import 'package:yimaru_app/ui/widgets/profile_card.dart';
import 'package:yimaru_app/ui/widgets/profile_image.dart'; import 'package:yimaru_app/ui/widgets/profile_image.dart';
import 'package:yimaru_app/ui/widgets/view_profile_button.dart'; import 'package:yimaru_app/ui/widgets/view_profile_button.dart';
@ -155,7 +156,7 @@ class ProfileView extends StackedView<ProfileViewModel> {
children: _buildSettingsChildren(viewModel)); children: _buildSettingsChildren(viewModel));
List<Widget> _buildSettingsChildren(ProfileViewModel viewModel) => [ List<Widget> _buildSettingsChildren(ProfileViewModel viewModel) => [
_buildDownloadsCard(viewModel), // _buildDownloadsCard(viewModel),
_buildProgressCard(viewModel), _buildProgressCard(viewModel),
_buildAccountCard(viewModel), _buildAccountCard(viewModel),
_buildSupportCard(viewModel) _buildSupportCard(viewModel)
@ -191,7 +192,7 @@ class ProfileView extends StackedView<ProfileViewModel> {
Widget _buildLogOutButton(ProfileViewModel viewModel) => CustomElevatedButton( Widget _buildLogOutButton(ProfileViewModel viewModel) => CustomElevatedButton(
height: 55, height: 55,
text: 'Log Out', text: 'Logout',
borderRadius: 12, borderRadius: 12,
foregroundColor: kcRed, foregroundColor: kcRed,
backgroundColor: kcRed.withOpacity(0.25), 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 // Dialog
Future<bool?> showAbortDialog() async { Future<bool?> showAbortDialog() async {
DialogResponse? response = await _dialogService.showDialog( DialogResponse? response = await _dialogService.showDialog(
@ -82,13 +75,6 @@ class ProfileViewModel extends ReactiveViewModel {
return response?.confirmed; return response?.confirmed;
} }
Future<void> logout() async {
bool? response = await showAbortDialog();
if (response != null && response) {
await _logout();
}
}
// Navigation // Navigation
void pop() => _navigationService.back(); void pop() => _navigationService.back();
@ -107,6 +93,9 @@ class ProfileViewModel extends ReactiveViewModel {
Future<void> navigateToSupport() async => Future<void> navigateToSupport() async =>
await _navigationService.navigateToSupportView(); await _navigationService.navigateToSupportView();
Future<void> navigateToLogin() async =>
await _navigationService.clearStackAndShow(Routes.loginView);
// Remote api call // Remote api call
// Update profile // Update profile
@ -115,10 +104,21 @@ class ProfileViewModel extends ReactiveViewModel {
Future<void> _updateProfilePicture(String image) async { Future<void> _updateProfilePicture(String image) async {
if (await _statusChecker.checkConnection()) { if (await _statusChecker.checkConnection()) {
Map<String, dynamic> data = { Map<String, dynamic> data = {'profile_picture_url': image};
'profile_picture_url': image,
};
await _apiService.updateProfileImage(data: data, userId: _user?.userId); 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 { Future<void> _update(ProfileDetailViewModel viewModel) async {
Map<String, dynamic> data = { Map<String, dynamic> data = {
'region':regionController.text, 'region': regionController.text,
'gender': viewModel.selectedGender, 'gender': viewModel.selectedGender,
'last_name': lastNameController.text, 'last_name': lastNameController.text,
'country': viewModel.selectedCountry, 'country': viewModel.selectedCountry,
'first_name': firstNameController.text, 'first_name': firstNameController.text,
'occupation': occupationController.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); viewModel.addUserData(data);
@ -75,7 +75,6 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
viewModel.clearUserData(); viewModel.clearUserData();
viewModel.setGender(viewModel.user?.gender ?? ''); viewModel.setGender(viewModel.user?.gender ?? '');
viewModel.setSelectedCountry(viewModel.user?.country ?? 'Ethiopia'); viewModel.setSelectedCountry(viewModel.user?.country ?? 'Ethiopia');
} }
@override @override
@ -147,7 +146,7 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
]; ];
Widget _buildAppbar(ProfileDetailViewModel viewModel) => SmallAppBar( Widget _buildAppbar(ProfileDetailViewModel viewModel) => SmallAppBar(
onTap: viewModel.pop, onPop: viewModel.pop,
showBackButton: true, showBackButton: true,
title: 'Edit Profile', title: 'Edit Profile',
); );
@ -194,7 +193,6 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
verticalSpaceMedium, verticalSpaceMedium,
_buildRegionFormFieldWrapper(viewModel), _buildRegionFormFieldWrapper(viewModel),
verticalSpaceMedium, verticalSpaceMedium,
_buildOccupationDropdownWrapper(viewModel), _buildOccupationDropdownWrapper(viewModel),
verticalSpaceLarge, verticalSpaceLarge,
_buildLowerColumn(viewModel) _buildLowerColumn(viewModel)
@ -535,7 +533,8 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
List<Widget> _buildRegionFormFieldChildren( List<Widget> _buildRegionFormFieldChildren(
ProfileDetailViewModel viewModel) => ProfileDetailViewModel viewModel) =>
[ [
_buildRegionFormFieldLabel(),verticalSpaceSmall, _buildRegionFormFieldLabel(),
verticalSpaceSmall,
_buildRegionFormField(viewModel), _buildRegionFormField(viewModel),
if (viewModel.hasRegionValidationMessage && viewModel.focusRegion) if (viewModel.hasRegionValidationMessage && viewModel.focusRegion)
verticalSpaceTiny, verticalSpaceTiny,

View File

@ -47,7 +47,6 @@ class ProfileDetailViewModel extends ReactiveViewModel
String? get selectedGender => _selectedGender; String? get selectedGender => _selectedGender;
// First name // First name
bool _focusPhoneNumber = false; bool _focusPhoneNumber = false;
@ -63,7 +62,6 @@ class ProfileDetailViewModel extends ReactiveViewModel
String get selectedCountry => _selectedCountry; String get selectedCountry => _selectedCountry;
// Occupation // Occupation
bool _focusOccupation = false; bool _focusOccupation = false;
@ -97,7 +95,6 @@ class ProfileDetailViewModel extends ReactiveViewModel
rebuildUi(); rebuildUi();
} }
// Phone number // Phone number
void setPhoneNumberFocus() { void setPhoneNumberFocus() {
_focusPhoneNumber = true; _focusPhoneNumber = true;
@ -269,11 +266,9 @@ class ProfileDetailViewModel extends ReactiveViewModel
void setSelectedCountry(String value) { void setSelectedCountry(String value) {
_selectedCountry = value; _selectedCountry = value;
rebuildUi(); rebuildUi();
} }
// Occupation // Occupation
void setOccupationFocus() { void setOccupationFocus() {
_focusOccupation = true; _focusOccupation = true;

View File

@ -56,7 +56,7 @@ class ProgressView extends StackedView<ProgressViewModel> {
Widget _buildAppbar(ProgressViewModel viewModel) => SmallAppBar( Widget _buildAppbar(ProgressViewModel viewModel) => SmallAppBar(
title: 'My Progress', title: 'My Progress',
showBackButton: true, showBackButton: true,
onTap: viewModel.pop, onPop: viewModel.pop,
); );
Widget _buildContentScrollViewWrapper(ProgressViewModel viewModel) => 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 '../../../app/app.locator.dart';
import '../../../models/user.dart'; import '../../../models/user.dart';
import '../../../services/google_auth_service.dart'; import '../../../services/google_auth_service.dart';
import '../../../services/smart_auth_service.dart';
import '../../../services/status_checker_service.dart'; import '../../../services/status_checker_service.dart';
class RegisterViewModel extends ReactiveViewModel class RegisterViewModel extends ReactiveViewModel

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -56,9 +56,7 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
{required BuildContext context, {required BuildContext context,
required LearnModuleViewModel viewModel}) => required LearnModuleViewModel viewModel}) =>
ExpansionTile( ExpansionTile(
enabled: true,
textColor: kcDarkGrey, textColor: kcDarkGrey,
showTrailingIcon: true,
initiallyExpanded: true, initiallyExpanded: true,
subtitle: _buildContent(), subtitle: _buildContent(),
title: _buildTitleWrapper(), title: _buildTitleWrapper(),
@ -69,13 +67,12 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
shape: Border.all(color: kcTransparent), shape: Border.all(color: kcTransparent),
expandedAlignment: Alignment.centerLeft, expandedAlignment: Alignment.centerLeft,
collapsedBackgroundColor: kcBackgroundColor, collapsedBackgroundColor: kcBackgroundColor,
enabled: (module.access?.isAccessible ?? false),
controlAffinity: ListTileControlAffinity.trailing, controlAffinity: ListTileControlAffinity.trailing,
expandedCrossAxisAlignment: CrossAxisAlignment.start, expandedCrossAxisAlignment: CrossAxisAlignment.start,
tilePadding: const EdgeInsets.symmetric(horizontal: 15), tilePadding: const EdgeInsets.symmetric(horizontal: 15),
childrenPadding: const EdgeInsets.fromLTRB(70, 0, 15, 15), childrenPadding: const EdgeInsets.fromLTRB(70, 0, 15, 15),
// enabled:(module.access?.isAccessible ?? false) , showTrailingIcon: (module.access?.isAccessible ?? false) ? true : false,
// showTrailingIcon: status != ProgressStatuses.pending ? true : false,
//initiallyExpanded: status == ProgressStatuses.started ? true : false,
children: children:
_buildExpansionTileChildren(context: context, viewModel: viewModel), _buildExpansionTileChildren(context: context, viewModel: viewModel),
); );
@ -209,9 +206,9 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
onTap: viewModel.pop, onTap: viewModel.pop,
); );
// Widget _buildContainerShaderState() => status == ProgressStatuses.pending Widget _buildContainerShaderState() => !(module.access?.isAccessible ?? false)
// ? _buildContainerShaderWrapper() ? _buildContainerShaderWrapper()
// : Container(); : Container();
Widget _buildContainerShaderWrapper() => Positioned.fill( Widget _buildContainerShaderWrapper() => Positioned.fill(
child: _buildContainerShader(), 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/common/ui_helpers.dart';
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart'; import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
class LearnPracticeTipSection extends ViewModelWidget<LearnPracticeViewModel> { class LearnPracticeTipSection extends ViewModelWidget<LearnPracticeViewModel> {
const LearnPracticeTipSection({super.key}); 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 { class SmallAppBar extends StatelessWidget {
final String? title; final String? title;
final bool showBackButton; final bool showBackButton;
final GestureTapCallback? onTap; final GestureTapCallback? onPop;
const SmallAppBar( const SmallAppBar(
{super.key, this.onTap, this.title, required this.showBackButton}); {super.key, this.onPop, this.title, required this.showBackButton});
@override @override
Widget build(BuildContext context) => _buildAppBar(); Widget build(BuildContext context) => _buildAppBar();
@ -28,7 +28,7 @@ class SmallAppBar extends StatelessWidget {
child: _buildBackButton(), child: _buildBackButton(),
); );
Widget _buildBackButton() => CustomBackButton(onTap: onTap); Widget _buildBackButton() => CustomBackButton(onTap: onPop);
Widget _buildTitleWrapper() => Align( Widget _buildTitleWrapper() => Align(
alignment: Alignment.center, alignment: Alignment.center,

View File

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