From 813720db89b69dbc2a6debb35b8eb0d721324eb7 Mon Sep 17 00:00:00 2001 From: BisratHailu Date: Wed, 10 Jun 2026 03:57:03 +0300 Subject: [PATCH] feat(course): Add lesson level duolingo practice. --- assets/translations/en.json | 14 +- lib/app/app.dart | 2 + lib/app/app.router.dart | 463 +++++++++------ lib/models/course_practice.dart | 47 ++ lib/models/course_practice.g.dart | 33 ++ lib/models/course_question.dart | 35 ++ lib/models/course_question.g.dart | 30 + lib/models/dynamic_item.dart | 21 + lib/models/dynamic_item.g.dart | 20 + lib/models/dynamic_payload.dart | 19 + lib/models/dynamic_payload.g.dart | 23 + lib/services/api_service.dart | 53 +- lib/services/course_service.dart | 14 + lib/services/push_notification_service.dart | 226 ++++---- lib/ui/common/app_constants.dart | 1 - lib/ui/common/enmus.dart | 9 +- lib/ui/common/helper_functions.dart | 18 + lib/ui/common/ui_helpers.dart | 12 +- lib/ui/views/course/course_view.dart | 4 +- lib/ui/views/course/course_viewmodel.dart | 5 +- .../course_module_viewmodel.dart | 2 + .../course_practice/course_practice_view.dart | 309 ++++++++++ .../course_practice_view.form.dart | 180 ++++++ .../course_practice_viewmodel.dart | 536 ++++++++++++++++++ .../screens/duolingo_finish_screen.dart | 144 +++++ .../screens/duolingo_intro_screen.dart | 158 ++++++ ...olingo_listening_practice_1_question.dart} | 77 +-- ...duolingo_listening_practice_1_review.dart} | 60 +- ...uolingo_listening_practice_2_question.dart | 153 +++++ ...duolingo_listening_practice_2_review.dart} | 70 +-- ...olingo_listening_practice_3_question.dart} | 88 +-- .../screens/duolingo_practices_screens.dart} | 44 +- .../screens/duolingo_retake_screen.dart | 166 ++++++ .../duolingo_speaking_practice_1_answer.dart | 207 +++++++ ...duolingo_speaking_practice_1_question.dart | 238 ++++++++ .../duolingo_speaking_practice_1_review.dart | 165 ++++++ .../duolingo_speaking_practice_2_answer.dart} | 41 +- ...uolingo_speaking_practice_2_question.dart} | 16 +- .../duolingo_speaking_practice_2_review.dart} | 44 +- .../duolingo_speaking_practice_3_answer.dart} | 16 +- ...uolingo_speaking_practice_3_question.dart} | 41 +- .../duolingo_speaking_practice_3_review.dart | 69 +++ .../duolingo_speaking_practice_4_answer.dart} | 37 +- ...uolingo_speaking_practice_4_question.dart} | 48 +- .../duolingo_speaking_practice_4_review.dart} | 35 +- ...duolingo_writing_practice_1_question.dart} | 56 +- .../duolingo_writing_practice_1_review.dart} | 22 +- .../duolingo_writing_practice_2_answer.dart} | 60 +- ...duolingo_writing_practice_2_question.dart} | 44 +- .../duolingo_writing_practice_2_review.dart} | 58 +- ...duolingo_writing_practice_3_question.dart} | 60 +- .../duolingo_writing_practice_3_review.dart} | 58 +- ...duolingo_writing_practice_4_question.dart} | 60 +- .../duolingo_writing_practice_4_review.dart} | 58 +- lib/ui/views/duolingo/duolingo_view.dart | 223 ++------ .../screens/duolingo_finish_screen.dart | 107 ---- .../screens/duolingo_intro_screen.dart | 125 ---- ...lingo_listening_assessment_2_question.dart | 150 ----- .../screens/duolingo_retake_screen.dart | 128 ----- ...duolingo_speaking_assessment_1_answer.dart | 123 ---- ...olingo_speaking_assessment_1_question.dart | 142 ----- ...duolingo_speaking_assessment_1_review.dart | 104 ---- ...duolingo_speaking_assessment_3_review.dart | 68 --- lib/ui/views/failure/failure_view.dart | 3 - .../failure/screens/first_failure_screen.dart | 82 ++- .../screens/second_failure_screen.dart | 3 - .../failure/screens/third_failure_screen.dart | 192 +++---- lib/ui/views/home/home_view.dart | 2 - lib/ui/views/home/home_viewmodel.dart | 4 +- lib/ui/views/landing/landing_view.dart | 1 - .../views/learn_module/learn_module_view.dart | 2 +- .../learn_practice/learn_practice_view.dart | 9 +- .../interact_learn_practice_screen.dart | 5 +- .../learn_practice_appreciation_screen.dart | 5 +- .../learn_practice_description_screen.dart | 5 +- .../screens/learn_practice_intro_screen.dart | 5 +- .../screens/learn_practice_result_screen.dart | 5 +- .../screens/start_learn_practice_screen.dart | 5 +- .../learn_program_viewmodel.dart | 2 +- .../views/notification/notification_view.dart | 25 +- lib/ui/views/profile/profile_viewmodel.dart | 3 +- .../startup/screens/first_startup_screen.dart | 56 +- .../screens/second_startup_screen.dart | 174 +++--- .../startup/screens/third_startup_screen.dart | 174 +++--- lib/ui/views/startup/startup_view.dart | 6 - lib/ui/views/startup/startup_viewmodel.dart | 4 - ..._sheet.dart => cancel_practice_sheet.dart} | 4 +- lib/ui/widgets/countdown_timer.dart | 90 ++- lib/ui/widgets/course_module_tile_large.dart | 6 +- lib/ui/widgets/custom_response_card.dart | 119 ++-- ...ar.dart => duolingo_practice_app_bar.dart} | 4 +- ..._card.dart => duolingo_practice_card.dart} | 4 +- ...t => duolingo_practice_question_card.dart} | 4 +- ... => duolingo_practice_review_section.dart} | 4 +- .../widgets/learn_practice_answer_card.dart | 4 +- ...ard.dart => listenable_practice_card.dart} | 4 +- lib/ui/widgets/notification_card.dart | 24 +- lib/ui/widgets/notification_icon.dart | 29 +- .../practice_loading_screen.dart} | 10 +- lib/ui/widgets/practice_result_card.dart | 56 -- lib/ui/widgets/profile_app_bar.dart | 14 +- ... => speaking_practice_review_section.dart} | 13 +- .../course_practice_viewmodel_test.dart | 11 + 103 files changed, 4220 insertions(+), 2591 deletions(-) create mode 100644 lib/models/course_practice.dart create mode 100644 lib/models/course_practice.g.dart create mode 100644 lib/models/course_question.dart create mode 100644 lib/models/course_question.g.dart create mode 100644 lib/models/dynamic_item.dart create mode 100644 lib/models/dynamic_item.g.dart create mode 100644 lib/models/dynamic_payload.dart create mode 100644 lib/models/dynamic_payload.g.dart create mode 100644 lib/ui/views/course_practice/course_practice_view.dart create mode 100644 lib/ui/views/course_practice/course_practice_view.form.dart create mode 100644 lib/ui/views/course_practice/course_practice_viewmodel.dart create mode 100644 lib/ui/views/course_practice/screens/duolingo_finish_screen.dart create mode 100644 lib/ui/views/course_practice/screens/duolingo_intro_screen.dart rename lib/ui/views/{duolingo/screens/duolingo_listening_assessment_1_question.dart => course_practice/screens/duolingo_listening_practice_1_question.dart} (50%) rename lib/ui/views/{duolingo/screens/duolingo_listening_assessment_1_review.dart => course_practice/screens/duolingo_listening_practice_1_review.dart} (59%) create mode 100644 lib/ui/views/course_practice/screens/duolingo_listening_practice_2_question.dart rename lib/ui/views/{duolingo/screens/duolingo_listening_assessment_2_review.dart => course_practice/screens/duolingo_listening_practice_2_review.dart} (57%) rename lib/ui/views/{duolingo/screens/duolingo_listening_assessment_3_question.dart => course_practice/screens/duolingo_listening_practice_3_question.dart} (51%) rename lib/ui/views/{duolingo/screens/duolingo_assessments_screens.dart => course_practice/screens/duolingo_practices_screens.dart} (58%) create mode 100644 lib/ui/views/course_practice/screens/duolingo_retake_screen.dart create mode 100644 lib/ui/views/course_practice/screens/duolingo_speaking_practice_1_answer.dart create mode 100644 lib/ui/views/course_practice/screens/duolingo_speaking_practice_1_question.dart create mode 100644 lib/ui/views/course_practice/screens/duolingo_speaking_practice_1_review.dart rename lib/ui/views/{duolingo/screens/duolingo_speaking_assessment_2_answer.dart => course_practice/screens/duolingo_speaking_practice_2_answer.dart} (68%) rename lib/ui/views/{duolingo/screens/duolingo_speaking_assessment_2_question.dart => course_practice/screens/duolingo_speaking_practice_2_question.dart} (87%) rename lib/ui/views/{duolingo/screens/duolingo_speaking_assessment_2_review.dart => course_practice/screens/duolingo_speaking_practice_2_review.dart} (60%) rename lib/ui/views/{duolingo/screens/duolingo_speaking_assessment_3_answer.dart => course_practice/screens/duolingo_speaking_practice_3_answer.dart} (86%) rename lib/ui/views/{duolingo/screens/duolingo_speaking_assessment_3_question.dart => course_practice/screens/duolingo_speaking_practice_3_question.dart} (64%) create mode 100644 lib/ui/views/course_practice/screens/duolingo_speaking_practice_3_review.dart rename lib/ui/views/{duolingo/screens/duolingo_speaking_assessment_4_answer.dart => course_practice/screens/duolingo_speaking_practice_4_answer.dart} (69%) rename lib/ui/views/{duolingo/screens/duolingo_speaking_assessment_4_question.dart => course_practice/screens/duolingo_speaking_practice_4_question.dart} (65%) rename lib/ui/views/{duolingo/screens/duolingo_speaking_assessment_4_review.dart => course_practice/screens/duolingo_speaking_practice_4_review.dart} (55%) rename lib/ui/views/{duolingo/screens/duolingo_writing_assessment_1_question.dart => course_practice/screens/duolingo_writing_practice_1_question.dart} (62%) rename lib/ui/views/{duolingo/screens/duolingo_writing_assessment_1_review.dart => course_practice/screens/duolingo_writing_practice_1_review.dart} (84%) rename lib/ui/views/{duolingo/screens/duolingo_writing_assessment_2_answer.dart => course_practice/screens/duolingo_writing_practice_2_answer.dart} (58%) rename lib/ui/views/{duolingo/screens/duolingo_writing_assessment_2_question.dart => course_practice/screens/duolingo_writing_practice_2_question.dart} (59%) rename lib/ui/views/{duolingo/screens/duolingo_writing_assessment_2_review.dart => course_practice/screens/duolingo_writing_practice_2_review.dart} (56%) rename lib/ui/views/{duolingo/screens/duolingo_writing_assessment_3_question.dart => course_practice/screens/duolingo_writing_practice_3_question.dart} (58%) rename lib/ui/views/{duolingo/screens/duolingo_writing_assessment_3_review.dart => course_practice/screens/duolingo_writing_practice_3_review.dart} (55%) rename lib/ui/views/{duolingo/screens/duolingo_writing_assessment_4_question.dart => course_practice/screens/duolingo_writing_practice_4_question.dart} (58%) rename lib/ui/views/{duolingo/screens/duolingo_writing_assessment_4_review.dart => course_practice/screens/duolingo_writing_practice_4_review.dart} (55%) delete mode 100644 lib/ui/views/duolingo/screens/duolingo_finish_screen.dart delete mode 100644 lib/ui/views/duolingo/screens/duolingo_intro_screen.dart delete mode 100644 lib/ui/views/duolingo/screens/duolingo_listening_assessment_2_question.dart delete mode 100644 lib/ui/views/duolingo/screens/duolingo_retake_screen.dart delete mode 100644 lib/ui/views/duolingo/screens/duolingo_speaking_assessment_1_answer.dart delete mode 100644 lib/ui/views/duolingo/screens/duolingo_speaking_assessment_1_question.dart delete mode 100644 lib/ui/views/duolingo/screens/duolingo_speaking_assessment_1_review.dart delete mode 100644 lib/ui/views/duolingo/screens/duolingo_speaking_assessment_3_review.dart rename lib/ui/widgets/{cancel_learn_practice_sheet.dart => cancel_practice_sheet.dart} (96%) rename lib/ui/widgets/{duolingo_assessment_app_bar.dart => duolingo_practice_app_bar.dart} (90%) rename lib/ui/widgets/{duolingo_assessment_card.dart => duolingo_practice_card.dart} (91%) rename lib/ui/widgets/{duolingo_assessment_question_card.dart => duolingo_practice_question_card.dart} (88%) rename lib/ui/widgets/{duolingo_assessment_review_section.dart => duolingo_practice_review_section.dart} (91%) rename lib/ui/widgets/{listenable_assessment_card.dart => listenable_practice_card.dart} (96%) rename lib/ui/{views/learn_practice/screens/learn_loading_screen.dart => widgets/practice_loading_screen.dart} (89%) delete mode 100644 lib/ui/widgets/practice_result_card.dart rename lib/ui/widgets/{speaking_assessment_review_section.dart => speaking_practice_review_section.dart} (78%) create mode 100644 test/viewmodels/course_practice_viewmodel_test.dart diff --git a/assets/translations/en.json b/assets/translations/en.json index 69ececd..73e56e9 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -194,5 +194,17 @@ "completed_practices": "Completed Practices", "total_practices": "Total Practices", "progress_percentage": "Progress Percentage", - "notifications": "Notifications" + "notifications": "Notifications", + "choose_course_to_improve":"Choose a course to improve your professional or exam skills.", + "start_course":"Start course", + "english_proficiency":"English Proficiency Exams", + "select_your_target_exam":"Select your target exam and start preparing", + "take_mock_exam":"Take Mock Exam", + "course_detail":"Course Detail", + "continue_course":"Continue Course", + "module_detail":"Module Detail", + "continue_module":"Continue Module", + "play_video":"Play Video", + "practice_test":"Practice Test", + "start_assessment":"Start assessment" } diff --git a/lib/app/app.dart b/lib/app/app.dart index b8493ef..608ec36 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -60,6 +60,7 @@ import 'package:yimaru_app/ui/views/payment/payment_view.dart'; import 'package:yimaru_app/ui/views/notification/notification_view.dart'; import 'package:yimaru_app/services/in_app_notification_service.dart'; import 'package:yimaru_app/services/push_notification_service.dart'; +import 'package:yimaru_app/ui/views/course_practice/course_practice_view.dart'; // @stacked-import @StackedApp( @@ -100,6 +101,7 @@ import 'package:yimaru_app/services/push_notification_service.dart'; MaterialRoute(page: LearnCourseView), MaterialRoute(page: PaymentView), MaterialRoute(page: NotificationView), + MaterialRoute(page: CoursePracticeView), // @stacked-route ], dependencies: [ diff --git a/lib/app/app.router.dart b/lib/app/app.router.dart index 2495d89..b2d41c7 100644 --- a/lib/app/app.router.dart +++ b/lib/app/app.router.dart @@ -7,18 +7,18 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'package:flutter/material.dart'; -import 'package:flutter/material.dart' as _i38; +import 'package:flutter/material.dart' as _i39; import 'package:stacked/stacked.dart' as _i1; -import 'package:stacked_services/stacked_services.dart' as _i48; -import 'package:yimaru_app/models/course.dart' as _i43; -import 'package:yimaru_app/models/course_catalog.dart' as _i45; -import 'package:yimaru_app/models/course_lesson.dart' as _i44; -import 'package:yimaru_app/models/course_module.dart' as _i46; -import 'package:yimaru_app/models/learn_course.dart' as _i39; -import 'package:yimaru_app/models/learn_lesson.dart' as _i41; -import 'package:yimaru_app/models/learn_module.dart' as _i40; -import 'package:yimaru_app/models/learn_subscription.dart' as _i47; -import 'package:yimaru_app/ui/common/enmus.dart' as _i42; +import 'package:stacked_services/stacked_services.dart' as _i49; +import 'package:yimaru_app/models/course.dart' as _i44; +import 'package:yimaru_app/models/course_catalog.dart' as _i46; +import 'package:yimaru_app/models/course_lesson.dart' as _i45; +import 'package:yimaru_app/models/course_module.dart' as _i47; +import 'package:yimaru_app/models/learn_course.dart' as _i40; +import 'package:yimaru_app/models/learn_lesson.dart' as _i42; +import 'package:yimaru_app/models/learn_module.dart' as _i41; +import 'package:yimaru_app/models/learn_subscription.dart' as _i48; +import 'package:yimaru_app/ui/common/enmus.dart' as _i43; import 'package:yimaru_app/ui/views/account_privacy/account_privacy_view.dart' as _i9; import 'package:yimaru_app/ui/views/assessment/assessment_view.dart' as _i29; @@ -33,6 +33,8 @@ import 'package:yimaru_app/ui/views/course_module/course_module_view.dart' as _i34; import 'package:yimaru_app/ui/views/course_payment/course_payment_view.dart' as _i23; +import 'package:yimaru_app/ui/views/course_practice/course_practice_view.dart' + as _i38; import 'package:yimaru_app/ui/views/course_unit/course_unit_view.dart' as _i32; import 'package:yimaru_app/ui/views/downloads/downloads_view.dart' as _i7; import 'package:yimaru_app/ui/views/duolingo/duolingo_view.dart' as _i26; @@ -148,6 +150,8 @@ class Routes { static const notificationView = '/notification-view'; + static const coursePracticeView = '/course-practice-view'; + static const all = { homeView, onboardingView, @@ -185,6 +189,7 @@ class Routes { learnCourseView, paymentView, notificationView, + coursePracticeView, }; } @@ -334,6 +339,10 @@ class StackedRouter extends _i1.RouterBase { Routes.notificationView, page: _i37.NotificationView, ), + _i1.RouteDef( + Routes.coursePracticeView, + page: _i38.CoursePracticeView, + ), ]; final _pagesMap = { @@ -341,7 +350,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const HomeViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i2.HomeView(key: args.key), settings: data, ); @@ -350,7 +359,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const OnboardingViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i3.OnboardingView(key: args.key), settings: data, ); @@ -359,7 +368,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const StartupViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i4.StartupView(key: args.key, label: args.label), settings: data, ); @@ -368,7 +377,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const ProfileViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i5.ProfileView(key: args.key), settings: data, ); @@ -377,7 +386,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const ProfileDetailViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i6.ProfileDetailView(key: args.key), settings: data, ); @@ -386,7 +395,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const DownloadsViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i7.DownloadsView(key: args.key), settings: data, ); @@ -395,7 +404,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const ProgressViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i8.ProgressView(key: args.key), settings: data, ); @@ -404,7 +413,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const AccountPrivacyViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i9.AccountPrivacyView(key: args.key), settings: data, ); @@ -413,7 +422,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const SupportViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i10.SupportView(key: args.key), settings: data, ); @@ -422,7 +431,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const TelegramSupportViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i11.TelegramSupportView(key: args.key), settings: data, ); @@ -431,7 +440,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const CallSupportViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i12.CallSupportView(key: args.key), settings: data, ); @@ -440,7 +449,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const LanguageViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i13.LanguageView(key: args.key), settings: data, ); @@ -449,7 +458,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const PrivacyPolicyViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i14.PrivacyPolicyView(key: args.key), settings: data, ); @@ -458,7 +467,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const TermsAndConditionsViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i15.TermsAndConditionsView(key: args.key), settings: data, ); @@ -467,7 +476,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const RegisterViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i16.RegisterView(key: args.key), settings: data, ); @@ -476,14 +485,14 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const LoginViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i17.LoginView(key: args.key), settings: data, ); }, _i18.LearnModuleView: (data) { final args = data.getArgs(nullOk: false); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i18.LearnModuleView( key: args.key, first: args.first, course: args.course), settings: data, @@ -491,7 +500,7 @@ class StackedRouter extends _i1.RouterBase { }, _i19.LearnLessonView: (data) { final args = data.getArgs(nullOk: false); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i19.LearnLessonView( key: args.key, first: args.first, module: args.module), settings: data, @@ -501,14 +510,14 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const ForgetPasswordViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i20.ForgetPasswordView(key: args.key), settings: data, ); }, _i21.LearnLessonDetailView: (data) { final args = data.getArgs(nullOk: false); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i21.LearnLessonDetailView( key: args.key, index: args.index, @@ -520,7 +529,7 @@ class StackedRouter extends _i1.RouterBase { }, _i22.LearnPracticeView: (data) { final args = data.getArgs(nullOk: false); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i22.LearnPracticeView( key: args.key, level: args.level, @@ -534,7 +543,7 @@ class StackedRouter extends _i1.RouterBase { }, _i23.CoursePaymentView: (data) { final args = data.getArgs(nullOk: false); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i23.CoursePaymentView(key: args.key, course: args.course), settings: data, @@ -542,7 +551,7 @@ class StackedRouter extends _i1.RouterBase { }, _i24.FailureView: (data) { final args = data.getArgs(nullOk: false); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i24.FailureView( key: args.key, onTap: args.onTap, label: args.label), settings: data, @@ -550,7 +559,7 @@ class StackedRouter extends _i1.RouterBase { }, _i25.CourseLessonDetailView: (data) { final args = data.getArgs(nullOk: false); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i25.CourseLessonDetailView(key: args.key, lesson: args.lesson), settings: data, @@ -560,7 +569,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const DuolingoViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i26.DuolingoView(key: args.key), settings: data, ); @@ -569,7 +578,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const CourseViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i27.CourseView(key: args.key), settings: data, ); @@ -578,14 +587,14 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const LearnProgramViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i28.LearnProgramView(key: args.key), settings: data, ); }, _i29.AssessmentView: (data) { final args = data.getArgs(nullOk: false); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i29.AssessmentView(key: args.key, data: args.data), settings: data, @@ -595,7 +604,7 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const LearnSubscriptionViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i30.LearnSubscriptionView(key: args.key), settings: data, ); @@ -604,14 +613,14 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const CourseCatalogViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i31.CourseCatalogView(key: args.key), settings: data, ); }, _i32.CourseUnitView: (data) { final args = data.getArgs(nullOk: false); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i32.CourseUnitView(key: args.key, catalog: args.catalog), settings: data, @@ -621,14 +630,14 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const LandingViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i33.LandingView(key: args.key), settings: data, ); }, _i34.CourseModuleView: (data) { final args = data.getArgs(nullOk: false); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i34.CourseModuleView( key: args.key, module: args.module, catalog: args.catalog), settings: data, @@ -636,7 +645,7 @@ class StackedRouter extends _i1.RouterBase { }, _i35.LearnCourseView: (data) { final args = data.getArgs(nullOk: false); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i35.LearnCourseView(key: args.key, id: args.id, first: args.first), settings: data, @@ -644,7 +653,7 @@ class StackedRouter extends _i1.RouterBase { }, _i36.PaymentView: (data) { final args = data.getArgs(nullOk: false); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i36.PaymentView( key: args.key, phone: args.phone, subscription: args.subscription), settings: data, @@ -654,11 +663,19 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs( orElse: () => const NotificationViewArguments(), ); - return _i38.MaterialPageRoute( + return _i39.MaterialPageRoute( builder: (context) => _i37.NotificationView(key: args.key), settings: data, ); }, + _i38.CoursePracticeView: (data) { + final args = data.getArgs(nullOk: false); + return _i39.MaterialPageRoute( + builder: (context) => _i38.CoursePracticeView( + key: args.key, id: args.id, practice: args.practice), + settings: data, + ); + }, }; @override @@ -671,7 +688,7 @@ class StackedRouter extends _i1.RouterBase { class HomeViewArguments { const HomeViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -693,7 +710,7 @@ class HomeViewArguments { class OnboardingViewArguments { const OnboardingViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -718,7 +735,7 @@ class StartupViewArguments { this.label, }); - final _i38.Key? key; + final _i39.Key? key; final String? label; @@ -742,7 +759,7 @@ class StartupViewArguments { class ProfileViewArguments { const ProfileViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -764,7 +781,7 @@ class ProfileViewArguments { class ProfileDetailViewArguments { const ProfileDetailViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -786,7 +803,7 @@ class ProfileDetailViewArguments { class DownloadsViewArguments { const DownloadsViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -808,7 +825,7 @@ class DownloadsViewArguments { class ProgressViewArguments { const ProgressViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -830,7 +847,7 @@ class ProgressViewArguments { class AccountPrivacyViewArguments { const AccountPrivacyViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -852,7 +869,7 @@ class AccountPrivacyViewArguments { class SupportViewArguments { const SupportViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -874,7 +891,7 @@ class SupportViewArguments { class TelegramSupportViewArguments { const TelegramSupportViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -896,7 +913,7 @@ class TelegramSupportViewArguments { class CallSupportViewArguments { const CallSupportViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -918,7 +935,7 @@ class CallSupportViewArguments { class LanguageViewArguments { const LanguageViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -940,7 +957,7 @@ class LanguageViewArguments { class PrivacyPolicyViewArguments { const PrivacyPolicyViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -962,7 +979,7 @@ class PrivacyPolicyViewArguments { class TermsAndConditionsViewArguments { const TermsAndConditionsViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -984,7 +1001,7 @@ class TermsAndConditionsViewArguments { class RegisterViewArguments { const RegisterViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -1006,7 +1023,7 @@ class RegisterViewArguments { class LoginViewArguments { const LoginViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -1032,11 +1049,11 @@ class LearnModuleViewArguments { required this.course, }); - final _i38.Key? key; + final _i39.Key? key; final bool first; - final _i39.LearnCourse course; + final _i40.LearnCourse course; @override String toString() { @@ -1062,11 +1079,11 @@ class LearnLessonViewArguments { required this.module, }); - final _i38.Key? key; + final _i39.Key? key; final bool first; - final _i40.LearnModule module; + final _i41.LearnModule module; @override String toString() { @@ -1088,7 +1105,7 @@ class LearnLessonViewArguments { class ForgetPasswordViewArguments { const ForgetPasswordViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -1116,13 +1133,13 @@ class LearnLessonDetailViewArguments { required this.hasPractice, }); - final _i38.Key? key; + final _i39.Key? key; final int index; - final _i41.LearnLesson lesson; + final _i42.LearnLesson lesson; - final _i40.LearnModule module; + final _i41.LearnModule module; final bool hasPractice; @@ -1162,7 +1179,7 @@ class LearnPracticeViewArguments { required this.subtitle, }); - final _i38.Key? key; + final _i39.Key? key; final String? level; @@ -1172,7 +1189,7 @@ class LearnPracticeViewArguments { final String title; - final _i42.LearnPractices practice; + final _i43.LearnPractices practice; final String subtitle; @@ -1211,9 +1228,9 @@ class CoursePaymentViewArguments { required this.course, }); - final _i38.Key? key; + final _i39.Key? key; - final _i43.Course course; + final _i44.Course course; @override String toString() { @@ -1239,7 +1256,7 @@ class FailureViewArguments { required this.label, }); - final _i38.Key? key; + final _i39.Key? key; final void Function() onTap; @@ -1268,9 +1285,9 @@ class CourseLessonDetailViewArguments { required this.lesson, }); - final _i38.Key? key; + final _i39.Key? key; - final _i44.CourseLesson lesson; + final _i45.CourseLesson lesson; @override String toString() { @@ -1292,7 +1309,7 @@ class CourseLessonDetailViewArguments { class DuolingoViewArguments { const DuolingoViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -1314,7 +1331,7 @@ class DuolingoViewArguments { class CourseViewArguments { const CourseViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -1336,7 +1353,7 @@ class CourseViewArguments { class LearnProgramViewArguments { const LearnProgramViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -1361,7 +1378,7 @@ class AssessmentViewArguments { required this.data, }); - final _i38.Key? key; + final _i39.Key? key; final Map data; @@ -1385,7 +1402,7 @@ class AssessmentViewArguments { class LearnSubscriptionViewArguments { const LearnSubscriptionViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -1407,7 +1424,7 @@ class LearnSubscriptionViewArguments { class CourseCatalogViewArguments { const CourseCatalogViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -1432,9 +1449,9 @@ class CourseUnitViewArguments { required this.catalog, }); - final _i38.Key? key; + final _i39.Key? key; - final _i45.CourseCatalog catalog; + final _i46.CourseCatalog catalog; @override String toString() { @@ -1456,7 +1473,7 @@ class CourseUnitViewArguments { class LandingViewArguments { const LandingViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -1482,11 +1499,11 @@ class CourseModuleViewArguments { required this.catalog, }); - final _i38.Key? key; + final _i39.Key? key; - final _i46.CourseModule? module; + final _i47.CourseModule? module; - final _i45.CourseCatalog catalog; + final _i46.CourseCatalog catalog; @override String toString() { @@ -1514,7 +1531,7 @@ class LearnCourseViewArguments { required this.first, }); - final _i38.Key? key; + final _i39.Key? key; final int id; @@ -1544,11 +1561,11 @@ class PaymentViewArguments { required this.subscription, }); - final _i38.Key? key; + final _i39.Key? key; final String phone; - final _i47.LearnSubscription subscription; + final _i48.LearnSubscription subscription; @override String toString() { @@ -1572,7 +1589,7 @@ class PaymentViewArguments { class NotificationViewArguments { const NotificationViewArguments({this.key}); - final _i38.Key? key; + final _i39.Key? key; @override String toString() { @@ -1591,9 +1608,39 @@ class NotificationViewArguments { } } -extension NavigatorStateExtension on _i48.NavigationService { +class CoursePracticeViewArguments { + const CoursePracticeViewArguments({ + this.key, + required this.id, + required this.practice, + }); + + final _i39.Key? key; + + final int id; + + final _i43.CoursePractices practice; + + @override + String toString() { + return '{"key": "$key", "id": "$id", "practice": "$practice"}'; + } + + @override + bool operator ==(covariant CoursePracticeViewArguments other) { + if (identical(this, other)) return true; + return other.key == key && other.id == id && other.practice == practice; + } + + @override + int get hashCode { + return key.hashCode ^ id.hashCode ^ practice.hashCode; + } +} + +extension NavigatorStateExtension on _i49.NavigationService { Future navigateToHomeView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1609,7 +1656,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToOnboardingView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1625,7 +1672,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToStartupView({ - _i38.Key? key, + _i39.Key? key, String? label, int? routerId, bool preventDuplicates = true, @@ -1642,7 +1689,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToProfileView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1658,7 +1705,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToProfileDetailView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1674,7 +1721,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToDownloadsView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1690,7 +1737,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToProgressView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1706,7 +1753,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToAccountPrivacyView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1722,7 +1769,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToSupportView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1738,7 +1785,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToTelegramSupportView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1754,7 +1801,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToCallSupportView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1770,7 +1817,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToLanguageView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1786,7 +1833,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToPrivacyPolicyView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1802,7 +1849,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToTermsAndConditionsView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1818,7 +1865,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToRegisterView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1834,7 +1881,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToLoginView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1850,9 +1897,9 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToLearnModuleView({ - _i38.Key? key, + _i39.Key? key, required bool first, - required _i39.LearnCourse course, + required _i40.LearnCourse course, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1869,9 +1916,9 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToLearnLessonView({ - _i38.Key? key, + _i39.Key? key, required bool first, - required _i40.LearnModule module, + required _i41.LearnModule module, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1888,7 +1935,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToForgetPasswordView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1904,10 +1951,10 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToLearnLessonDetailView({ - _i38.Key? key, + _i39.Key? key, required int index, - required _i41.LearnLesson lesson, - required _i40.LearnModule module, + required _i42.LearnLesson lesson, + required _i41.LearnModule module, required bool hasPractice, int? routerId, bool preventDuplicates = true, @@ -1929,12 +1976,12 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToLearnPracticeView({ - _i38.Key? key, + _i39.Key? key, String? level, required int id, required String label, required String title, - required _i42.LearnPractices practice, + required _i43.LearnPractices practice, required String subtitle, int? routerId, bool preventDuplicates = true, @@ -1958,8 +2005,8 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToCoursePaymentView({ - _i38.Key? key, - required _i43.Course course, + _i39.Key? key, + required _i44.Course course, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1975,7 +2022,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToFailureView({ - _i38.Key? key, + _i39.Key? key, required void Function() onTap, required String label, int? routerId, @@ -1993,8 +2040,8 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToCourseLessonDetailView({ - _i38.Key? key, - required _i44.CourseLesson lesson, + _i39.Key? key, + required _i45.CourseLesson lesson, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2010,7 +2057,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToDuolingoView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2026,7 +2073,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToCourseView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2042,7 +2089,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToLearnProgramView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2058,7 +2105,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToAssessmentView({ - _i38.Key? key, + _i39.Key? key, required Map data, int? routerId, bool preventDuplicates = true, @@ -2075,7 +2122,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToLearnSubscriptionView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2091,7 +2138,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToCourseCatalogView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2107,8 +2154,8 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToCourseUnitView({ - _i38.Key? key, - required _i45.CourseCatalog catalog, + _i39.Key? key, + required _i46.CourseCatalog catalog, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2124,7 +2171,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToLandingView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2140,9 +2187,9 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToCourseModuleView({ - _i38.Key? key, - required _i46.CourseModule? module, - required _i45.CourseCatalog catalog, + _i39.Key? key, + required _i47.CourseModule? module, + required _i46.CourseCatalog catalog, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2159,7 +2206,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToLearnCourseView({ - _i38.Key? key, + _i39.Key? key, required int id, required bool first, int? routerId, @@ -2177,9 +2224,9 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToPaymentView({ - _i38.Key? key, + _i39.Key? key, required String phone, - required _i47.LearnSubscription subscription, + required _i48.LearnSubscription subscription, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2196,7 +2243,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future navigateToNotificationView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2211,8 +2258,27 @@ extension NavigatorStateExtension on _i48.NavigationService { transition: transition); } + Future navigateToCoursePracticeView({ + _i39.Key? key, + required int id, + required _i43.CoursePractices practice, + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + }) async { + return navigateTo(Routes.coursePracticeView, + arguments: + CoursePracticeViewArguments(key: key, id: id, practice: practice), + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + Future replaceWithHomeView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2228,7 +2294,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithOnboardingView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2244,7 +2310,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithStartupView({ - _i38.Key? key, + _i39.Key? key, String? label, int? routerId, bool preventDuplicates = true, @@ -2261,7 +2327,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithProfileView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2277,7 +2343,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithProfileDetailView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2293,7 +2359,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithDownloadsView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2309,7 +2375,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithProgressView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2325,7 +2391,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithAccountPrivacyView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2341,7 +2407,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithSupportView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2357,7 +2423,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithTelegramSupportView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2373,7 +2439,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithCallSupportView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2389,7 +2455,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithLanguageView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2405,7 +2471,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithPrivacyPolicyView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2421,7 +2487,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithTermsAndConditionsView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2437,7 +2503,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithRegisterView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2453,7 +2519,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithLoginView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2469,9 +2535,9 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithLearnModuleView({ - _i38.Key? key, + _i39.Key? key, required bool first, - required _i39.LearnCourse course, + required _i40.LearnCourse course, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2488,9 +2554,9 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithLearnLessonView({ - _i38.Key? key, + _i39.Key? key, required bool first, - required _i40.LearnModule module, + required _i41.LearnModule module, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2507,7 +2573,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithForgetPasswordView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2523,10 +2589,10 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithLearnLessonDetailView({ - _i38.Key? key, + _i39.Key? key, required int index, - required _i41.LearnLesson lesson, - required _i40.LearnModule module, + required _i42.LearnLesson lesson, + required _i41.LearnModule module, required bool hasPractice, int? routerId, bool preventDuplicates = true, @@ -2548,12 +2614,12 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithLearnPracticeView({ - _i38.Key? key, + _i39.Key? key, String? level, required int id, required String label, required String title, - required _i42.LearnPractices practice, + required _i43.LearnPractices practice, required String subtitle, int? routerId, bool preventDuplicates = true, @@ -2577,8 +2643,8 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithCoursePaymentView({ - _i38.Key? key, - required _i43.Course course, + _i39.Key? key, + required _i44.Course course, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2594,7 +2660,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithFailureView({ - _i38.Key? key, + _i39.Key? key, required void Function() onTap, required String label, int? routerId, @@ -2612,8 +2678,8 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithCourseLessonDetailView({ - _i38.Key? key, - required _i44.CourseLesson lesson, + _i39.Key? key, + required _i45.CourseLesson lesson, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2629,7 +2695,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithDuolingoView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2645,7 +2711,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithCourseView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2661,7 +2727,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithLearnProgramView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2677,7 +2743,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithAssessmentView({ - _i38.Key? key, + _i39.Key? key, required Map data, int? routerId, bool preventDuplicates = true, @@ -2694,7 +2760,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithLearnSubscriptionView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2710,7 +2776,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithCourseCatalogView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2726,8 +2792,8 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithCourseUnitView({ - _i38.Key? key, - required _i45.CourseCatalog catalog, + _i39.Key? key, + required _i46.CourseCatalog catalog, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2743,7 +2809,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithLandingView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2759,9 +2825,9 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithCourseModuleView({ - _i38.Key? key, - required _i46.CourseModule? module, - required _i45.CourseCatalog catalog, + _i39.Key? key, + required _i47.CourseModule? module, + required _i46.CourseCatalog catalog, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2778,7 +2844,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithLearnCourseView({ - _i38.Key? key, + _i39.Key? key, required int id, required bool first, int? routerId, @@ -2796,9 +2862,9 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithPaymentView({ - _i38.Key? key, + _i39.Key? key, required String phone, - required _i47.LearnSubscription subscription, + required _i48.LearnSubscription subscription, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2815,7 +2881,7 @@ extension NavigatorStateExtension on _i48.NavigationService { } Future replaceWithNotificationView({ - _i38.Key? key, + _i39.Key? key, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2829,4 +2895,23 @@ extension NavigatorStateExtension on _i48.NavigationService { parameters: parameters, transition: transition); } + + Future replaceWithCoursePracticeView({ + _i39.Key? key, + required int id, + required _i43.CoursePractices practice, + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + }) async { + return replaceWith(Routes.coursePracticeView, + arguments: + CoursePracticeViewArguments(key: key, id: id, practice: practice), + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } } diff --git a/lib/models/course_practice.dart b/lib/models/course_practice.dart new file mode 100644 index 0000000..8e1845d --- /dev/null +++ b/lib/models/course_practice.dart @@ -0,0 +1,47 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'course_practice.g.dart'; + +@JsonSerializable() +class CoursePractice { + final int? id; + + final String? title; + + @JsonKey(name: 'lesson_id') + final int? lessonId; + + @JsonKey(name: 'persona_id') + final int? personaId; + + @JsonKey(name: 'quick_tips') + final String? quickTips; + + @JsonKey(name: 'story_image') + final String? storyImage; + + @JsonKey(name: 'publish_status') + final String? publishStatus; + + @JsonKey(name: 'question_set_id') + final int? questionSetId; + + @JsonKey(name: 'story_description') + final String? storyDescription; + + const CoursePractice( + {this.id, + this.title, + this.lessonId, + this.quickTips, + this.storyImage, + this.personaId, + this.publishStatus, + this.questionSetId, + this.storyDescription}); + + factory CoursePractice.fromJson(Map json) => + _$CoursePracticeFromJson(json); + + Map toJson() => _$CoursePracticeToJson(this); +} diff --git a/lib/models/course_practice.g.dart b/lib/models/course_practice.g.dart new file mode 100644 index 0000000..e9b28e3 --- /dev/null +++ b/lib/models/course_practice.g.dart @@ -0,0 +1,33 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'course_practice.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +CoursePractice _$CoursePracticeFromJson(Map json) => + CoursePractice( + id: (json['id'] as num?)?.toInt(), + title: json['title'] as String?, + lessonId: (json['lesson_id'] as num?)?.toInt(), + quickTips: json['quick_tips'] as String?, + storyImage: json['story_image'] as String?, + personaId: (json['persona_id'] as num?)?.toInt(), + publishStatus: json['publish_status'] as String?, + questionSetId: (json['question_set_id'] as num?)?.toInt(), + storyDescription: json['story_description'] as String?, + ); + +Map _$CoursePracticeToJson(CoursePractice instance) => + { + 'id': instance.id, + 'title': instance.title, + 'lesson_id': instance.lessonId, + 'persona_id': instance.personaId, + 'quick_tips': instance.quickTips, + 'story_image': instance.storyImage, + 'publish_status': instance.publishStatus, + 'question_set_id': instance.questionSetId, + 'story_description': instance.storyDescription, + }; diff --git a/lib/models/course_question.dart b/lib/models/course_question.dart new file mode 100644 index 0000000..0c0a6e7 --- /dev/null +++ b/lib/models/course_question.dart @@ -0,0 +1,35 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:yimaru_app/models/dynamic_payload.dart'; + +part 'course_question.g.dart'; + +@JsonSerializable() +class CourseQuestion { + final int? id; + + final int? points; + + final String? status; + + @JsonKey(name: 'question_type') + final String? questionType; + + @JsonKey(name: 'difficulty_level') + final String? difficultyLevel; + + @JsonKey(name: 'dynamic_payload') + final DynamicPayload? dynamicPayload; + + const CourseQuestion( + {this.id, + this.status, + this.points, + this.questionType, + this.difficultyLevel, + this.dynamicPayload}); + + factory CourseQuestion.fromJson(Map json) => + _$CourseQuestionFromJson(json); + + Map toJson() => _$CourseQuestionToJson(this); +} diff --git a/lib/models/course_question.g.dart b/lib/models/course_question.g.dart new file mode 100644 index 0000000..4911841 --- /dev/null +++ b/lib/models/course_question.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'course_question.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +CourseQuestion _$CourseQuestionFromJson(Map json) => + CourseQuestion( + id: (json['id'] as num?)?.toInt(), + status: json['status'] as String?, + points: (json['points'] as num?)?.toInt(), + questionType: json['question_type'] as String?, + difficultyLevel: json['difficulty_level'] as String?, + dynamicPayload: json['dynamic_payload'] == null + ? null + : DynamicPayload.fromJson( + json['dynamic_payload'] as Map), + ); + +Map _$CourseQuestionToJson(CourseQuestion instance) => + { + 'id': instance.id, + 'points': instance.points, + 'status': instance.status, + 'question_type': instance.questionType, + 'difficulty_level': instance.difficultyLevel, + 'dynamic_payload': instance.dynamicPayload, + }; diff --git a/lib/models/dynamic_item.dart b/lib/models/dynamic_item.dart new file mode 100644 index 0000000..178ca53 --- /dev/null +++ b/lib/models/dynamic_item.dart @@ -0,0 +1,21 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'dynamic_item.g.dart'; + +@JsonSerializable() +class DynamicItem { + final String id; + final String kind; + final dynamic value; + + DynamicItem({ + required this.id, + required this.kind, + required this.value, + }); + + factory DynamicItem.fromJson(Map json) => + _$DynamicItemFromJson(json); + + Map toJson() => _$DynamicItemToJson(this); +} diff --git a/lib/models/dynamic_item.g.dart b/lib/models/dynamic_item.g.dart new file mode 100644 index 0000000..92f79f2 --- /dev/null +++ b/lib/models/dynamic_item.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'dynamic_item.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DynamicItem _$DynamicItemFromJson(Map json) => DynamicItem( + id: json['id'] as String, + kind: json['kind'] as String, + value: json['value'], + ); + +Map _$DynamicItemToJson(DynamicItem instance) => + { + 'id': instance.id, + 'kind': instance.kind, + 'value': instance.value, + }; diff --git a/lib/models/dynamic_payload.dart b/lib/models/dynamic_payload.dart new file mode 100644 index 0000000..d3e1ee2 --- /dev/null +++ b/lib/models/dynamic_payload.dart @@ -0,0 +1,19 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'dynamic_item.dart'; + +part 'dynamic_payload.g.dart'; + +@JsonSerializable() +class DynamicPayload { + final List? stimulus; + + final List? response; + + const DynamicPayload({this.stimulus, this.response}); + + factory DynamicPayload.fromJson(Map json) => + _$DynamicPayloadFromJson(json); + + Map toJson() => _$DynamicPayloadToJson(this); +} diff --git a/lib/models/dynamic_payload.g.dart b/lib/models/dynamic_payload.g.dart new file mode 100644 index 0000000..6c02198 --- /dev/null +++ b/lib/models/dynamic_payload.g.dart @@ -0,0 +1,23 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'dynamic_payload.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DynamicPayload _$DynamicPayloadFromJson(Map json) => + DynamicPayload( + stimulus: (json['stimulus'] as List?) + ?.map((e) => DynamicItem.fromJson(e as Map)) + .toList(), + response: (json['response'] as List?) + ?.map((e) => DynamicItem.fromJson(e as Map)) + .toList(), + ); + +Map _$DynamicPayloadToJson(DynamicPayload instance) => + { + 'stimulus': instance.stimulus, + 'response': instance.response, + }; diff --git a/lib/services/api_service.dart b/lib/services/api_service.dart index 252bea4..302e8c7 100644 --- a/lib/services/api_service.dart +++ b/lib/services/api_service.dart @@ -14,6 +14,8 @@ import 'package:yimaru_app/ui/common/app_constants.dart'; import '../app/app.locator.dart'; import '../models/course_module.dart'; +import '../models/course_practice.dart'; +import '../models/course_question.dart'; import '../models/course_unit.dart'; import '../models/field_option.dart'; import '../models/learn_course.dart'; @@ -903,7 +905,7 @@ class ApiService { print('Here'); final Response response = await _service.dio.get( - '$kBaseUrl/api/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl'); + '$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl'); if (response.statusCode == 200) { var data = response.data; @@ -1144,6 +1146,55 @@ class ApiService { } } + // Course practices + Future> getCoursePractices(int id) async { + try { + List practices = []; + + final Response response = await _service.dio.get( + '$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kExamPrepUrl/$kLessonsUrl/$id/$kPracticesUrl'); + + if (response.statusCode == 200) { + var data = response.data; + var decodedData = data['data']['practices'] as List; + practices = decodedData.map( + (e) { + return CoursePractice.fromJson(e); + }, + ).toList(); + return practices; + } + return []; + } catch (e) { + return []; + } + } + + // Get course questions + Future> getCourseQuestions(int id) async { + try { + List questions = []; + print('Here'); + + final Response response = await _service.dio.get( + '$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl'); + + if (response.statusCode == 200) { + var data = response.data; + var decodedData = data['data'] as List; + questions = decodedData.map( + (e) { + return CourseQuestion.fromJson(e); + }, + ).toList(); + return questions; + } + return []; + } catch (e) { + return []; + } + } + // Check update Future> checkUpdate(Map data) async { try { diff --git a/lib/services/course_service.dart b/lib/services/course_service.dart index b4758d8..df2ebae 100644 --- a/lib/services/course_service.dart +++ b/lib/services/course_service.dart @@ -6,6 +6,8 @@ import '../models/course_catalog.dart'; import '../models/course_lesson.dart'; import '../models/course_module.dart'; import '../models/course_unit.dart'; +import '../models/refresh_object.dart'; +import '../ui/common/enmus.dart'; class CourseService with ListenableServiceMixin { // Dependency injection @@ -76,4 +78,16 @@ class CourseService with ListenableServiceMixin { _lessons.sort((a, b) => (a.sortOrder ?? 0).compareTo(b.sortOrder ?? 0)); notifyListeners(); } + + Future refreshObject(String url) async { + Map data = {'reference': url}; + Map response = await _apiService.refreshObject(data); + + if (response['status'] == ResponseStatus.success) { + RefreshObject object = response['data'] as RefreshObject; + + return object.url ?? ''; + } + return null; + } } diff --git a/lib/services/push_notification_service.dart b/lib/services/push_notification_service.dart index f435ce3..be7c448 100644 --- a/lib/services/push_notification_service.dart +++ b/lib/services/push_notification_service.dart @@ -7,130 +7,132 @@ Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await locator().setupFlutterNotifications(); await locator().showNotification(message); } -class PushNotificationService { final _messaging = FirebaseMessaging.instance; -bool _isFlutterLocalNotificationInitialized = false; +class PushNotificationService { + final _messaging = FirebaseMessaging.instance; -final _localNotifications = FlutterLocalNotificationsPlugin(); + bool _isFlutterLocalNotificationInitialized = false; -Future initialize() async { - // Initialize FCM token - await updateFCMToken(); + final _localNotifications = FlutterLocalNotificationsPlugin(); - FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); + Future initialize() async { + // Initialize FCM token + await updateFCMToken(); - // Request permission - await _requestPermission(); + FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); - // setup message handle - await _setupMessageHandler(); + // Request permission + await _requestPermission(); - // Subscribe to all devices - subscribeToTopic('yimaru'); -} + // setup message handle + await _setupMessageHandler(); -Future _requestPermission() async { - await _messaging.requestPermission( - alert: true, - badge: true, - sound: true, - carPlay: false, - provisional: false, - announcement: false, - criticalAlert: false); -} - -Future setupFlutterNotifications() async { - if (_isFlutterLocalNotificationInitialized) { - return; + // Subscribe to all devices + subscribeToTopic('yimaru'); } - // Android setup - const channel = AndroidNotificationChannel( - 'yimaru', // id - 'Yimaru', // title - importance: Importance.high, - ); + Future _requestPermission() async { + await _messaging.requestPermission( + alert: true, + badge: true, + sound: true, + carPlay: false, + provisional: false, + announcement: false, + criticalAlert: false); + } - await _localNotifications - .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() - ?.createNotificationChannel(channel); + Future setupFlutterNotifications() async { + if (_isFlutterLocalNotificationInitialized) { + return; + } - const initializationSettingsAndroid = - AndroidInitializationSettings('@mipmap/ic_launcher'); - - // IOS setup - const initializationSettingsDarwin = DarwinInitializationSettings(); - - const initializationSettings = InitializationSettings( - android: initializationSettingsAndroid, - iOS: initializationSettingsDarwin); - - // Flutter notification setup - await _localNotifications.initialize( - settings: initializationSettings, - onDidReceiveNotificationResponse: (NotificationResponse response) { - if (response.payload == 'Page') { - // navigatorKey.currentState?.pushNamed('RouteName'); - } - }, - ); - - _isFlutterLocalNotificationInitialized = true; -} - -Future showNotification(RemoteMessage message) async { - RemoteNotification? notification = message.notification; - AndroidNotification? android = message.notification?.android; - - if (notification != null && android != null) { - await _localNotifications.show( - id: notification.hashCode, - title: notification.title, - body: notification.body, - notificationDetails: const NotificationDetails( - android: AndroidNotificationDetails('yimaru', 'Yimaru', - enableVibration: true, - priority: Priority.high, - icon: '@mipmap/ic_launcher', - importance: Importance.high), - iOS: DarwinNotificationDetails( - presentAlert: true, presentBadge: true, presentSound: true)), + // Android setup + const channel = AndroidNotificationChannel( + 'yimaru', // id + 'Yimaru', // title + importance: Importance.high, ); + + await _localNotifications + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.createNotificationChannel(channel); + + const initializationSettingsAndroid = + AndroidInitializationSettings('@mipmap/ic_launcher'); + + // IOS setup + const initializationSettingsDarwin = DarwinInitializationSettings(); + + const initializationSettings = InitializationSettings( + android: initializationSettingsAndroid, + iOS: initializationSettingsDarwin); + + // Flutter notification setup + await _localNotifications.initialize( + settings: initializationSettings, + onDidReceiveNotificationResponse: (NotificationResponse response) { + if (response.payload == 'Page') { + // navigatorKey.currentState?.pushNamed('RouteName'); + } + }, + ); + + _isFlutterLocalNotificationInitialized = true; + } + + Future showNotification(RemoteMessage message) async { + RemoteNotification? notification = message.notification; + AndroidNotification? android = message.notification?.android; + + if (notification != null && android != null) { + await _localNotifications.show( + id: notification.hashCode, + title: notification.title, + body: notification.body, + notificationDetails: const NotificationDetails( + android: AndroidNotificationDetails('yimaru', 'Yimaru', + enableVibration: true, + priority: Priority.high, + icon: '@mipmap/ic_launcher', + importance: Importance.high), + iOS: DarwinNotificationDetails( + presentAlert: true, presentBadge: true, presentSound: true)), + ); + } + } + + Future _setupMessageHandler() async { + // Foreground message + FirebaseMessaging.onMessage + .listen((RemoteMessage message) => showNotification(message)); + + // Background message + FirebaseMessaging.onMessageOpenedApp.listen(_handleBackgroundMessage); + + // Opened app + final initialMessage = await _messaging.getInitialMessage(); + + if (initialMessage != null) { + _handleBackgroundMessage(initialMessage); + } + } + + void _handleBackgroundMessage(RemoteMessage message) { + if (message.data['type'] == 'Page') { + // navigatorKey.currentState?.pushNamed('RouteName'); + } + } + + Future subscribeToTopic(String topic) async { + await FirebaseMessaging.instance.subscribeToTopic(topic); + } + + Future updateFCMToken() async { + // print('DEVICE TOKEN: ${await _messaging.getToken()}'); + _messaging.onTokenRefresh.listen((newToken) { + // updateTokenOnServer(newToken); + }); } } - -Future _setupMessageHandler() async { - // Foreground message - FirebaseMessaging.onMessage - .listen((RemoteMessage message) => showNotification(message)); - - // Background message - FirebaseMessaging.onMessageOpenedApp.listen(_handleBackgroundMessage); - - // Opened app - final initialMessage = await _messaging.getInitialMessage(); - - if (initialMessage != null) { - _handleBackgroundMessage(initialMessage); - } -} - -void _handleBackgroundMessage(RemoteMessage message) { - if (message.data['type'] == 'Page') { - // navigatorKey.currentState?.pushNamed('RouteName'); - } -} - -Future subscribeToTopic(String topic) async { - await FirebaseMessaging.instance.subscribeToTopic(topic); -} - -Future updateFCMToken() async { - // print('DEVICE TOKEN: ${await _messaging.getToken()}'); - _messaging.onTokenRefresh.listen((newToken) { - // updateTokenOnServer(newToken); - }); -} -} diff --git a/lib/ui/common/app_constants.dart b/lib/ui/common/app_constants.dart index fbece22..32fb617 100644 --- a/lib/ui/common/app_constants.dart +++ b/lib/ui/common/app_constants.dart @@ -21,7 +21,6 @@ String kLevelsUrl = 'levels'; String kCoursesUrl = 'courses'; - String kModulesUrl = 'modules'; String kLessonsUrl = 'lessons'; diff --git a/lib/ui/common/enmus.dart b/lib/ui/common/enmus.dart index f2858b6..7cdcdf8 100644 --- a/lib/ui/common/enmus.dart +++ b/lib/ui/common/enmus.dart @@ -16,8 +16,8 @@ enum LearnPractices { course, module, lesson } // Voice recording state enum VoiceRecordingState { pending, recording } -// // Levels -// enum ProficiencyLevels { a1, a2, b1, b2, none } +// Course practice +enum CoursePractices { courseCatalog, unit, lesson } // Progress status enum ProgressStatuses { pending, started, completed } @@ -67,10 +67,13 @@ enum StateObjects { learnPracticeAnswer, loginWithPhoneNumber, assessmentQuestions, + coursePracticeReview, learnPracticeQuestion, completeLearnPractice, coursePracticeQuestion, coursePracticeQuestions, recordLearnPracticeAnswer, - finishLearnPracticeQuestion + recordCoursePracticeAnswer, + finishLearnPracticeQuestion, + finishCoursePracticeQuestion } diff --git a/lib/ui/common/helper_functions.dart b/lib/ui/common/helper_functions.dart index b132ff2..9dda5f8 100644 --- a/lib/ui/common/helper_functions.dart +++ b/lib/ui/common/helper_functions.dart @@ -3,6 +3,24 @@ import 'dart:ui'; import 'app_colors.dart'; +// Seconds to minutes +String getSecondsToMinutes(int totalSeconds) { + final minutes = totalSeconds ~/ 60; + final seconds = totalSeconds % 60; + + return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}'; +} + +// Minutes +int getMinutes(int totalSeconds) { + return totalSeconds ~/ 60; +} + +// Seconds +int getSeconds(int totalSeconds) { + return totalSeconds % 60; +} + // Split full name Map splitFullName(String fullName) { final parts = fullName.trim().split(RegExp(r'\s+')); diff --git a/lib/ui/common/ui_helpers.dart b/lib/ui/common/ui_helpers.dart index adc305c..10dfbdc 100644 --- a/lib/ui/common/ui_helpers.dart +++ b/lib/ui/common/ui_helpers.dart @@ -236,6 +236,11 @@ TextStyle style14B400 = const TextStyle( color: kcBlue, ); +TextStyle style0Ts = const TextStyle( + fontSize: 0, + color: kcTransparent, +); + TextStyle style14P600 = const TextStyle( color: kcPrimaryColor, fontWeight: FontWeight.w600, @@ -333,11 +338,8 @@ TextStyle style14LG400 = const TextStyle( color: kcLightGrey, ); -TextStyle style12W600 = const TextStyle( - fontSize: 12, - color: kcWhite, - fontWeight: FontWeight.w600 -); +TextStyle style12W600 = + const TextStyle(fontSize: 12, color: kcWhite, fontWeight: FontWeight.w600); TextStyle style14MG400 = const TextStyle( color: kcMediumGrey, diff --git a/lib/ui/views/course/course_view.dart b/lib/ui/views/course/course_view.dart index 8438875..a10a3d1 100644 --- a/lib/ui/views/course/course_view.dart +++ b/lib/ui/views/course/course_view.dart @@ -51,8 +51,8 @@ class CourseView extends StackedView { Widget _buildAppBar(CourseViewModel viewModel) => ProfileAppBar( name: viewModel.user?.firstName, profileImage: viewModel.user?.profilePicture, - unreadCount: viewModel.unreadCount.toString(), - onTap: () async => await viewModel.navigateToNotification(), + unreadCount: viewModel.unreadCount.toString(), + onTap: () async => await viewModel.navigateToNotification(), ); Widget _buildCategoryColumnWrapper(CourseViewModel viewModel) => diff --git a/lib/ui/views/course/course_viewmodel.dart b/lib/ui/views/course/course_viewmodel.dart index 7f08e1a..41352c6 100644 --- a/lib/ui/views/course/course_viewmodel.dart +++ b/lib/ui/views/course/course_viewmodel.dart @@ -16,10 +16,9 @@ class CourseViewModel extends ReactiveViewModel { final _inAppNotificationService = locator(); - @override List get listenableServices => - [_authenticationService,_inAppNotificationService]; + [_authenticationService, _inAppNotificationService]; // Current user User? get _user => _authenticationService.user; @@ -55,6 +54,4 @@ class CourseViewModel extends ReactiveViewModel { Future navigateToCourseCatalog() async => await _navigationService.navigateToCourseCatalogView(); - - } diff --git a/lib/ui/views/course_module/course_module_viewmodel.dart b/lib/ui/views/course_module/course_module_viewmodel.dart index b19f585..880192a 100644 --- a/lib/ui/views/course_module/course_module_viewmodel.dart +++ b/lib/ui/views/course_module/course_module_viewmodel.dart @@ -30,6 +30,8 @@ class CourseModuleViewModel extends ReactiveViewModel { Future navigateToCourseLessonDetail(CourseLesson lesson) async => await _navigationService.navigateToCourseLessonDetailView(lesson: lesson); + Future navigateToCoursePractice(int id) async => + await _navigationService.navigateToCoursePracticeView(id: id, practice: CoursePractices.lesson,); // Remote api call // Course modules diff --git a/lib/ui/views/course_practice/course_practice_view.dart b/lib/ui/views/course_practice/course_practice_view.dart new file mode 100644 index 0000000..be45241 --- /dev/null +++ b/lib/ui/views/course_practice/course_practice_view.dart @@ -0,0 +1,309 @@ +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; +import 'package:stacked/stacked_annotations.dart'; +import 'package:yimaru_app/models/course_question.dart'; +import 'package:yimaru_app/ui/common/enmus.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_listening_practice_1_review.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_listening_practice_2_review.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_practices_screens.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_finish_screen.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_intro_screen.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_listening_practice_1_question.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_listening_practice_2_question.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_listening_practice_3_question.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_retake_screen.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_1_answer.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_1_question.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_1_review.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_2_answer.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_2_question.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_2_review.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_3_answer.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_3_question.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_3_review.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_4_answer.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_4_question.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_4_review.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_1_question.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_1_review.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_2_answer.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_2_question.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_2_review.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_3_question.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_3_review.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_4_question.dart'; +import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_4_review.dart'; + +import '../../common/app_colors.dart'; +import '../../common/validators/form_validator.dart'; +import '../../widgets/cancel_practice_sheet.dart'; +import '../../widgets/page_loading_indicator.dart'; +import '../../widgets/practice_loading_screen.dart'; +import 'course_practice_view.form.dart'; +import 'course_practice_viewmodel.dart'; + +@FormView(fields: [ + FormTextField(name: 'practice', validator: FormValidator.validateForm), +]) +class CoursePracticeView extends StackedView with $CoursePracticeView { + final int id; + + final CoursePractices practice; + + const CoursePracticeView({ + Key? key, + required this.id, + required this.practice, + }) : super(key: key); + + Future _cancel(CoursePracticeViewModel viewModel) async { + await viewModel.stopRecording(); + viewModel.pop(); + viewModel.pop(); + } + + Future _showSheet( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) async => + await showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: kcTransparent, + builder: (_) => _buildSheet(viewModel), + ); + + Widget _buildQuestionScreen(CoursePracticeViewModel viewModel) { + String type = + viewModel.questions[viewModel.currentQuestion].id.toString() ?? ''; + if (type == '636') { + return const DuolingoSpeakingPractice1Question(); + } else if (type == 'Read, Then Speak') { + return const DuolingoSpeakingPractice2Question(); + } else if (type == 'Speaking Sample') { + return const DuolingoSpeakingPractice3Question(); + } else if (type == 'Interactive Speaking') { + return const DuolingoSpeakingPractice4Question(); + } else if (type == 'Write About the Photo') { + return DuolingoWritingPractice1Question( + practiceController: practiceController); + } else if (type == 'Writing Sample') { + return DuolingoWritingPractice2Question( + practiceController: practiceController); + } else if (type == 'Interactive Writing Part 1') { + return DuolingoWritingPractice3Question( + practiceController: practiceController); + } else if (type == 'Interactive Writing Part 2') { + return DuolingoWritingPractice4Question( + practiceController: practiceController); + } else if (type == 'Listen and Type') { + return DuolingoListeningPractice1Question( + practiceController: practiceController); + } else if (type == 'Interactive Listening - Part 1') { + return DuolingoListeningPractice2Question( + practiceController: practiceController); + } else if (type == 'Interactive Listening - Part 2') { + return DuolingoListeningPractice3Question( + practiceController: practiceController); + } + + return Container(); + } + + Widget _buildAnswerScreen(CoursePracticeViewModel viewModel) { + String type = + viewModel.questions[viewModel.currentQuestion].id.toString() ?? ''; + + if (type == '636') { + return const DuolingoSpeakingPractice1Answer(); + } else if (type == 'Read, Then Speak') { + return const DuolingoSpeakingPractice2Answer(); + } else if (type == 'Speaking Sample') { + return const DuolingoSpeakingPractice3Answer(); + } else if (type == 'Interactive Speaking') { + return const DuolingoSpeakingPractice4Answer(); + } else if (type == 'Write About the Photo') { + return Container(); + } else if (type == 'Writing Sample') { + return DuolingoWritingPractice2Answer( + practiceController: practiceController); + } else if (type == 'Interactive Writing Part 1') { + return Container(); + } else if (type == 'Interactive Writing Part 2') { + return Container(); + } else if (type == 'Listen and Type') { + return Container(); + } else if (type == 'Interactive Listening - Part 1') { + return Container(); + } + + return Container(); + } + + Widget _buildReviewScreen(CoursePracticeViewModel viewModel) { + String type = + viewModel.questions[viewModel.currentQuestion].id.toString() ?? ''; + + if (type == '636') { + return const DuolingoSpeakingPractice1Review(); + } else if (type == 'Read, Then Speak') { + return const DuolingoSpeakingPractice2Review(); + } else if (type == 'Speaking Sample') { + return const DuolingoSpeakingPractice3Review(); + } else if (type == 'Interactive Speaking') { + return const DuolingoSpeakingPractice4Review(); + } else if (type == 'Write About the Photo') { + return DuolingoWritingPractice1Review( + practiceController: practiceController); + } else if (type == 'Writing Sample') { + return DuolingoWritingPractice2Review( + practiceController: practiceController); + } else if (type == 'Interactive Writing Part 1') { + return DuolingoWritingPractice3Review( + practiceController: practiceController); + } else if (type == 'Interactive Writing Part 2') { + return DuolingoWritingPractice4Review( + practiceController: practiceController); + } else if (type == 'Listen and Type') { + return DuolingoListeningPractice1Review( + practiceController: practiceController); + } else if (type == 'Interactive Listening - Part 1') { + return DuolingoListeningPractice2Review( + practiceController: practiceController); + } + + return Container(); + } + + @override + void onViewModelReady(CoursePracticeViewModel viewModel) async { + await viewModel.getCoursePractices(id: id, practice: practice); + super.onViewModelReady(viewModel); + } + + + @override + CoursePracticeViewModel viewModelBuilder(BuildContext context) => + CoursePracticeViewModel(); + + @override + Widget builder( + BuildContext context, + CoursePracticeViewModel viewModel, + Widget? child, + ) => + _buildPracticeScreensWrapper(context: context, viewModel: viewModel); + + Widget _buildPracticeScreensWrapper( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, data) { + if (!didPop) { + Future.microtask(() async => + await _showSheet(context: context, viewModel: viewModel)); + } + }, + child: _buildScaffoldWrapper(viewModel)); + + Widget _buildSheet(CoursePracticeViewModel viewModel) => CancelPracticeSheet( + onClose: viewModel.pop, + onContinue: viewModel.pop, + user: viewModel.user?.firstName ?? '', + onCancel: () async => await _cancel(viewModel), + ); + + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( + backgroundColor: kcBackgroundColor, + body: _buildBodyState(viewModel), + ); + + Widget _buildBodyState(CoursePracticeViewModel viewModel) => + viewModel.busy(StateObjects.coursePractice) + ? const PageLoadingIndicator() + : viewModel.practices.isEmpty || viewModel.questions.isEmpty + ? _buildPageLoadingIndicator(viewModel) + : _buildBody(viewModel); + + Widget _buildPageLoadingIndicator(CoursePracticeViewModel viewModel) => + PracticeLoadingScreen( + isLoading: viewModel.busy(StateObjects.coursePractice), + onTap: () async => + await viewModel.getCoursePractices(id: id, practice: practice), + onPop: viewModel.practices.isEmpty || viewModel.questions.isEmpty + ? viewModel.pop + : null, + isEmpty: viewModel.practices.isEmpty || viewModel.questions.isEmpty, + ); + + Widget _buildBody(CoursePracticeViewModel viewModel) => IndexedStack( + index: viewModel.currentPage, children: _buildBodyChildren(viewModel)); + + List _buildBodyChildren(CoursePracticeViewModel viewModel) => [ + if (practice != CoursePractices.lesson) + _buildDuolingoAssessmentsScreen(), + _buildQuestionSetView(viewModel) + ]; + + Widget _buildQuestionSetView(CoursePracticeViewModel viewModel) => + PageView.builder( + itemCount: viewModel.questions.length, + controller: viewModel.practiceController, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (cotext, index) => _buildQuestionView( + index: index + 1, + viewModel: viewModel, + question: viewModel.questions[index]), + ); + + Widget _buildQuestionView( + {required int index, + required CourseQuestion question, + required CoursePracticeViewModel viewModel}) => + PageView( + controller: viewModel.questionController, + physics: const NeverScrollableScrollPhysics(), + children: _buildScreens( + index: index, viewModel: viewModel, question: question), + ); + + List _buildScreens( + {required int index, + required CourseQuestion question, + required CoursePracticeViewModel viewModel}) => + [ + _buildDuolingoIntroScreen(viewModel), + _buildDuolingoQuestionScreen(viewModel), + _buildDuolingoAnswerScreen(viewModel), + _buildDuolingoReviewScreen(viewModel), + _buildDuolingoRetakeScreen(viewModel), + _buildDuolingoFinishScreen(viewModel), + ]; + + Widget _buildDuolingoAssessmentsScreen() => const DuolingoPracticesScreens(); + + Widget _buildDuolingoIntroScreen(CoursePracticeViewModel viewModel) => + DuolingoIntroScreen( + type: viewModel.selectedQuestionParam['type'], + title: viewModel.selectedQuestionParam['intro_title'], + subtitle: viewModel.selectedQuestionParam['intro_subtitle']); + + Widget _buildDuolingoQuestionScreen(CoursePracticeViewModel viewModel) => + _buildQuestionScreen(viewModel); + + Widget _buildDuolingoAnswerScreen(CoursePracticeViewModel viewModel) => + _buildAnswerScreen(viewModel); + + Widget _buildDuolingoReviewScreen(CoursePracticeViewModel viewModel) => + _buildReviewScreen(viewModel); + + Widget _buildDuolingoRetakeScreen(CoursePracticeViewModel viewModel) => + DuolingoRetakeScreen( + title: viewModel.selectedQuestionParam['outro_title'], + subtitle: viewModel.selectedQuestionParam['outro_subtitle']); + + Widget _buildDuolingoFinishScreen(CoursePracticeViewModel viewModel) => + DuolingoFinishScreen( + title: viewModel.selectedQuestionParam['outro_title'], + subtitle: viewModel.selectedQuestionParam['outro_subtitle']); +} diff --git a/lib/ui/views/course_practice/course_practice_view.form.dart b/lib/ui/views/course_practice/course_practice_view.form.dart new file mode 100644 index 0000000..24ffe05 --- /dev/null +++ b/lib/ui/views/course_practice/course_practice_view.form.dart @@ -0,0 +1,180 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// dart format width=80 + +// ************************************************************************** +// StackedFormGenerator +// ************************************************************************** + +// ignore_for_file: public_member_api_docs, constant_identifier_names, non_constant_identifier_names,unnecessary_this + +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; +import 'package:yimaru_app/ui/common/validators/form_validator.dart'; + +const bool _autoTextFieldValidation = true; + +const String PracticeValueKey = 'practice'; + +final Map + _CoursePracticeViewTextEditingControllers = {}; + +final Map _CoursePracticeViewFocusNodes = {}; + +final Map + _CoursePracticeViewTextValidations = { + PracticeValueKey: FormValidator.validateForm, +}; + +mixin $CoursePracticeView { + TextEditingController get practiceController => + _getFormTextEditingController(PracticeValueKey); + + FocusNode get practiceFocusNode => _getFormFocusNode(PracticeValueKey); + + TextEditingController _getFormTextEditingController( + String key, { + String? initialValue, + }) { + if (_CoursePracticeViewTextEditingControllers.containsKey(key)) { + return _CoursePracticeViewTextEditingControllers[key]!; + } + + _CoursePracticeViewTextEditingControllers[key] = + TextEditingController(text: initialValue); + return _CoursePracticeViewTextEditingControllers[key]!; + } + + FocusNode _getFormFocusNode(String key) { + if (_CoursePracticeViewFocusNodes.containsKey(key)) { + return _CoursePracticeViewFocusNodes[key]!; + } + _CoursePracticeViewFocusNodes[key] = FocusNode(); + return _CoursePracticeViewFocusNodes[key]!; + } + + /// Registers a listener on every generated controller that calls [model.setData()] + /// with the latest textController values + void syncFormWithViewModel(FormStateHelper model) { + practiceController.addListener(() => _updateFormData(model)); + + _updateFormData(model, forceValidate: _autoTextFieldValidation); + } + + /// Registers a listener on every generated controller that calls [model.setData()] + /// with the latest textController values + @Deprecated( + 'Use syncFormWithViewModel instead.' + 'This feature was deprecated after 3.1.0.', + ) + void listenToFormUpdated(FormViewModel model) { + practiceController.addListener(() => _updateFormData(model)); + + _updateFormData(model, forceValidate: _autoTextFieldValidation); + } + + /// Updates the formData on the FormViewModel + void _updateFormData(FormStateHelper model, {bool forceValidate = false}) { + model.setData( + model.formValueMap + ..addAll({ + PracticeValueKey: practiceController.text, + }), + ); + + if (_autoTextFieldValidation || forceValidate) { + updateValidationData(model); + } + } + + bool validateFormFields(FormViewModel model) { + _updateFormData(model, forceValidate: true); + return model.isFormValid; + } + + /// Calls dispose on all the generated controllers and focus nodes + void disposeForm() { + // The dispose function for a TextEditingController sets all listeners to null + + for (var controller in _CoursePracticeViewTextEditingControllers.values) { + controller.dispose(); + } + for (var focusNode in _CoursePracticeViewFocusNodes.values) { + focusNode.dispose(); + } + + _CoursePracticeViewTextEditingControllers.clear(); + _CoursePracticeViewFocusNodes.clear(); + } +} + +extension ValueProperties on FormStateHelper { + bool get hasAnyValidationMessage => this + .fieldsValidationMessages + .values + .any((validation) => validation != null); + + bool get isFormValid { + if (!_autoTextFieldValidation) this.validateForm(); + + return !hasAnyValidationMessage; + } + + String? get practiceValue => this.formValueMap[PracticeValueKey] as String?; + + set practiceValue(String? value) { + this.setData( + this.formValueMap..addAll({PracticeValueKey: value}), + ); + + if (_CoursePracticeViewTextEditingControllers.containsKey( + PracticeValueKey)) { + _CoursePracticeViewTextEditingControllers[PracticeValueKey]?.text = + value ?? ''; + } + } + + bool get hasPractice => + this.formValueMap.containsKey(PracticeValueKey) && + (practiceValue?.isNotEmpty ?? false); + + bool get hasPracticeValidationMessage => + this.fieldsValidationMessages[PracticeValueKey]?.isNotEmpty ?? false; + + String? get practiceValidationMessage => + this.fieldsValidationMessages[PracticeValueKey]; +} + +extension Methods on FormStateHelper { + void setPracticeValidationMessage(String? validationMessage) => + this.fieldsValidationMessages[PracticeValueKey] = validationMessage; + + /// Clears text input fields on the Form + void clearForm() { + practiceValue = ''; + } + + /// Validates text input fields on the Form + void validateForm() { + this.setValidationMessages({ + PracticeValueKey: getValidationMessage(PracticeValueKey), + }); + } +} + +/// Returns the validation message for the given key +String? getValidationMessage(String key) { + final validatorForKey = _CoursePracticeViewTextValidations[key]; + if (validatorForKey == null) return null; + + String? validationMessageForKey = validatorForKey( + _CoursePracticeViewTextEditingControllers[key]?.text, + ); + + return validationMessageForKey; +} + +/// Updates the fieldsValidationMessages on the FormViewModel +void updateValidationData(FormStateHelper model) => + model.setValidationMessages({ + PracticeValueKey: getValidationMessage(PracticeValueKey), + }); diff --git a/lib/ui/views/course_practice/course_practice_viewmodel.dart b/lib/ui/views/course_practice/course_practice_viewmodel.dart new file mode 100644 index 0000000..0ca67ab --- /dev/null +++ b/lib/ui/views/course_practice/course_practice_viewmodel.dart @@ -0,0 +1,536 @@ +import 'package:audioplayers/audioplayers.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:stacked/stacked.dart'; +import 'package:stacked_services/stacked_services.dart'; +import 'package:waveform_recorder/waveform_recorder.dart'; +import 'package:yimaru_app/models/course_practice.dart'; + +import '../../../app/app.locator.dart'; +import '../../../models/course_question.dart'; +import '../../../models/user.dart'; +import '../../../services/api_service.dart'; +import '../../../services/audio_player_service.dart'; +import '../../../services/authentication_service.dart'; +import '../../../services/course_service.dart'; +import '../../../services/status_checker_service.dart'; +import '../../../services/voice_recorder_service.dart'; +import '../../common/app_colors.dart'; +import '../../common/enmus.dart'; +import '../../common/helper_functions.dart'; + +class CoursePracticeViewModel extends ReactiveViewModel + with FormStateHelper + implements FormViewModel { + // Dependency injection + + final _apiService = locator(); + + final _dialogService = locator(); + + final _courseService = locator(); + + final _statusChecker = locator(); + + final _navigationService = locator(); + + final _audioPlayerService = locator(); + + final _voiceRecorderService = locator(); + + final _authenticationService = locator(); + + CoursePracticeViewModel() { + _listenToAudio(); + } + + @override + List get listenableServices => [ + _courseService, + _audioPlayerService, + _voiceRecorderService, + _authenticationService + ]; + + // User + User? get _user => _authenticationService.user; + + User? get user => _user; + + // AudioPlayer + AudioPlayer get _player => _audioPlayerService.player; + + AudioPlayer get player => _player; + + Duration _duration = Duration.zero; + + Duration _position = Duration.zero; + + Duration get position => _position; + + Duration get duration => _duration; + + double get progress { + if (_duration.inMilliseconds == 0) return 0; + return _position.inMilliseconds / _duration.inMilliseconds; + } + + // Voice recorder + String? _recordedAudio; + + String? get recordedAudio => _recordedAudio; + + WaveformRecorderController get _waveController => + _voiceRecorderService.waveController; + + WaveformRecorderController get waveController => _waveController; + + // Voice recorder state + VoiceRecordingState get _recordingState => + _voiceRecorderService.recordingState; + + VoiceRecordingState get recordingState => _recordingState; + + // Busy object + String? _busyObject; + + String? get busyObject => _busyObject; + + Voice? _playing; + + Voice? get playing => _playing; + + // Speaking state + bool _isSpeaking = false; + + bool get isSpeaking => _isSpeaking; + + // Course practices + bool _focusPractice = false; + + bool get focusPractice => _focusPractice; + + List _practices = []; + + List get practices => _practices; + + // Practice questions + String? _refreshedUrl; + + String? get refreshedUrl => _refreshedUrl; + + int _currentQuestion = 0; + + int get currentQuestion => _currentQuestion; + + List _questions = []; + + List get questions => _questions; + + final List> _questionParams = [ + { + 'label': 'Speaking 01', + 'type': DuolingoAssessments.speaking, + 'intro_title': 'Speak About the Photo', + 'outro_title': 'Speaking Practice Completed', + 'outro_subtitle': 'You’ve finished this speaking session. Great work!', + 'intro_subtitle': + 'Prepare to speak for at least 30 seconds about the photo you are shown' + }, + { + 'label': 'Speaking 02', + 'intro_title': 'Read, Then Speak', + 'type': DuolingoAssessments.speaking, + 'outro_title': 'Speaking Practice Completed', + 'intro_subtitle': 'You will speak about the given topic', + 'outro_subtitle': 'You’ve finished this speaking session. Great work!', + }, + { + 'label': 'Speaking 03', + 'intro_title': 'Speaking Sample', + 'type': DuolingoAssessments.speaking, + 'outro_title': 'Speaking Practice Completed', + 'intro_subtitle': 'You’ll speak for 1–3 minutes about a given topic.', + 'outro_subtitle': 'You’ve finished this speaking session. Great work!', + }, + { + 'label': 'Speaking 04', + 'type': DuolingoAssessments.speaking, + 'intro_title': 'Interactive Speaking', + 'outro_title': 'Speaking Practice Completed', + 'intro_subtitle': ' You’ll answer a series of short questions.', + 'outro_subtitle': 'You’ve finished this speaking session. Great work!', + }, + { + 'label': 'Writing 05', + 'type': DuolingoAssessments.writing, + 'intro_title': 'Write About the Photo', + 'outro_title': 'Writing Practice Completed', + 'outro_subtitle': 'You’ve finished this writing session. Great work!', + 'intro_subtitle': + 'You will see a picture and write a short description based on what you observe. Focus on clear, simple sentences.' + }, + { + 'label': 'Writing 06', + 'intro_title': 'Writing Sample', + 'type': DuolingoAssessments.writing, + 'outro_title': 'Writing Practice Completed', + 'outro_subtitle': 'You’ve finished this writing session. Great work!', + 'intro_subtitle': + 'You will write a longer response based on a given question. Your writing will be shared with institutions as part of your score.' + }, + { + 'label': 'Writing 07', + 'type': DuolingoAssessments.writing, + 'outro_title': 'Writing Practice Completed', + 'intro_title': 'Interactive Writing Part 1', + 'outro_subtitle': 'You’ve finished this writing session. Great work!', + 'intro_subtitle': + ' You will write short and simple sentences.
 Focus on basic ideas and clear meaning.
 Write naturally and manage your time.' + }, + { + 'label': 'Writing 08', + 'type': DuolingoAssessments.writing, + 'intro_title': 'Interactive Writing Part 2', + 'outro_title': 'Writing Practice Completed', + 'outro_subtitle': 'You’ve finished this writing session. Great work!', + 'intro_subtitle': + ' You will continue writing on a related idea.
 Add more details using clear sentences.
 Stay focused and complete your response within the time.' + }, + { + 'label': 'Listening 09', + 'intro_title': 'Listen and Type', + 'type': DuolingoAssessments.listening, + 'outro_title': 'Listening Practice Completed', + 'intro_subtitle': + 'You will hear a short audio clip. Type exactly what you hear.', + 'outro_subtitle': 'You’ve finished this Listening session. Great work!', + }, + { + 'label': 'Listening 10', + 'type': DuolingoAssessments.listening, + 'outro_title': 'Listening Practice Completed', + 'intro_title': 'Interactive Listening - Part 1', + 'intro_subtitle': ' Listen carefully and complete the missing words.', + 'outro_subtitle': 'You’ve finished this Listening session. Great work!', + }, + { + 'label': 'Listening 11', + 'type': DuolingoAssessments.listening, + 'outro_title': 'Listening Practice Completed', + 'intro_title': 'Interactive Listening - Part 2', + 'intro_subtitle': 'Listen and choose the correct option.', + 'outro_subtitle': 'You’ve finished this Listening session. Great work!', + }, + { + 'label': 'Assessment 12', + 'type': DuolingoAssessments.listening, + 'title': 'Interactive Listening - Part 3', + 'outro_title': 'Listening Practice Completed', + 'subtitle': 'Write a summary of the conversation you just had', + 'outro_subtitle': 'You’ve finished this Listening session. Great work!', + }, + { + 'label': 'Reading 13', + 'intro_title': 'Read and Select', + 'type': DuolingoAssessments.reading, + 'intro_subtitle': + 'Read the sentence and select the option that correctly completes the meaning.' + }, + { + 'label': 'Reading 14', + 'intro_title': 'Fill in the blank', + 'type': DuolingoAssessments.reading, + 'intro_subtitle': 'Complete the sentences by filling in the missing words' + }, + ]; + + List> get questionParams => _questionParams; + + // Selected question param + Map _selectedQuestionParam = { + 'label': 'Speaking 01', + 'intro_title': 'Speak About the Photo', + 'type': DuolingoAssessments.speaking, + 'outro_title': 'Speaking Practice Completed', + 'outro_subtitle': 'You’ve finished this speaking session. Great work!', + 'intro_subtitle': + 'Prepare to speak for at least 30 seconds about the photo you are shown', + }; + + Map get selectedQuestionParam => _selectedQuestionParam; + + // Practice answers + final List> _answers = []; + + List> get answers => _answers; + + // Next button state + bool _buttonActive = false; + + bool get buttonActive => _buttonActive; + + // In-app navigation + int _currentPage = 0; + + int get currentPage => _currentPage; + + final PageController _practiceController = PageController(); + + PageController get practiceController => _practiceController; + + final PageController _questionController = PageController(); + + PageController get questionController => _questionController; + + // Practice + void setPracticeFocus() { + _focusPractice = true; + rebuildUi(); + } + + // Speaking state + void setSpeakingState() { + _isSpeaking = !_isSpeaking; + rebuildUi(); + } + + // Next button + void setNextButton() { + _buttonActive = true; + rebuildUi(); + } + + // Question param + Future setQuestionParam(String type) async { + print('FIRST QUESTION: $type'); + if (type == '636') { + // await refreshQuestionUrl(_questions[_currentQuestion]); + _selectedQuestionParam = _questionParams.elementAt(0); + } else if (type == '') { + _selectedQuestionParam = _questionParams.elementAt(0); + } else if (type == '') { + _selectedQuestionParam = _questionParams.elementAt(0); + } else if (type == '') { + _selectedQuestionParam = _questionParams.elementAt(0); + } else if (type == '') { + _selectedQuestionParam = _questionParams.elementAt(0); + } else if (type == '') { + _selectedQuestionParam = _questionParams.elementAt(0); + } else if (type == '') { + _selectedQuestionParam = _questionParams.elementAt(0); + } else if (type == '') { + _selectedQuestionParam = _questionParams.elementAt(0); + } else if (type == '') { + _selectedQuestionParam = _questionParams.elementAt(0); + } else if (type == '') { + _selectedQuestionParam = _questionParams.elementAt(0); + } else if (type == '') { + _selectedQuestionParam = _questionParams.elementAt(0); + } else if (type == '') { + _selectedQuestionParam = _questionParams.elementAt(0); + } else if (type == '') { + _selectedQuestionParam = _questionParams.elementAt(0); + } else { + _selectedQuestionParam = _questionParams.elementAt(0); + } + } + + // Voice recorder + Future stopRecording() async { + if (_voiceRecorderService.waveController.isRecording) { + await _voiceRecorderService.stopRecording(); + _recordedAudio = await _voiceRecorderService.getRecordedAudio(); + } + } + + Future startRecording() async => await runBusyFuture(_startRecording(), + busyObject: StateObjects.recordCoursePracticeAnswer); + + Future _startRecording() async => + await _voiceRecorderService.startRecording(); + + // Play practice audio + void _listenToAudio() { + _audioPlayerService.durationStream.listen((dur) { + if (dur.inMilliseconds > 0) { + _duration = dur; + rebuildUi(); + } + }); + + _audioPlayerService.positionStream.listen((pos) { + _position = pos; + rebuildUi(); + }); + } + + Future playVoicePrompt(CourseQuestion question) async => + await runBusyFuture(_playVoicePrompt(question), + busyObject: StateObjects.coursePracticeQuestion); + + Future _playVoicePrompt(CourseQuestion question) async { + _questionController.jumpToPage(1); + await _audioPlayerService + .playUrl(question.dynamicPayload?.stimulus?.first.value ?? ''); + } + + Future replayVoicePrompt(CourseQuestion question) async { + await _audioPlayerService + .playUrl(question.dynamicPayload?.stimulus?.first.value ?? ''); + } + + Future playResult(Voice voice) async { + setBusyObject(voice); + await playAudio(voice); + } + + Future playAudio(Voice voice) async => + await runBusyFuture(_playAudio(voice), + busyObject: StateObjects.coursePracticeReview); + + Future _playAudio(Voice voice) async { + if (voice == Voice.recorded) { + print('RECORDED: ${_recordedAudio ?? ''}'); + await _audioPlayerService.playLocal(_recordedAudio ?? ''); + } else { + String url = await getRefreshedUrl(_questions[currentQuestion] + .dynamicPayload + ?.response + ?.last + .value ?? + '') ?? + ''; + print('REFRESHED: ${_questions[currentQuestion] + .dynamicPayload + ?.response + ?.last + .value ?? + ''}'); + await _audioPlayerService.playUrl(url); + } + } + + Future pauseAudio() async { + await _audioPlayerService.pause(); + } + + // Set busy object + void setBusyObject(Voice playing) { + _playing = playing; + rebuildUi(); + } + + // Dialogue + Future showAbortDialog() async { + DialogResponse? response = await _dialogService.showDialog( + cancelTitle: 'No', + title: 'Recording', + buttonTitle: 'Yes', + barrierDismissible: true, + cancelTitleColor: kcDarkGrey, + buttonTitleColor: kcPrimaryColor, + description: 'Are you sure you want to stop recording?', + ); + return response?.confirmed; + } + + // In-app navigation + void nextScreen() { + _questionController.nextPage( + curve: Curves.easeInOutCubic, + duration: const Duration(milliseconds: 350), + ); + } + + void nextPage() { + _currentPage++; + rebuildUi(); + } + + void goTo(int page) { + _currentPage = page; + rebuildUi(); + } + + void goBack() { + if (_currentPage == 0) { + pop(); + } else { + _currentPage--; + rebuildUi(); + } + } + + Future nextQuestion( + {required int index, required CourseQuestion question}) async => + await runBusyFuture(_nextQuestion(index: index, question: question), + busyObject: StateObjects.finishCoursePracticeQuestion); + + Future _nextQuestion( + {required int index, required CourseQuestion question}) async { + await stopRecording(); + _answers.add({ + 'busy_object': question.id.toString(), + 'recorded_voice_answer': await _voiceRecorderService.getRecordedAudio(), + 'sample_text_answer': + question.dynamicPayload?.response?.first.value ?? '', + 'sample_voice_answer': + question.dynamicPayload?.stimulus?.first.value ?? '', + }); + if (index != _questions.length) { + _practiceController.nextPage( + curve: Curves.easeInOutCubic, + duration: const Duration(milliseconds: 350), + ); + await playVoicePrompt(_questions[index]); + } else { + goTo(3); + } + } + + // Navigation + void pop() => _navigationService.back(); + + // Remote api call + + // Learn practice + Future getCoursePractices( + {required int id, required CoursePractices practice}) async => + await runBusyFuture(_getCoursePractices(id: id, practice: practice), + busyObject: StateObjects.coursePractice); + + Future _getCoursePractices( + {required int id, required CoursePractices practice}) async { + if (await _statusChecker.checkConnection()) { + if (practice == CoursePractices.courseCatalog) { + _practices = await _apiService.getCoursePractices(id); + // await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0); + } else if (practice == CoursePractices.unit) { + _practices = await _apiService.getCoursePractices(id); + // await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0); + } else { + _practices = await _apiService.getCoursePractices(id); + await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0); + await setQuestionParam( + _questions[_currentQuestion].id.toString() ?? ''); + } + } + } + + Future _getLearnPracticeQuestions(int id) async { + _questions = await _apiService.getCourseQuestions(id); + } + + // Refresh url + Future getRefreshedUrl(String value) async { + final String? refreshedUrl = await _courseService.refreshObject(value); + + if (refreshedUrl != null) { + return refreshedUrl; + } else { + return null; + } + } + + String getReadableImage(String image)=> getReadableUrl(image) ?? ''; +} diff --git a/lib/ui/views/course_practice/screens/duolingo_finish_screen.dart b/lib/ui/views/course_practice/screens/duolingo_finish_screen.dart new file mode 100644 index 0000000..8537482 --- /dev/null +++ b/lib/ui/views/course_practice/screens/duolingo_finish_screen.dart @@ -0,0 +1,144 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stacked/stacked.dart'; + +import '../../../common/app_colors.dart'; +import '../../../common/ui_helpers.dart'; +import '../../../widgets/cancel_practice_sheet.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../../../widgets/custom_elevated_button.dart'; +import '../course_practice_viewmodel.dart'; + +class DuolingoFinishScreen extends ViewModelWidget { + final String title; + final String subtitle; + + const DuolingoFinishScreen( + {super.key, required this.title, required this.subtitle}); + + Future _cancel(CoursePracticeViewModel viewModel) async { + await viewModel.stopRecording(); + viewModel.pop(); + viewModel.pop(); + } + + Future _showSheet( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) async => + await showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: kcTransparent, + builder: (cxt) => _buildSheet(context: context, viewModel: viewModel), + ); + + @override + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => + _buildScaffoldWrapper(context: context, viewModel: viewModel); + + Widget _buildScaffoldWrapper({required BuildContext context, + required CoursePracticeViewModel viewModel}) => Scaffold( + backgroundColor: kcBackgroundColor, + body: _buildSafeWrapper(context: context, viewModel: viewModel), + ); + + Widget _buildSafeWrapper({required BuildContext context, + required CoursePracticeViewModel viewModel}) => + SafeArea(child: _buildScaffold(context: context, viewModel: viewModel)); + + Widget _buildScaffold({required BuildContext context, + required CoursePracticeViewModel viewModel}) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: _buildScaffoldChildren(context: context, viewModel: viewModel), + ); + + List _buildScaffoldChildren({required BuildContext context, + required CoursePracticeViewModel viewModel}) => [ + _buildAppBar(context: context, viewModel: viewModel), + verticalSpaceMedium, + _buildExpandedBody(viewModel) + ]; + + Widget _buildExpandedBody(CoursePracticeViewModel viewModel) => + Expanded(child: _buildBodyWrapper(viewModel)); + + Widget _buildBodyWrapper(CoursePracticeViewModel viewModel) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _buildBody(viewModel), + ); + + Widget _buildBody(CoursePracticeViewModel viewModel) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: _buildBodyChildren(viewModel), + ); + + List _buildBodyChildren(CoursePracticeViewModel viewModel) => + [_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)]; + + Widget _buildUpperColumn(CoursePracticeViewModel viewModel) => Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: _buildUpperColumnChildren(viewModel), + ); + + List _buildUpperColumnChildren(CoursePracticeViewModel viewModel) => [ + verticalSpaceMassive, + _buildIcon(), + verticalSpaceMedium, + _buildTitle(), + verticalSpaceSmall, + _buildSubtitle(), + ]; + + Widget _buildAppBar({required BuildContext context, + required CoursePracticeViewModel viewModel}) => + DuolingoPracticeAppBar( + onClose: () async => + await _showSheet(context: context, viewModel: viewModel), + ); + + Widget _buildSheet( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + CancelPracticeSheet( + onClose: viewModel.pop, + onContinue: viewModel.pop, + user: viewModel.user?.firstName ?? '', + onCancel: () async => await _cancel(viewModel), + ); + + Widget _buildIcon() => SvgPicture.asset( + 'assets/icons/complete.svg', + ); + + Widget _buildTitle() => Text( + title, + style: style25DG600, + textAlign: TextAlign.center, + ); + + Widget _buildSubtitle() => Text( + subtitle, + style: style14MG400, + textAlign: TextAlign.center, + ); + + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( + padding: const EdgeInsets.only(bottom: 50), + child: _buildContinueButton(viewModel), + ); + + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => + CustomElevatedButton( + height: 55, + text: 'Continue', + borderRadius: 12, + onTap: viewModel.pop, + backgroundColor: kcWhite, + borderColor: kcPrimaryColor, + foregroundColor: kcPrimaryColor, + ); +} diff --git a/lib/ui/views/course_practice/screens/duolingo_intro_screen.dart b/lib/ui/views/course_practice/screens/duolingo_intro_screen.dart new file mode 100644 index 0000000..60dfa32 --- /dev/null +++ b/lib/ui/views/course_practice/screens/duolingo_intro_screen.dart @@ -0,0 +1,158 @@ +import 'package:flutter/material.dart'; +import 'package:iconsax/iconsax.dart'; +import 'package:stacked/stacked.dart'; +import 'package:yimaru_app/ui/common/enmus.dart'; +import 'package:yimaru_app/ui/views/duolingo/duolingo_viewmodel.dart'; +import 'package:yimaru_app/ui/widgets/duolingo_practice_app_bar.dart'; + +import '../../../common/app_colors.dart'; +import '../../../common/ui_helpers.dart'; +import '../../../widgets/cancel_practice_sheet.dart'; +import '../../../widgets/custom_elevated_button.dart'; +import '../../../widgets/wave_wrapper.dart'; +import '../course_practice_viewmodel.dart'; + +class DuolingoIntroScreen extends ViewModelWidget { + final String title; + final String subtitle; + final DuolingoAssessments type; + + const DuolingoIntroScreen( + {super.key, + required this.type, + required this.title, + required this.subtitle}); + + IconData _getIcon() { + if (type == DuolingoAssessments.speaking) { + return Icons.waves; + } else if (type == DuolingoAssessments.writing) { + return Iconsax.pen_add; + } else if (type == DuolingoAssessments.listening) { + return Icons.hearing; + } else { + return Icons.book; + } + } + + Future _cancel(CoursePracticeViewModel viewModel) async { + await viewModel.stopRecording(); + viewModel.pop(); + viewModel.pop(); + } + + Future _showSheet( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) async => + await showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: kcTransparent, + builder: (cxt) => _buildSheet(viewModel), + ); + + @override + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => + _buildScaffoldWrapper(context:context, viewModel:viewModel); + + Widget _buildScaffoldWrapper( {required BuildContext context, + required CoursePracticeViewModel viewModel}) => Scaffold( + backgroundColor: kcBackgroundColor, + body: _buildScaffold(context:context, viewModel:viewModel), + ); + + Widget _buildScaffold( {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + SafeArea(child: _buildBodyColumnWrapper(context:context, viewModel:viewModel)); + + Widget _buildBodyColumnWrapper( {required BuildContext context, + required CoursePracticeViewModel viewModel}) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _buildBodyColumn(context:context, viewModel:viewModel), + ); + + Widget _buildBodyColumn( {required BuildContext context, + required CoursePracticeViewModel viewModel}) => Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: _buildBodyColumnChildren(context:context, viewModel:viewModel), + ); + + List _buildBodyColumnChildren( {required BuildContext context, + required CoursePracticeViewModel viewModel}) => [ + _buildAppBarWrapper(context:context, viewModel:viewModel), + _buildSpeakingIndicatorWrapper(viewModel), + _buildContinueButtonWrapper(viewModel) + ]; + + Widget _buildAppBarWrapper( {required BuildContext context, + required CoursePracticeViewModel viewModel}) => Column( + children: [ + verticalSpaceMedium, + _buildAppBar(context:context, viewModel:viewModel), + verticalSpaceMedium, + ], + ); + + Widget _buildAppBar( {required BuildContext context, +required CoursePracticeViewModel viewModel}) => DuolingoPracticeAppBar( + title: 'Speaking practice', + onClose: () async => + await _showSheet(context: context, viewModel: viewModel), + ); + + Widget _buildSheet(CoursePracticeViewModel viewModel) => CancelPracticeSheet( + onClose: viewModel.pop, + onContinue: viewModel.pop, + user: viewModel.user?.firstName ?? '', + onCancel: () async => await _cancel(viewModel), + ); + + Widget _buildSpeakingIndicatorWrapper(CoursePracticeViewModel viewModel) => Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: _buildSpeakingIndicatorChildren(), + ); + + List _buildSpeakingIndicatorChildren() => [ + _buildSpeakingIconWrapper(), + verticalSpaceMedium, + _buildTitle(), + _buildSubtitle(), + ]; + + Widget _buildTitle() => Text( + title, + style: style18DG700, + textAlign: TextAlign.center, + ); + + Widget _buildSubtitle() => Text( + subtitle, + style: style14DG400, + textAlign: TextAlign.center, + ); + + Widget _buildSpeakingIconWrapper() => + WaveWrapper(height: 125, child: _buildSpeakingIcon()); + + Widget _buildSpeakingIcon() => Icon( + _getIcon(), + size: 30, + color: kcPrimaryColor, + ); + + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => Padding( + padding: const EdgeInsets.only(bottom: 50), + child: _buildContinueButton(viewModel), + ); + + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => + CustomElevatedButton( + height: 55, + borderRadius: 12, + foregroundColor: kcWhite, + text: 'Start Preparation', + onTap: viewModel.nextScreen, + backgroundColor: kcPrimaryColor, + ); +} diff --git a/lib/ui/views/duolingo/screens/duolingo_listening_assessment_1_question.dart b/lib/ui/views/course_practice/screens/duolingo_listening_practice_1_question.dart similarity index 50% rename from lib/ui/views/duolingo/screens/duolingo_listening_assessment_1_question.dart rename to lib/ui/views/course_practice/screens/duolingo_listening_practice_1_question.dart index f8c78eb..54daad6 100644 --- a/lib/ui/views/duolingo/screens/duolingo_listening_assessment_1_question.dart +++ b/lib/ui/views/course_practice/screens/duolingo_listening_practice_1_question.dart @@ -1,82 +1,84 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; +import 'package:yimaru_app/ui/views/course_practice/course_practice_view.form.dart'; import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart'; -import 'package:yimaru_app/ui/widgets/listenable_assessment_card.dart'; +import 'package:yimaru_app/ui/widgets/listenable_practice_card.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; import '../../../widgets/custom_elevated_button.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../duolingo_viewmodel.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoListeningAssessment1Question - extends ViewModelWidget { - final TextEditingController assessmentController; +class DuolingoListeningPractice1Question + extends ViewModelWidget { + final TextEditingController practiceController; - const DuolingoListeningAssessment1Question( - {super.key, required this.assessmentController}); + const DuolingoListeningPractice1Question( + {super.key, required this.practiceController}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumnWrapper(viewModel)); - Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildBodyColumn(viewModel), ); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarWrapper(viewModel), _buildQuestionWrapper(viewModel), _buildContinueButtonWrapper(viewModel) ]; - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - title: 'Listening Assessment', + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( + title: 'Listening practice', onClose: () => viewModel.goTo(0), ); - Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: _buildQuestionChildren(viewModel), ); - List _buildQuestionChildren(DuolingoViewModel viewModel) => [ + List _buildQuestionChildren(CoursePracticeViewModel viewModel) => [ _buildTitle(), verticalSpaceMedium, _buildQuestion(), verticalSpaceLarge, _buildLabel(), verticalSpaceSmall, - _buildAssessmentFormField(viewModel), - if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) + _buildPracticeFormField(viewModel), + if (viewModel.hasPracticeValidationMessage && + viewModel.focusPractice) verticalSpaceTiny, - if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) - _buildAssessmentWrapper(viewModel), + if (viewModel.hasPracticeValidationMessage && + viewModel.focusPractice) + _buildPracticeWrapper(viewModel), ]; Widget _buildTitle() => Text( @@ -85,7 +87,7 @@ class DuolingoListeningAssessment1Question textAlign: TextAlign.center, ); - Widget _buildQuestion() => const ListenableAssessmentCard(); + Widget _buildQuestion() => const ListenablePracticeCard(); Widget _buildLabel() => Text( 'Your Answer', @@ -93,34 +95,35 @@ class DuolingoListeningAssessment1Question textAlign: TextAlign.center, ); - Widget _buildAssessmentFormField(DuolingoViewModel viewModel) => + Widget _buildPracticeFormField(CoursePracticeViewModel viewModel) => TextFormField( maxLines: 5, maxLength: 250, - controller: assessmentController, - onTap: viewModel.setAssessmentFocus, + controller: practiceController, + onTap: viewModel.setPracticeFocus, decoration: inputDecoration( focus: true, hint: 'Start writing here...', - filled: assessmentController.text.isNotEmpty), + filled: practiceController.text.isNotEmpty), ); - Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) => - viewModel.hasAssessmentValidationMessage - ? _buildAssessmentValidator(viewModel) + Widget _buildPracticeWrapper(CoursePracticeViewModel viewModel) => + viewModel.hasPracticeValidationMessage + ? _buildPracticeValidator(viewModel) : Container(); - Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text( - viewModel.assessmentValidationMessage!, + Widget _buildPracticeValidator(CoursePracticeViewModel viewModel) => Text( + viewModel.practiceValidationMessage!, style: style12R700, ); - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( padding: const EdgeInsets.only(bottom: 50), child: _buildContinueButton(viewModel), ); - Widget _buildContinueButton(DuolingoViewModel viewModel) => + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => CustomElevatedButton( height: 55, text: 'Submit', diff --git a/lib/ui/views/duolingo/screens/duolingo_listening_assessment_1_review.dart b/lib/ui/views/course_practice/screens/duolingo_listening_practice_1_review.dart similarity index 59% rename from lib/ui/views/duolingo/screens/duolingo_listening_assessment_1_review.dart rename to lib/ui/views/course_practice/screens/duolingo_listening_practice_1_review.dart index 9b74283..75f24f6 100644 --- a/lib/ui/views/duolingo/screens/duolingo_listening_assessment_1_review.dart +++ b/lib/ui/views/course_practice/screens/duolingo_listening_practice_1_review.dart @@ -1,48 +1,48 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/widgets/duolingo_assessment_review_section.dart'; +import 'package:yimaru_app/ui/widgets/duolingo_practice_review_section.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../../../widgets/listenable_assessment_card.dart'; -import '../duolingo_viewmodel.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../../../widgets/listenable_practice_card.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoListeningAssessment1Review - extends ViewModelWidget { - final TextEditingController assessmentController; +class DuolingoListeningPractice1Review + extends ViewModelWidget { + final TextEditingController practiceController; - const DuolingoListeningAssessment1Review( - {super.key, required this.assessmentController}); + const DuolingoListeningPractice1Review( + {super.key, required this.practiceController}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumn(viewModel)); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarIndenter(viewModel), _buildExpandedBody(viewModel), ]; - Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding( + Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildAppBarWrapper(viewModel), ); - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), @@ -50,20 +50,22 @@ class DuolingoListeningAssessment1Review ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( title: 'Feedback', onClose: () => viewModel.goTo(0), ); - Widget _buildExpandedBody(DuolingoViewModel viewModel) => + Widget _buildExpandedBody(CoursePracticeViewModel viewModel) => Expanded(child: _buildBodyScroller(viewModel)); - Widget _buildBodyScroller(DuolingoViewModel viewModel) => + Widget _buildBodyScroller(CoursePracticeViewModel viewModel) => SingleChildScrollView( child: _buildQuestionSectionWrapper(viewModel), ); - Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) => + Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -71,7 +73,7 @@ class DuolingoListeningAssessment1Review ); List _buildQuestionQuestionSectionChildren( - DuolingoViewModel viewModel) => + CoursePracticeViewModel viewModel) => [ _buildTitleWrapper(), verticalSpaceMedium, @@ -100,7 +102,7 @@ class DuolingoListeningAssessment1Review child: _buildQuestion(), ); - Widget _buildQuestion() => const ListenableAssessmentCard(); + Widget _buildQuestion() => const ListenablePracticeCard(); Widget _buildLabelWrapper() => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), @@ -113,24 +115,24 @@ class DuolingoListeningAssessment1Review textAlign: TextAlign.center, ); - Widget _buildAssessmentFormFieldWrapper(DuolingoViewModel viewModel) => + Widget _buildAssessmentFormFieldWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildAssessmentFormField(viewModel), ); - Widget _buildAssessmentFormField(DuolingoViewModel viewModel) => + Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) => TextFormField( maxLines: 5, maxLength: 250, - controller: assessmentController, - onTap: viewModel.setAssessmentFocus, + controller: practiceController, + onTap: viewModel.setPracticeFocus, decoration: inputDecoration( focus: true, hint: 'Start writing here...', - filled: assessmentController.text.isNotEmpty), + filled: practiceController.text.isNotEmpty), ); - Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) => - DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5)); + Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) => + DuolingoPracticeReviewSection(onTap: () => viewModel.goTo(5)); } diff --git a/lib/ui/views/course_practice/screens/duolingo_listening_practice_2_question.dart b/lib/ui/views/course_practice/screens/duolingo_listening_practice_2_question.dart new file mode 100644 index 0000000..22c744a --- /dev/null +++ b/lib/ui/views/course_practice/screens/duolingo_listening_practice_2_question.dart @@ -0,0 +1,153 @@ +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; +import 'package:yimaru_app/ui/widgets/listenable_practice_card.dart'; + +import '../../../common/app_colors.dart'; +import '../../../common/ui_helpers.dart'; +import '../../../widgets/custom_elevated_button.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../course_practice_viewmodel.dart'; +import 'package:yimaru_app/ui/views/course_practice/course_practice_view.form.dart'; + + +class DuolingoListeningPractice2Question + extends ViewModelWidget { + final TextEditingController practiceController; + + const DuolingoListeningPractice2Question( + {super.key, required this.practiceController}); + + @override + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => + _buildScaffoldWrapper(viewModel); + + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( + backgroundColor: kcBackgroundColor, + body: _buildScaffold(viewModel), + ); + + Widget _buildScaffold(CoursePracticeViewModel viewModel) => + SafeArea(child: _buildBodyColumnWrapper(viewModel)); + + Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _buildBodyColumn(viewModel), + ); + + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: _buildBodyColumnChildren(viewModel), + ); + + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ + _buildAppBarWrapper(viewModel), + _buildQuestionWrapper(viewModel), + _buildContinueButtonWrapper(viewModel) + ]; + + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( + children: [ + verticalSpaceMedium, + _buildAppBar(viewModel), + ], + ); + + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( + title: 'Listening practice', + onClose: () => viewModel.goTo(0), + ); + + Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: _buildQuestionChildren(viewModel), + ); + + List _buildQuestionChildren(CoursePracticeViewModel viewModel) => [ + _buildTitle(), + verticalSpaceMedium, + _buildQuestion(), + verticalSpaceLarge, + _buildLabel('1. Focus of the conference:'), + verticalSpaceTiny, + _buildPracticeFormField(viewModel), + if (viewModel.hasPracticeValidationMessage && + viewModel.focusPractice) + verticalSpaceTiny, + if (viewModel.hasPracticeValidationMessage && + viewModel.focusPractice) + _buildPracticeWrapper(viewModel), + verticalSpaceSmall, + _buildLabel('2. Number of presentations:'), + verticalSpaceTiny, + _buildPracticeFormField(viewModel), + if (viewModel.hasPracticeValidationMessage && + viewModel.focusPractice) + verticalSpaceTiny, + if (viewModel.hasPracticeValidationMessage && + viewModel.focusPractice) + _buildPracticeWrapper(viewModel), + verticalSpaceSmall, + _buildLabel('3. Keynote speaker’s field:'), + verticalSpaceTiny, + _buildPracticeFormField(viewModel), + if (viewModel.hasPracticeValidationMessage && + viewModel.focusPractice) + verticalSpaceTiny, + if (viewModel.hasPracticeValidationMessage && + viewModel.focusPractice) + _buildPracticeWrapper(viewModel), + ]; + + Widget _buildTitle() => Text( + 'Listen to the audio message and type your response.', + style: style18DG700, + textAlign: TextAlign.center, + ); + + Widget _buildQuestion() => const ListenablePracticeCard(); + + Widget _buildLabel(String question) => Text( + question, + style: style14MG400, + textAlign: TextAlign.center, + ); + + Widget _buildPracticeFormField(CoursePracticeViewModel viewModel) => + TextFormField( + controller: practiceController, + onTap: viewModel.setPracticeFocus, + decoration: inputDecoration( + focus: true, + hint: 'Start writing here...', + filled: practiceController.text.isNotEmpty), + ); + + Widget _buildPracticeWrapper(CoursePracticeViewModel viewModel) => + viewModel.hasPracticeValidationMessage + ? _buildPracticeValidator(viewModel) + : Container(); + + Widget _buildPracticeValidator(CoursePracticeViewModel viewModel) => Text( + viewModel.practiceValidationMessage!, + style: style12R700, + ); + + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( + padding: const EdgeInsets.only(bottom: 50), + child: _buildContinueButton(viewModel), + ); + + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => + CustomElevatedButton( + height: 55, + text: 'Submit', + borderRadius: 12, + foregroundColor: kcWhite, + onTap: () => viewModel.goTo(4), + backgroundColor: kcPrimaryColor, + ); +} diff --git a/lib/ui/views/duolingo/screens/duolingo_listening_assessment_2_review.dart b/lib/ui/views/course_practice/screens/duolingo_listening_practice_2_review.dart similarity index 57% rename from lib/ui/views/duolingo/screens/duolingo_listening_assessment_2_review.dart rename to lib/ui/views/course_practice/screens/duolingo_listening_practice_2_review.dart index a2f3b51..16913cc 100644 --- a/lib/ui/views/duolingo/screens/duolingo_listening_assessment_2_review.dart +++ b/lib/ui/views/course_practice/screens/duolingo_listening_practice_2_review.dart @@ -1,48 +1,48 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/widgets/duolingo_assessment_review_section.dart'; +import 'package:yimaru_app/ui/widgets/duolingo_practice_review_section.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../../../widgets/listenable_assessment_card.dart'; -import '../duolingo_viewmodel.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../../../widgets/listenable_practice_card.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoListeningAssessment2Review - extends ViewModelWidget { - final TextEditingController assessmentController; +class DuolingoListeningPractice2Review + extends ViewModelWidget { + final TextEditingController practiceController; - const DuolingoListeningAssessment2Review( - {super.key, required this.assessmentController}); + const DuolingoListeningPractice2Review( + {super.key, required this.practiceController}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumn(viewModel)); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarIndenter(viewModel), _buildExpandedBody(viewModel), ]; - Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding( + Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildAppBarWrapper(viewModel), ); - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), @@ -50,20 +50,22 @@ class DuolingoListeningAssessment2Review ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( title: 'Feedback', onClose: () => viewModel.goTo(0), ); - Widget _buildExpandedBody(DuolingoViewModel viewModel) => + Widget _buildExpandedBody(CoursePracticeViewModel viewModel) => Expanded(child: _buildBodyScroller(viewModel)); - Widget _buildBodyScroller(DuolingoViewModel viewModel) => + Widget _buildBodyScroller(CoursePracticeViewModel viewModel) => SingleChildScrollView( child: _buildQuestionSectionWrapper(viewModel), ); - Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) => + Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -71,7 +73,7 @@ class DuolingoListeningAssessment2Review ); List _buildQuestionQuestionSectionChildren( - DuolingoViewModel viewModel) => + CoursePracticeViewModel viewModel) => [ _buildTitleWrapper(), verticalSpaceMedium, @@ -79,17 +81,17 @@ class DuolingoListeningAssessment2Review verticalSpaceLarge, _buildLabelWrapper('1. Focus of the conference:'), verticalSpaceTiny, - _buildAssessmentFormFieldWrapper(viewModel), + _buildPracticeFormFieldWrapper(viewModel), verticalSpaceSmall, _buildLabelWrapper('2. Number of presentations:'), verticalSpaceTiny, - _buildAssessmentFormFieldWrapper(viewModel), + _buildPracticeFormFieldWrapper(viewModel), verticalSpaceSmall, _buildLabelWrapper('3. Keynote speaker’s field:'), verticalSpaceTiny, - _buildAssessmentFormFieldWrapper(viewModel), + _buildPracticeFormFieldWrapper(viewModel), verticalSpaceLarge, - _buildAssessmentReviewSection(viewModel) + _buildPracticeReviewSection(viewModel) ]; Widget _buildTitleWrapper() => Padding( @@ -108,7 +110,7 @@ class DuolingoListeningAssessment2Review child: _buildQuestion(), ); - Widget _buildQuestion() => const ListenableAssessmentCard(); + Widget _buildQuestion() => const ListenablePracticeCard(); Widget _buildLabelWrapper(String question) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), @@ -121,22 +123,22 @@ class DuolingoListeningAssessment2Review textAlign: TextAlign.center, ); - Widget _buildAssessmentFormFieldWrapper(DuolingoViewModel viewModel) => + Widget _buildPracticeFormFieldWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildAssessmentFormField(viewModel), + child: _buildPracticeFormField(viewModel), ); - Widget _buildAssessmentFormField(DuolingoViewModel viewModel) => + Widget _buildPracticeFormField(CoursePracticeViewModel viewModel) => TextFormField( - controller: assessmentController, - onTap: viewModel.setAssessmentFocus, + controller: practiceController, + onTap: viewModel.setPracticeFocus, decoration: inputDecoration( focus: true, hint: 'Start writing here...', - filled: assessmentController.text.isNotEmpty), + filled: practiceController.text.isNotEmpty), ); - Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) => - DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5)); + Widget _buildPracticeReviewSection(CoursePracticeViewModel viewModel) => + DuolingoPracticeReviewSection(onTap: () => viewModel.goTo(5)); } diff --git a/lib/ui/views/duolingo/screens/duolingo_listening_assessment_3_question.dart b/lib/ui/views/course_practice/screens/duolingo_listening_practice_3_question.dart similarity index 51% rename from lib/ui/views/duolingo/screens/duolingo_listening_assessment_3_question.dart rename to lib/ui/views/course_practice/screens/duolingo_listening_practice_3_question.dart index 26d3010..a73a3e2 100644 --- a/lib/ui/views/duolingo/screens/duolingo_listening_assessment_3_question.dart +++ b/lib/ui/views/course_practice/screens/duolingo_listening_practice_3_question.dart @@ -1,85 +1,86 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import 'package:yimaru_app/ui/widgets/dwarf_tile.dart'; -import 'package:yimaru_app/ui/widgets/listenable_assessment_card.dart'; +import 'package:yimaru_app/ui/widgets/listenable_practice_card.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; import '../../../widgets/custom_elevated_button.dart'; import '../../../widgets/custom_small_radio_button.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../duolingo_viewmodel.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoListeningAssessment3Question - extends ViewModelWidget { - final TextEditingController assessmentController; +class DuolingoListeningPractice3Question + extends ViewModelWidget { + final TextEditingController practiceController; - const DuolingoListeningAssessment3Question( - {super.key, required this.assessmentController}); + const DuolingoListeningPractice3Question( + {super.key, required this.practiceController}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumn(viewModel)); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarWrapper(viewModel), _buildExpandedBody(viewModel), ]; - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, children: _buildAppBarChildren(viewModel), ); - List _buildAppBarChildren(DuolingoViewModel viewModel) => [ + List _buildAppBarChildren(CoursePracticeViewModel viewModel) => [ verticalSpaceMedium, _buildAppBarIndenter(viewModel), ]; - Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding( + Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildAppBar(viewModel), ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - title: 'Listening Assessment', + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( + title: 'Listening practice', onClose: () => viewModel.goTo(0), ); - Widget _buildExpandedBody(DuolingoViewModel viewModel) => + Widget _buildExpandedBody(CoursePracticeViewModel viewModel) => Expanded(child: _buildBodyScroller(viewModel)); - Widget _buildBodyScroller(DuolingoViewModel viewModel) => + Widget _buildBodyScroller(CoursePracticeViewModel viewModel) => SingleChildScrollView( child: _buildQuestionIndenter(viewModel), ); - Widget _buildQuestionIndenter(DuolingoViewModel viewModel) => Padding( + Widget _buildQuestionIndenter(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildQuestionWrapper(viewModel), ); - Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: _buildQuestionChildren(viewModel), ); - List _buildQuestionChildren(DuolingoViewModel viewModel) => [ + List _buildQuestionChildren(CoursePracticeViewModel viewModel) => [ verticalSpaceLarge, _buildTitle(), verticalSpaceMedium, @@ -87,7 +88,7 @@ class DuolingoListeningAssessment3Question verticalSpaceLarge, _buildLabel(), verticalSpaceTiny, - _buildAssessmentQuestions(viewModel), + // _buildPracticeQuestions(viewModel), verticalSpaceMedium, _buildContinueButtonWrapper(viewModel) ]; @@ -99,10 +100,10 @@ class DuolingoListeningAssessment3Question ); Widget _buildQuestion() => - DwarfTile(small: true, child: _buildListenableAssessmentCard()); + DwarfTile(small: true, child: _buildListenablePracticeCard()); - Widget _buildListenableAssessmentCard() => - const Expanded(child: ListenableAssessmentCard()); + Widget _buildListenablePracticeCard() => + const Expanded(child: ListenablePracticeCard()); Widget _buildLabel() => Text( 'Select the best response', @@ -110,21 +111,21 @@ class DuolingoListeningAssessment3Question textAlign: TextAlign.center, ); - Widget _buildAssessmentQuestions(DuolingoViewModel viewModel) => - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: viewModel.listeningAssessments.length, - itemBuilder: (context, index) => _buildAssessmentCard( - title: viewModel.listeningAssessments[index], - selected: viewModel.isSelectedListeningAssessment( - viewModel.listeningAssessments[index]), - onTap: () => viewModel.setSelectedListeningAssessment( - viewModel.listeningAssessments[index]), - ), - ); + // Widget _buildPracticeQuestions(CoursePracticeViewModel viewModel) => + // ListView.builder( + // shrinkWrap: true, + // physics: const NeverScrollableScrollPhysics(), + // itemCount: viewModel.questions.length, + // itemBuilder: (context, index) => _buildAssessmentCard( + // title: viewModel.listeningAssessments[index], + // selected: viewModel.isSelectedListeningAssessment( + // viewModel.listeningAssessments[index]), + // onTap: () => viewModel.setSelectedListeningAssessment( + // viewModel.listeningAssessments[index]), + // ), + // ); - Widget _buildAssessmentCard( + Widget _buildPracticeCard( {required String title, required bool selected, required GestureTapCallback onTap}) => @@ -134,12 +135,13 @@ class DuolingoListeningAssessment3Question selected: selected, ); - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( padding: const EdgeInsets.only(bottom: 50), child: _buildContinueButton(viewModel), ); - Widget _buildContinueButton(DuolingoViewModel viewModel) => + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => CustomElevatedButton( height: 55, text: 'Submit', diff --git a/lib/ui/views/duolingo/screens/duolingo_assessments_screens.dart b/lib/ui/views/course_practice/screens/duolingo_practices_screens.dart similarity index 58% rename from lib/ui/views/duolingo/screens/duolingo_assessments_screens.dart rename to lib/ui/views/course_practice/screens/duolingo_practices_screens.dart index 49b2a45..61f3906 100644 --- a/lib/ui/views/duolingo/screens/duolingo_assessments_screens.dart +++ b/lib/ui/views/course_practice/screens/duolingo_practices_screens.dart @@ -3,31 +3,32 @@ import 'package:stacked/stacked.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_card.dart'; +import '../../../widgets/duolingo_practice_card.dart'; import '../../../widgets/small_app_bar.dart'; -import '../duolingo_viewmodel.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoAssessmentsScreens extends ViewModelWidget { - const DuolingoAssessmentsScreens({super.key}); +class DuolingoPracticesScreens + extends ViewModelWidget { + const DuolingoPracticesScreens({super.key}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBody(viewModel)); - Widget _buildBody(DuolingoViewModel viewModel) => Padding( + Widget _buildBody(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildColumn(viewModel), ); - Widget _buildColumn(DuolingoViewModel viewModel) => Column( + Widget _buildColumn(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), @@ -36,25 +37,27 @@ class DuolingoAssessmentsScreens extends ViewModelWidget { ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => const SmallAppBar( + Widget _buildAppBar(CoursePracticeViewModel viewModel) => const SmallAppBar( showBackButton: false, ); - Widget _buildPracticeColumnWrapper(DuolingoViewModel viewModel) => + Widget _buildPracticeColumnWrapper(CoursePracticeViewModel viewModel) => Expanded(child: _buildPracticeColumnScrollView(viewModel)); - Widget _buildPracticeColumnScrollView(DuolingoViewModel viewModel) => + Widget _buildPracticeColumnScrollView(CoursePracticeViewModel viewModel) => SingleChildScrollView( child: _buildPracticeColumn(viewModel), ); - Widget _buildPracticeColumn(DuolingoViewModel viewModel) => Column( + Widget _buildPracticeColumn(CoursePracticeViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: _buildPracticeColumnChildren(viewModel), ); - List _buildPracticeColumnChildren(DuolingoViewModel viewModel) => [ + List _buildPracticeColumnChildren( + CoursePracticeViewModel viewModel) => + [ verticalSpaceMedium, _buildTitle(), _buildSubtitle(), @@ -72,14 +75,15 @@ class DuolingoAssessmentsScreens extends ViewModelWidget { style: style14DG400, ); - Widget _buildListView(DuolingoViewModel viewModel) => GridView.builder( + Widget _buildListView(CoursePracticeViewModel viewModel) => GridView.builder( shrinkWrap: true, - itemCount: viewModel.assessments.length, + itemCount: viewModel.practices.length, physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) => _buildCard( - title: viewModel.assessments[index]['label'], - onTap: () => viewModel.setSelectedAssessment( - page: 1, assessment: viewModel.assessments[index])), + title: viewModel.practices[index].title ?? '', onTap: () {} + // onTap: () => viewModel.setSelectedAssessment( + // page: 1, assessment: viewModel.assessments[index]) + ), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing: 15, @@ -92,7 +96,7 @@ class DuolingoAssessmentsScreens extends ViewModelWidget { required String title, required GestureTapCallback onTap, }) => - DuolingoAssessmentCard( + DuolingoPracticeCard( title: title, onTap: onTap, ); diff --git a/lib/ui/views/course_practice/screens/duolingo_retake_screen.dart b/lib/ui/views/course_practice/screens/duolingo_retake_screen.dart new file mode 100644 index 0000000..d4bdd81 --- /dev/null +++ b/lib/ui/views/course_practice/screens/duolingo_retake_screen.dart @@ -0,0 +1,166 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stacked/stacked.dart'; + +import '../../../common/app_colors.dart'; +import '../../../common/ui_helpers.dart'; +import '../../../widgets/cancel_practice_sheet.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../../../widgets/custom_elevated_button.dart'; +import '../course_practice_viewmodel.dart'; + +class DuolingoRetakeScreen extends ViewModelWidget { + final String title; + final String subtitle; + + const DuolingoRetakeScreen( + {super.key, required this.title, required this.subtitle}); + + Future _cancel(CoursePracticeViewModel viewModel) async { + await viewModel.stopRecording(); + viewModel.pop(); + viewModel.pop(); + } + + Future _showSheet( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) async => + await showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: kcTransparent, + builder: (cxt) => _buildSheet(context: context, viewModel: viewModel), + ); + + @override + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => + _buildScaffoldWrapper(context: context, viewModel: viewModel); + + Widget _buildScaffoldWrapper({required BuildContext context, + required CoursePracticeViewModel viewModel}) => Scaffold( + backgroundColor: kcBackgroundColor, + body: _buildSafeWrapper(context: context, viewModel: viewModel), + ); + + Widget _buildSafeWrapper({required BuildContext context, + required CoursePracticeViewModel viewModel}) => + SafeArea(child: _buildScaffold(context: context, viewModel: viewModel)); + + Widget _buildScaffold({required BuildContext context, + required CoursePracticeViewModel viewModel}) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: _buildScaffoldChildren(context: context, viewModel: viewModel), + ); + + List _buildScaffoldChildren({required BuildContext context, + required CoursePracticeViewModel viewModel}) => [ + _buildAppBar(context: context, viewModel: viewModel), + verticalSpaceMedium, + _buildExpandedBody(viewModel) + ]; + + Widget _buildExpandedBody( CoursePracticeViewModel viewModel) => + Expanded(child: _buildBodyWrapper(viewModel)); + + Widget _buildBodyWrapper( CoursePracticeViewModel viewModel) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _buildBody(viewModel), + ); + + Widget _buildBody( CoursePracticeViewModel viewModel) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: _buildBodyChildren(viewModel), + ); + + List _buildBodyChildren( + CoursePracticeViewModel viewModel) => + [_buildUpperColumn(viewModel), _buildLowerColumn(viewModel)]; + + Widget _buildUpperColumn( CoursePracticeViewModel viewModel) => Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: _buildUpperColumnChildren(viewModel), + ); + + List _buildUpperColumnChildren( CoursePracticeViewModel viewModel) => [ + verticalSpaceMassive, + _buildIcon(), + verticalSpaceMedium, + _buildTitle(), + verticalSpaceSmall, + _buildSubtitle(), + ]; + + Widget _buildAppBar( {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + DuolingoPracticeAppBar( + onClose: () async => + await _showSheet(context: context, viewModel: viewModel), + ); + + Widget _buildSheet( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + CancelPracticeSheet( + onClose: viewModel.pop, + onContinue: viewModel.pop, + user: viewModel.user?.firstName ?? '', + onCancel: () async => await _cancel(viewModel), + ); + + Widget _buildIcon() => SvgPicture.asset( + 'assets/icons/complete.svg', + ); + + Widget _buildTitle() => Text( + title, + style: style25DG600, + textAlign: TextAlign.center, + ); + + Widget _buildSubtitle() => Text( + subtitle, + style: style14MG400, + textAlign: TextAlign.center, + ); + + Widget _buildLowerColumn(CoursePracticeViewModel viewModel) => Column( + mainAxisSize: MainAxisSize.min, + children: _buildLowerColumnChildren(viewModel), + ); + + List _buildLowerColumnChildren(CoursePracticeViewModel viewModel) => [ + _buildPracticeButton(viewModel), + verticalSpaceSmall, + _buildSkipButtonWrapper(viewModel) + ]; + + Widget _buildPracticeButton(CoursePracticeViewModel viewModel) => + CustomElevatedButton( + height: 55, + safe: false, + borderRadius: 12, + text: 'Practice Again', + foregroundColor: kcWhite, + // onTap: () => viewModel.goTo(0), + backgroundColor: kcPrimaryColor, + ); + + Widget _buildSkipButtonWrapper(CoursePracticeViewModel viewModel) => Padding( + padding: const EdgeInsets.only(bottom: 50), + child: _buildSkipButton(viewModel), + ); + + Widget _buildSkipButton(CoursePracticeViewModel viewModel) => + CustomElevatedButton( + height: 55, + text: 'Continue', + borderRadius: 12, + backgroundColor: kcWhite, + borderColor: kcPrimaryColor, + onTap: viewModel.nextScreen, + foregroundColor: kcPrimaryColor, + ); +} diff --git a/lib/ui/views/course_practice/screens/duolingo_speaking_practice_1_answer.dart b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_1_answer.dart new file mode 100644 index 0000000..4c3d779 --- /dev/null +++ b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_1_answer.dart @@ -0,0 +1,207 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; +import 'package:waveform_recorder/waveform_recorder.dart'; +import 'package:yimaru_app/ui/widgets/countdown_timer.dart'; +import 'package:yimaru_app/ui/widgets/speaking_indicator.dart'; +import 'package:yimaru_app/ui/widgets/speaking_label.dart'; + +import '../../../common/app_colors.dart'; +import '../../../common/enmus.dart'; +import '../../../common/ui_helpers.dart'; +import '../../../widgets/cancel_practice_sheet.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../../../widgets/custom_elevated_button.dart'; +import '../course_practice_viewmodel.dart'; + +class DuolingoSpeakingPractice1Answer + extends ViewModelWidget { + const DuolingoSpeakingPractice1Answer({ + super.key, + }); + + Future _cancel(CoursePracticeViewModel viewModel) async { + await viewModel.stopRecording(); + viewModel.pop(); + viewModel.pop(); + } + + Future _showSheet( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) async => + await showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: kcTransparent, + builder: (cxt) => _buildSheet(viewModel), + ); + + @override + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => + _buildScaffoldWrapper(context: context, viewModel: viewModel); + + Widget _buildScaffoldWrapper( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + Scaffold( + backgroundColor: kcBackgroundColor, + body: _buildScaffold(context: context, viewModel: viewModel), + ); + + Widget _buildScaffold( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + SafeArea( + child: + _buildBodyColumnWrapper(context: context, viewModel: viewModel)); + + Widget _buildBodyColumnWrapper( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _buildBodyColumn(context: context, viewModel: viewModel), + ); + + Widget _buildBodyColumn( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: + _buildBodyColumnChildren(context: context, viewModel: viewModel), + ); + + List _buildBodyColumnChildren( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + [ + _buildAppBarWrapper(context: context, viewModel: viewModel), + _buildSpeakingIndicatorColumn(viewModel), + _buildContinueButtonWrapper(viewModel) + ]; + + Widget _buildAppBarWrapper( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + Column( + children: [ + verticalSpaceMedium, + _buildAppBar(context: context, viewModel: viewModel), + verticalSpaceSmall, + _buildCountdownWrapper(), + ], + ); + + Widget _buildAppBar( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + DuolingoPracticeAppBar( + title: 'Speaking practice', + onClose: () async => + await _showSheet(context: context, viewModel: viewModel), + ); + + Widget _buildSheet(CoursePracticeViewModel viewModel) => CancelPracticeSheet( + onClose: viewModel.pop, + onContinue: viewModel.pop, + user: viewModel.user?.firstName ?? '', + onCancel: () async => await _cancel(viewModel), + ); + + Widget _buildSpeakingIndicatorColumn(CoursePracticeViewModel viewModel) => + Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: _buildSpeakingIndicatorChildren(viewModel), + ); + + List _buildSpeakingIndicatorChildren( + CoursePracticeViewModel viewModel) => + [ + _buildTitle(), + verticalSpaceMedium, + _buildImageContainer(viewModel), + verticalSpaceSmall, + _buildSpeakerLabelState(viewModel), + verticalSpaceSmall, + _buildSpeakingIndicatorState(viewModel) + ]; + + Widget _buildCountdownWrapper() => + Align(alignment: Alignment.centerRight, child:_buildCountdown()); + + Widget _buildCountdown()=> const CountdownTimer(); + + Widget _buildTitle() => Text( + 'Speak about the photo below', + style: style18DG700, + textAlign: TextAlign.center, + ); + + Widget _buildImageContainer(CoursePracticeViewModel viewModel) => SizedBox( + width: 250, + height: 300, + child: _buildImageWrapper(viewModel), + ); + + Widget _buildImageWrapper(CoursePracticeViewModel viewModel) => ClipRRect( + borderRadius: BorderRadius.circular(5), + child: _buildImage(viewModel), + ); + + Widget _buildImage(CoursePracticeViewModel viewModel) => CachedNetworkImage( + fit: BoxFit.fill, + width: double.maxFinite, + imageUrl: viewModel.questions[viewModel.currentQuestion].dynamicPayload + ?.stimulus?.first.value ?? + '', + ); + + Widget _buildSpeakerLabelState(CoursePracticeViewModel viewModel) => + VoiceRecordingState.recording == viewModel.recordingState + ? _buildSpeakerLabel() + : const SizedBox(height: 20); + + Widget _buildSpeakerLabel() => const SpeakingLabel(); + + Widget _buildSpeakingIndicatorState(CoursePracticeViewModel viewModel) => + VoiceRecordingState.recording == viewModel.recordingState + ? _buildSpeakingIndicatorWrapper(viewModel) + : Container(); + + Widget _buildSpeakingIndicatorWrapper(CoursePracticeViewModel viewModel) => + Container( + width: 100, + padding: const EdgeInsets.only(right: 15), + child: _buildSpeakingIndicator(viewModel), + ); + + Widget _buildSpeakingIndicator(CoursePracticeViewModel viewModel) => + WaveformRecorder( + height: 35, + waveColor: kcPrimaryColor, + durationTextStyle: style0Ts, + controller: viewModel.waveController, + ); + + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( + padding: const EdgeInsets.only(bottom: 50), + child: _buildContinueButton(viewModel), + ); + + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => + CustomElevatedButton( + height: 55, + borderRadius: 12, + foregroundColor: kcWhite, + backgroundColor: viewModel.buttonActive + ? kcPrimaryColor + : kcPrimaryColor.withOpacity(0.1), + onTap: viewModel.buttonActive ? viewModel.nextScreen : null, + text: viewModel.buttonActive + ? 'Summit' + : 'You can submit after ${viewModel.questions[viewModel.currentQuestion].dynamicPayload?.response?.first.value['seconds']}sec', + ); +} diff --git a/lib/ui/views/course_practice/screens/duolingo_speaking_practice_1_question.dart b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_1_question.dart new file mode 100644 index 0000000..7208c59 --- /dev/null +++ b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_1_question.dart @@ -0,0 +1,238 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_timer_countdown/flutter_timer_countdown.dart'; +import 'package:stacked/stacked.dart'; +import 'package:yimaru_app/ui/common/helper_functions.dart'; +import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart'; + +import '../../../common/app_colors.dart'; +import '../../../common/enmus.dart'; +import '../../../common/ui_helpers.dart'; +import '../../../widgets/cancel_practice_sheet.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../../../widgets/custom_elevated_button.dart'; +import '../course_practice_viewmodel.dart'; + +class DuolingoSpeakingPractice1Question + extends ViewModelWidget { + const DuolingoSpeakingPractice1Question({ + super.key, + }); + + Future _cancel(CoursePracticeViewModel viewModel) async { + await viewModel.stopRecording(); + viewModel.pop(); + viewModel.pop(); + } + + Future _showSheet( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) async => + await showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: kcTransparent, + builder: (cxt) => _buildSheet(viewModel), + ); + + Future _next(CoursePracticeViewModel viewModel)async{ + await viewModel.startRecording(); + viewModel.nextScreen(); + + } + + @override + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => + _buildScaffoldWrapper(context: context, viewModel: viewModel); + + Widget _buildScaffoldWrapper( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + Scaffold( + backgroundColor: kcBackgroundColor, + body: _buildScaffoldStack(context: context, viewModel: viewModel), + ); + + Widget _buildScaffoldStack( {required BuildContext context, + required CoursePracticeViewModel viewModel})=> Stack( + children: [ + _buildScaffold(context: context,viewModel: viewModel), + _buildStartRecState(viewModel) + ], + ); + + Widget _buildScaffold( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + SafeArea( + child: + _buildBodyColumnWrapper(context: context, viewModel: viewModel)); + + Widget _buildBodyColumnWrapper( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _buildBodyColumn(context: context, viewModel: viewModel), + ); + + Widget _buildBodyColumn( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: + _buildBodyColumnChildren(context: context, viewModel: viewModel), + ); + + List _buildBodyColumnChildren( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + [ + _buildAppBarWrapper(context: context, viewModel: viewModel), + _buildSpeakingIndicatorWrapper(viewModel), + _buildContinueButtonWrapper(viewModel) + ]; + + Widget _buildAppBarWrapper( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + Column( + children: [ + verticalSpaceMedium, + _buildAppBar(context: context, viewModel: viewModel), + ], + ); + + Widget _buildAppBar( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + DuolingoPracticeAppBar( + title: 'Speaking practice', + onClose: () async => + await _showSheet(context: context, viewModel: viewModel), + ); + + Widget _buildSheet(CoursePracticeViewModel viewModel) => CancelPracticeSheet( + onClose: viewModel.pop, + onContinue: viewModel.pop, + user: viewModel.user?.firstName ?? '', + onCancel: () async => await _cancel(viewModel), + ); + + Widget _buildSpeakingIndicatorWrapper(CoursePracticeViewModel viewModel) => + Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: _buildSpeakingIndicatorChildren(viewModel), + ); + + List _buildSpeakingIndicatorChildren( + CoursePracticeViewModel viewModel) => + [ + _buildTitle(), + verticalSpaceMedium, + _buildTimerStackWrapper(viewModel), + verticalSpaceSmall, + _buildSubtitle(), + verticalSpaceSmall, + _buildImageContainer(viewModel), + ]; + + Widget _buildTitle() => Text( + 'Prepare to speak about the photo', + style: style18DG700, + textAlign: TextAlign.center, + ); + + Widget _buildTimerStackWrapper(CoursePracticeViewModel viewModel) => SizedBox( + height: 50, + child: _buildTimerStack(viewModel), + ); + + Widget _buildTimerStack(CoursePracticeViewModel viewModel) => Stack( + alignment: Alignment.center, + children: _buildTimerStackChildren(viewModel), + ); + + List _buildTimerStackChildren(CoursePracticeViewModel viewModel) => + [_buildTimer(), _buildCountdownTime(viewModel)]; + + Widget _buildTimer() => const CircularProgressIndicator( + value: 1.0, + strokeWidth: 5, + color: kcPrimaryColor, + padding: EdgeInsets.zero, + constraints: BoxConstraints(minWidth: 50, minHeight: 50), + ); + + Widget _buildCountdownTime(CoursePracticeViewModel viewModel) => + TimerCountdown( + spacerWidth: 0, + enableDescriptions: false, + timeTextStyle: style14P600, + onEnd: viewModel.nextScreen, + colonsTextStyle: style14P400, + format: CountDownTimerFormat.minutesSeconds, + endTime: DateTime.now().add( + Duration( + minutes: getMinutes( + viewModel.questions[viewModel.currentQuestion].dynamicPayload + ?.stimulus?.last.value['seconds'], + ), + seconds: getSeconds( + viewModel.questions[viewModel.currentQuestion].dynamicPayload + ?.stimulus?.last.value['seconds'], + ), + ), + ), + ); + + Widget _buildSubtitle() => Text( + 'Prep time', + style: style16DG400, + textAlign: TextAlign.center, + ); + + Widget _buildImageContainer(CoursePracticeViewModel viewModel) => SizedBox( + width: 250, + height: 300, + child: _buildImageWrapper(viewModel), + ); + + Widget _buildImageWrapper(CoursePracticeViewModel viewModel) => ClipRRect( + borderRadius: BorderRadius.circular(5), + child: _buildImage(viewModel), + ); + + Widget _buildImage(CoursePracticeViewModel viewModel) => CachedNetworkImage( + fit: BoxFit.fill, + width: double.maxFinite, + imageUrl: viewModel.questions[viewModel.currentQuestion].dynamicPayload + ?.stimulus?.first.value ?? + '', + ); + + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( + padding: const EdgeInsets.only(bottom: 50), + child: _buildContinueButton(viewModel), + ); + + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => + CustomElevatedButton( + height: 55, + borderRadius: 12, + text: 'Start Speaking', + foregroundColor: kcWhite, + onTap: () => _next(viewModel), + backgroundColor: kcPrimaryColor, + ); + + Widget _buildStartRecState(CoursePracticeViewModel viewModel) => + viewModel.busy(StateObjects.recordCoursePracticeAnswer) + ? const PageLoadingIndicator() + : Container(); + + +} diff --git a/lib/ui/views/course_practice/screens/duolingo_speaking_practice_1_review.dart b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_1_review.dart new file mode 100644 index 0000000..720a2ca --- /dev/null +++ b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_1_review.dart @@ -0,0 +1,165 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; +import 'package:yimaru_app/ui/common/helper_functions.dart'; +import 'package:yimaru_app/ui/widgets/countdown_timer.dart'; + +import '../../../common/app_colors.dart'; +import '../../../common/ui_helpers.dart'; +import '../../../widgets/cancel_practice_sheet.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../../../widgets/speaking_practice_review_section.dart'; +import '../../duolingo/duolingo_viewmodel.dart'; +import '../course_practice_viewmodel.dart'; + +class DuolingoSpeakingPractice1Review + extends ViewModelWidget { + const DuolingoSpeakingPractice1Review({ + super.key, + }); + + Future _cancel(CoursePracticeViewModel viewModel) async { + await viewModel.stopRecording(); + viewModel.pop(); + viewModel.pop(); + } + + Future _showSheet( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) async => + await showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: kcTransparent, + builder: (cxt) => _buildSheet(context: context, viewModel: viewModel), + ); + + @override + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => + _buildScaffoldWrapper(context: context, viewModel: viewModel); + + Widget _buildScaffoldWrapper( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + Scaffold( + backgroundColor: kcBackgroundColor, + body: _buildScaffold(context: context, viewModel: viewModel), + ); + + Widget _buildScaffold( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + SafeArea(child: _buildBodyColumn(context: context, viewModel: viewModel)); + + Widget _buildBodyColumn( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: + _buildBodyColumnChildren(context: context, viewModel: viewModel), + ); + + List _buildBodyColumnChildren( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + [ + _buildAppBarIndenter(context: context, viewModel: viewModel), + _buildImageSectionIndenter(viewModel), + _buildPracticeReviewSection(viewModel) + ]; + + Widget _buildAppBarIndenter( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _buildAppBarWrapper(context: context, viewModel: viewModel), + ); + + Widget _buildAppBarWrapper( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + Column( + children: [ + verticalSpaceMedium, + _buildAppBar(context: context, viewModel: viewModel), + verticalSpaceSmall, + _buildCountdownWrapper(viewModel), + ], + ); + + Widget _buildAppBar( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + DuolingoPracticeAppBar( + title: 'Feedback', + onClose: () async => + await _showSheet(context: context, viewModel: viewModel), + ); + + Widget _buildSheet( + {required BuildContext context, + required CoursePracticeViewModel viewModel}) => + CancelPracticeSheet( + onClose: viewModel.pop, + onContinue: viewModel.pop, + user: viewModel.user?.firstName ?? '', + onCancel: () async => await _cancel(viewModel), + ); + + Widget _buildImageSectionIndenter(CoursePracticeViewModel viewModel) => + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _buildImageSectionWrapper(viewModel), + ); + + Widget _buildImageSectionWrapper(CoursePracticeViewModel viewModel) => Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: _buildImageSectionChildren(viewModel), + ); + + List _buildImageSectionChildren(CoursePracticeViewModel viewModel) => + [ + _buildTitle(), + verticalSpaceMedium, + _buildImageContainer(viewModel), + ]; + + Widget _buildCountdownWrapper(CoursePracticeViewModel viewModel) => Align( + alignment: Alignment.centerRight, + child: _buildCountdown(viewModel), + ); + + Widget _buildCountdown(CoursePracticeViewModel viewModel) => + const CountdownTimer(time: '0:0'); + + Widget _buildTitle() => Text( + 'Speak about the photo below', + style: style18DG700, + textAlign: TextAlign.center, + ); + + Widget _buildImageContainer(CoursePracticeViewModel viewModel) => SizedBox( + width: 150, + height: 200, + child: _buildImageWrapper(viewModel), + ); + + Widget _buildImageWrapper(CoursePracticeViewModel viewModel) => ClipRRect( + borderRadius: BorderRadius.circular(5), + child: _buildImage(viewModel), + ); + + Widget _buildImage(CoursePracticeViewModel viewModel) => CachedNetworkImage( + fit: BoxFit.fill, + width: double.maxFinite, + imageUrl: viewModel.questions[viewModel.currentQuestion].dynamicPayload + ?.stimulus?.first.value ?? + '', + ); + + Widget _buildPracticeReviewSection(CoursePracticeViewModel viewModel) => + SpeakingPracticeReviewSection(onTap: viewModel.nextScreen,); +} diff --git a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_2_answer.dart b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_2_answer.dart similarity index 68% rename from lib/ui/views/duolingo/screens/duolingo_speaking_assessment_2_answer.dart rename to lib/ui/views/course_practice/screens/duolingo_speaking_practice_2_answer.dart index 4adc810..d322b81 100644 --- a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_2_answer.dart +++ b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_2_answer.dart @@ -1,49 +1,49 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/widgets/duolingo_assessment_question_card.dart'; +import 'package:yimaru_app/ui/widgets/duolingo_practice_question_card.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; import '../../../widgets/countdown_timer.dart'; import '../../../widgets/custom_elevated_button.dart'; import '../../../widgets/speaking_indicator.dart'; import '../../../widgets/speaking_label.dart'; -import '../duolingo_viewmodel.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoSpeakingAssessment2Answer - extends ViewModelWidget { - const DuolingoSpeakingAssessment2Answer({super.key}); +class DuolingoSpeakingPractice2Answer + extends ViewModelWidget { + const DuolingoSpeakingPractice2Answer({super.key}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumnWrapper(viewModel)); - Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildBodyColumn(viewModel), ); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarWrapper(viewModel), _buildSpeakingIndicatorWrapper(viewModel), _buildContinueButtonWrapper(viewModel) ]; - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), @@ -52,12 +52,14 @@ class DuolingoSpeakingAssessment2Answer ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - title: 'Speaking Assessment', + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( + title: 'Speaking practice', onClose: () => viewModel.goTo(0), ); - Widget _buildSpeakingIndicatorWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildSpeakingIndicatorWrapper(CoursePracticeViewModel viewModel) => + Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: _buildSpeakingIndicatorChildren(), @@ -89,7 +91,7 @@ class DuolingoSpeakingAssessment2Answer textAlign: TextAlign.center, ); - Widget _buildQuestion() => const DuolingoAssessmentQuestionCard( + Widget _buildQuestion() => const DuolingoPracticeQuestionCard( title: 'How has growing up in your hometown influenced you?', subtitle: 'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?', @@ -101,12 +103,13 @@ class DuolingoSpeakingAssessment2Answer size: 25, ); - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( padding: const EdgeInsets.only(bottom: 50), child: _buildContinueButton(viewModel), ); - Widget _buildContinueButton(DuolingoViewModel viewModel) => + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => CustomElevatedButton( height: 55, text: 'Submit', diff --git a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_2_question.dart b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_2_question.dart similarity index 87% rename from lib/ui/views/duolingo/screens/duolingo_speaking_assessment_2_question.dart rename to lib/ui/views/course_practice/screens/duolingo_speaking_practice_2_question.dart index dfa512a..df52ee4 100644 --- a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_2_question.dart +++ b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_2_question.dart @@ -1,17 +1,17 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/widgets/duolingo_assessment_question_card.dart'; +import 'package:yimaru_app/ui/widgets/duolingo_practice_question_card.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; import '../../../widgets/countdown_timer.dart'; import '../../../widgets/custom_elevated_button.dart'; -import '../duolingo_viewmodel.dart'; +import '../../duolingo/duolingo_viewmodel.dart'; -class DuolingoSpeakingAssessment2Question +class DuolingoSpeakingPractice2Question extends ViewModelWidget { - const DuolingoSpeakingAssessment2Question({ + const DuolingoSpeakingPractice2Question({ super.key, }); @@ -52,8 +52,8 @@ class DuolingoSpeakingAssessment2Question ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - title: 'Speaking Assessment', + Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoPracticeAppBar( + title: 'Speaking practice', onClose: () => viewModel.goTo(0), ); @@ -85,7 +85,7 @@ class DuolingoSpeakingAssessment2Question textAlign: TextAlign.center, ); - Widget _buildQuestion() => const DuolingoAssessmentQuestionCard( + Widget _buildQuestion() => const DuolingoPracticeQuestionCard( title: 'How has growing up in your hometown influenced you?', subtitle: 'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?', diff --git a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_2_review.dart b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_2_review.dart similarity index 60% rename from lib/ui/views/duolingo/screens/duolingo_speaking_assessment_2_review.dart rename to lib/ui/views/course_practice/screens/duolingo_speaking_practice_2_review.dart index 705e457..c931ea1 100644 --- a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_2_review.dart +++ b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_2_review.dart @@ -4,44 +4,44 @@ import 'package:yimaru_app/ui/widgets/countdown_timer.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../../../widgets/speaking_assessment_review_section.dart'; -import '../../../widgets/duolingo_assessment_question_card.dart'; -import '../duolingo_viewmodel.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../../../widgets/speaking_practice_review_section.dart'; +import '../../../widgets/duolingo_practice_question_card.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoSpeakingAssessment2Review - extends ViewModelWidget { - const DuolingoSpeakingAssessment2Review({super.key}); +class DuolingoSpeakingPractice2Review + extends ViewModelWidget { + const DuolingoSpeakingPractice2Review({super.key}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumn(viewModel)); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarIndenter(viewModel), _buildImageSectionIndenter(viewModel), - _buildAssessmentReviewSection(viewModel) + _buildPracticeReviewSection(viewModel) ]; - Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding( + Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildAppBarWrapper(viewModel), ); - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), @@ -50,17 +50,19 @@ class DuolingoSpeakingAssessment2Review ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( title: 'Feedback', onClose: () => viewModel.goTo(0), ); - Widget _buildImageSectionIndenter(DuolingoViewModel viewModel) => Padding( + Widget _buildImageSectionIndenter(CoursePracticeViewModel viewModel) => + Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildImageSectionWrapper(viewModel), ); - Widget _buildImageSectionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildImageSectionWrapper(CoursePracticeViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: _buildImageSectionChildren(), @@ -88,12 +90,12 @@ class DuolingoSpeakingAssessment2Review textAlign: TextAlign.center, ); - Widget _buildQuestion() => const DuolingoAssessmentQuestionCard( + Widget _buildQuestion() => const DuolingoPracticeQuestionCard( title: 'How has growing up in your hometown influenced you?', subtitle: 'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?', ); - Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) => - SpeakingAssessmentReviewSection(onTap: () => viewModel.goTo(5)); + Widget _buildPracticeReviewSection(CoursePracticeViewModel viewModel) => + SpeakingPracticeReviewSection(onTap: () => viewModel.goTo(5)); } diff --git a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_3_answer.dart b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_3_answer.dart similarity index 86% rename from lib/ui/views/duolingo/screens/duolingo_speaking_assessment_3_answer.dart rename to lib/ui/views/course_practice/screens/duolingo_speaking_practice_3_answer.dart index e85a927..62a6bc9 100644 --- a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_3_answer.dart +++ b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_3_answer.dart @@ -1,19 +1,19 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/widgets/duolingo_assessment_question_card.dart'; +import 'package:yimaru_app/ui/widgets/duolingo_practice_question_card.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; import '../../../widgets/countdown_timer.dart'; import '../../../widgets/custom_elevated_button.dart'; import '../../../widgets/speaking_indicator.dart'; import '../../../widgets/speaking_label.dart'; -import '../duolingo_viewmodel.dart'; +import '../../duolingo/duolingo_viewmodel.dart'; -class DuolingoSpeakingAssessment3Answer +class DuolingoSpeakingPractice3Answer extends ViewModelWidget { - const DuolingoSpeakingAssessment3Answer({super.key}); + const DuolingoSpeakingPractice3Answer({super.key}); @override Widget build(BuildContext context, DuolingoViewModel viewModel) => @@ -52,8 +52,8 @@ class DuolingoSpeakingAssessment3Answer ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - title: 'Speaking Assessment', + Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoPracticeAppBar( + title: 'Speaking practice', onClose: () => viewModel.goTo(0), ); @@ -74,7 +74,7 @@ class DuolingoSpeakingAssessment3Answer Widget _buildCountdownWrapper() => const Align(alignment: Alignment.centerRight, child: CountdownTimer()); - Widget _buildQuestion() => const DuolingoAssessmentQuestionCard( + Widget _buildQuestion() => const DuolingoPracticeQuestionCard( subtitle: 'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?', ); diff --git a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_3_question.dart b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_3_question.dart similarity index 64% rename from lib/ui/views/duolingo/screens/duolingo_speaking_assessment_3_question.dart rename to lib/ui/views/course_practice/screens/duolingo_speaking_practice_3_question.dart index 3e8418d..971e4b5 100644 --- a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_3_question.dart +++ b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_3_question.dart @@ -1,49 +1,49 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/widgets/duolingo_assessment_question_card.dart'; +import 'package:yimaru_app/ui/widgets/duolingo_practice_question_card.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; import '../../../widgets/countdown_timer.dart'; import '../../../widgets/custom_elevated_button.dart'; -import '../duolingo_viewmodel.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoSpeakingAssessment3Question - extends ViewModelWidget { - const DuolingoSpeakingAssessment3Question({ +class DuolingoSpeakingPractice3Question + extends ViewModelWidget { + const DuolingoSpeakingPractice3Question({ super.key, }); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumnWrapper(viewModel)); - Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildBodyColumn(viewModel), ); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarWrapper(viewModel), _buildSpeakingIndicatorWrapper(viewModel), _buildContinueButtonWrapper(viewModel) ]; - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), @@ -52,12 +52,14 @@ class DuolingoSpeakingAssessment3Question ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - title: 'Speaking Assessment', + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( + title: 'Speaking practice', onClose: () => viewModel.goTo(0), ); - Widget _buildSpeakingIndicatorWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildSpeakingIndicatorWrapper(CoursePracticeViewModel viewModel) => + Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: _buildSpeakingIndicatorChildren(), @@ -85,17 +87,18 @@ class DuolingoSpeakingAssessment3Question textAlign: TextAlign.center, ); - Widget _buildQuestion() => const DuolingoAssessmentQuestionCard( + Widget _buildQuestion() => const DuolingoPracticeQuestionCard( subtitle: 'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?', ); - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( padding: const EdgeInsets.only(bottom: 50), child: _buildContinueButton(viewModel), ); - Widget _buildContinueButton(DuolingoViewModel viewModel) => + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => CustomElevatedButton( height: 55, borderRadius: 12, diff --git a/lib/ui/views/course_practice/screens/duolingo_speaking_practice_3_review.dart b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_3_review.dart new file mode 100644 index 0000000..6a9dd8c --- /dev/null +++ b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_3_review.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import '../../../common/app_colors.dart'; +import '../../../common/ui_helpers.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../../../widgets/speaking_practice_review_section.dart'; +import '../../../widgets/duolingo_practice_question_card.dart'; +import '../course_practice_viewmodel.dart'; + +class DuolingoSpeakingPractice3Review + extends ViewModelWidget { + const DuolingoSpeakingPractice3Review({super.key}); + + @override + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => + _buildScaffoldWrapper(viewModel); + + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( + backgroundColor: kcBackgroundColor, + body: _buildScaffold(viewModel), + ); + + Widget _buildScaffold(CoursePracticeViewModel viewModel) => + SafeArea(child: _buildBodyColumn(viewModel)); + + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: _buildBodyColumnChildren(viewModel), + ); + + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ + _buildAppBarIndenter(viewModel), + _buildQuestionIndenter(viewModel), + _buildPracticeReviewSection(viewModel) + ]; + + Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _buildAppBarWrapper(viewModel), + ); + + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( + children: [ + verticalSpaceMedium, + _buildAppBar(viewModel), + verticalSpaceSmall, + ], + ); + + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( + title: 'Feedback', + onClose: () => viewModel.goTo(0), + ); + + Widget _buildQuestionIndenter(CoursePracticeViewModel viewModel) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _buildQuestion(), + ); + + Widget _buildQuestion() => const DuolingoPracticeQuestionCard( + subtitle: + 'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?', + ); + + Widget _buildPracticeReviewSection(CoursePracticeViewModel viewModel) => + SpeakingPracticeReviewSection(onTap: () => viewModel.goTo(5)); +} diff --git a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_4_answer.dart b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_4_answer.dart similarity index 69% rename from lib/ui/views/duolingo/screens/duolingo_speaking_assessment_4_answer.dart rename to lib/ui/views/course_practice/screens/duolingo_speaking_practice_4_answer.dart index cc0550e..19f8335 100644 --- a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_4_answer.dart +++ b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_4_answer.dart @@ -4,46 +4,46 @@ import 'package:yimaru_app/ui/widgets/wave_wrapper.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; import '../../../widgets/countdown_timer.dart'; import '../../../widgets/custom_elevated_button.dart'; import '../../../widgets/speaking_indicator.dart'; import '../../../widgets/speaking_label.dart'; -import '../duolingo_viewmodel.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoSpeakingAssessment4Answer - extends ViewModelWidget { - const DuolingoSpeakingAssessment4Answer({super.key}); +class DuolingoSpeakingPractice4Answer + extends ViewModelWidget { + const DuolingoSpeakingPractice4Answer({super.key}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumnWrapper(viewModel)); - Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildBodyColumn(viewModel), ); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarWrapper(viewModel), _buildAnswerSectionWrapper(viewModel), _buildContinueButtonWrapper(viewModel) ]; - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), @@ -52,15 +52,17 @@ class DuolingoSpeakingAssessment4Answer ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - title: 'Speaking Assessment', + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( + title: 'Speaking practice', onClose: () => viewModel.goTo(0), ); Widget _buildCountdownWrapper() => const Align(alignment: Alignment.centerRight, child: CountdownTimer()); - Widget _buildAnswerSectionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAnswerSectionWrapper(CoursePracticeViewModel viewModel) => + Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: _buildAnswerSectionChildren(), @@ -96,12 +98,13 @@ class DuolingoSpeakingAssessment4Answer color: kcWhite, ); - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( padding: const EdgeInsets.only(bottom: 50), child: _buildContinueButton(viewModel), ); - Widget _buildContinueButton(DuolingoViewModel viewModel) => + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => CustomElevatedButton( height: 55, text: 'Submit', diff --git a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_4_question.dart b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_4_question.dart similarity index 65% rename from lib/ui/views/duolingo/screens/duolingo_speaking_assessment_4_question.dart rename to lib/ui/views/course_practice/screens/duolingo_speaking_practice_4_question.dart index 857480e..40ade71 100644 --- a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_4_question.dart +++ b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_4_question.dart @@ -4,17 +4,17 @@ import 'package:yimaru_app/ui/widgets/dwarf_tile.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; import '../../../widgets/countdown_timer.dart'; import '../../../widgets/custom_elevated_button.dart'; import '../../../widgets/speaking_indicator.dart'; -import '../duolingo_viewmodel.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoSpeakingAssessment4Question - extends ViewModelWidget { - const DuolingoSpeakingAssessment4Question({super.key}); +class DuolingoSpeakingPractice4Question + extends ViewModelWidget { + const DuolingoSpeakingPractice4Question({super.key}); - void _goTo(DuolingoViewModel viewModel) { + void _goTo(CoursePracticeViewModel viewModel) { viewModel.setSpeakingState(); if (!viewModel.isSpeaking) { viewModel.goTo(3); @@ -22,34 +22,34 @@ class DuolingoSpeakingAssessment4Question } @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumnWrapper(viewModel)); - Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildBodyColumn(viewModel), ); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarWrapper(viewModel), _buildSpeakingIndicatorState(viewModel), _buildContinueButtonWrapper(viewModel) ]; - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), @@ -57,17 +57,20 @@ class DuolingoSpeakingAssessment4Question ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - title: 'Speaking Assessment', + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( + title: 'Speaking practice', onClose: () => viewModel.goTo(0), ); - Widget _buildSpeakingIndicatorState(DuolingoViewModel viewModel) => - viewModel.isSpeaking - ? _buildSpeakingState() - : _buildQuestionSectionWrapper(viewModel); + Widget _buildSpeakingIndicatorState(CoursePracticeViewModel viewModel) => + // viewModel.isSpeaking + // ? _buildSpeakingState() + // : + _buildQuestionSectionWrapper(viewModel); - Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) => + Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: _buildQuestionSectionChildren(), @@ -105,12 +108,13 @@ class DuolingoSpeakingAssessment4Question size: 75, ); - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( padding: const EdgeInsets.only(bottom: 50), child: _buildContinueButton(viewModel), ); - Widget _buildContinueButton(DuolingoViewModel viewModel) => + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => CustomElevatedButton( height: 55, text: 'Start', diff --git a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_4_review.dart b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_4_review.dart similarity index 55% rename from lib/ui/views/duolingo/screens/duolingo_speaking_assessment_4_review.dart rename to lib/ui/views/course_practice/screens/duolingo_speaking_practice_4_review.dart index f4f267d..b02daf3 100644 --- a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_4_review.dart +++ b/lib/ui/views/course_practice/screens/duolingo_speaking_practice_4_review.dart @@ -3,44 +3,44 @@ import 'package:stacked/stacked.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../../../widgets/speaking_assessment_review_section.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../../../widgets/speaking_practice_review_section.dart'; import '../../../widgets/dwarf_tile.dart'; -import '../duolingo_viewmodel.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoSpeakingAssessment4Review - extends ViewModelWidget { - const DuolingoSpeakingAssessment4Review({super.key}); +class DuolingoSpeakingPractice4Review + extends ViewModelWidget { + const DuolingoSpeakingPractice4Review({super.key}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumn(viewModel)); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarIndenter(viewModel), _buildQuestionIndenter(viewModel), _buildAssessmentReviewSection(viewModel) ]; - Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding( + Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildAppBarWrapper(viewModel), ); - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), @@ -48,12 +48,13 @@ class DuolingoSpeakingAssessment4Review ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( title: 'Feedback', onClose: () => viewModel.goTo(0), ); - Widget _buildQuestionIndenter(DuolingoViewModel viewModel) => Padding( + Widget _buildQuestionIndenter(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildDwarfWrapper(), ); @@ -69,6 +70,6 @@ class DuolingoSpeakingAssessment4Review style: style16DG600, ); - Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) => - SpeakingAssessmentReviewSection(onTap: () => viewModel.goTo(5)); + Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) => + SpeakingPracticeReviewSection(onTap: () => viewModel.goTo(5)); } diff --git a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_1_question.dart b/lib/ui/views/course_practice/screens/duolingo_writing_practice_1_question.dart similarity index 62% rename from lib/ui/views/duolingo/screens/duolingo_writing_assessment_1_question.dart rename to lib/ui/views/course_practice/screens/duolingo_writing_practice_1_question.dart index 91881c7..5d71dab 100644 --- a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_1_question.dart +++ b/lib/ui/views/course_practice/screens/duolingo_writing_practice_1_question.dart @@ -4,73 +4,74 @@ import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; import '../../../widgets/custom_elevated_button.dart'; -import '../duolingo_viewmodel.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoWritingAssessment1Question - extends ViewModelWidget { - final TextEditingController assessmentController; - const DuolingoWritingAssessment1Question( - {super.key, required this.assessmentController}); +class DuolingoWritingPractice1Question + extends ViewModelWidget { + final TextEditingController practiceController; + const DuolingoWritingPractice1Question( + {super.key, required this.practiceController}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumnWrapper(viewModel)); - Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildBodyColumn(viewModel), ); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarWrapper(viewModel), _buildQuestionWrapper(viewModel), _buildContinueButtonWrapper(viewModel) ]; - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( title: 'Writing Assessment', onClose: () => viewModel.goTo(0), ); - Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: _buildQuestionChildren(viewModel), ); - List _buildQuestionChildren(DuolingoViewModel viewModel) => [ + List _buildQuestionChildren(CoursePracticeViewModel viewModel) => [ _buildTitle(), verticalSpaceSmall, _buildImageContainer(), verticalSpaceMedium, _buildAssessmentFormField(viewModel), if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) + viewModel.focusPractice) verticalSpaceTiny, if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) + viewModel.focusPractice) _buildAssessmentWrapper(viewModel), ]; @@ -97,34 +98,35 @@ class DuolingoWritingAssessment1Question width: double.maxFinite, ); - Widget _buildAssessmentFormField(DuolingoViewModel viewModel) => + Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) => TextFormField( maxLines: 5, maxLength: 250, - controller: assessmentController, - onTap: viewModel.setAssessmentFocus, + controller: practiceController, + onTap: viewModel.setPracticeFocus, decoration: inputDecoration( focus: true, hint: 'Start writing here...', - filled: assessmentController.text.isNotEmpty), + filled: practiceController.text.isNotEmpty), ); - Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) => + Widget _buildAssessmentWrapper(CoursePracticeViewModel viewModel) => viewModel.hasAssessmentValidationMessage ? _buildAssessmentValidator(viewModel) : Container(); - Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text( + Widget _buildAssessmentValidator(CoursePracticeViewModel viewModel) => Text( viewModel.assessmentValidationMessage!, style: style12R700, ); - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( padding: const EdgeInsets.only(bottom: 50), child: _buildContinueButton(viewModel), ); - Widget _buildContinueButton(DuolingoViewModel viewModel) => + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => CustomElevatedButton( height: 55, text: 'Submit', diff --git a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_1_review.dart b/lib/ui/views/course_practice/screens/duolingo_writing_practice_1_review.dart similarity index 84% rename from lib/ui/views/duolingo/screens/duolingo_writing_assessment_1_review.dart rename to lib/ui/views/course_practice/screens/duolingo_writing_practice_1_review.dart index 5e48050..bd02511 100644 --- a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_1_review.dart +++ b/lib/ui/views/course_practice/screens/duolingo_writing_practice_1_review.dart @@ -1,18 +1,18 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/widgets/duolingo_assessment_review_section.dart'; +import 'package:yimaru_app/ui/widgets/duolingo_practice_review_section.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../duolingo_viewmodel.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../../duolingo/duolingo_viewmodel.dart'; -class DuolingoWritingAssessment1Review +class DuolingoWritingPractice1Review extends ViewModelWidget { - final TextEditingController assessmentController; + final TextEditingController practiceController; - const DuolingoWritingAssessment1Review( - {super.key, required this.assessmentController}); + const DuolingoWritingPractice1Review( + {super.key, required this.practiceController}); @override Widget build(BuildContext context, DuolingoViewModel viewModel) => @@ -49,7 +49,7 @@ class DuolingoWritingAssessment1Review ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( + Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoPracticeAppBar( title: 'Feedback', onClose: () => viewModel.goTo(0), ); @@ -112,13 +112,13 @@ class DuolingoWritingAssessment1Review maxLines: 5, enabled: false, maxLength: 250, - controller: assessmentController, + controller: practiceController, decoration: inputDecoration( focus: true, hint: 'Start writing here...', - filled: assessmentController.text.isNotEmpty), + filled: practiceController.text.isNotEmpty), ); Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) => - DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5)); + DuolingoPracticeReviewSection(onTap: () => viewModel.goTo(5)); } diff --git a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_2_answer.dart b/lib/ui/views/course_practice/screens/duolingo_writing_practice_2_answer.dart similarity index 58% rename from lib/ui/views/duolingo/screens/duolingo_writing_assessment_2_answer.dart rename to lib/ui/views/course_practice/screens/duolingo_writing_practice_2_answer.dart index d99bf9d..a92ddbf 100644 --- a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_2_answer.dart +++ b/lib/ui/views/course_practice/screens/duolingo_writing_practice_2_answer.dart @@ -4,75 +4,76 @@ import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; import '../../../widgets/custom_elevated_button.dart'; -import '../../../widgets/duolingo_assessment_question_card.dart'; -import '../duolingo_viewmodel.dart'; +import '../../../widgets/duolingo_practice_question_card.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoWritingAssessment2Answer - extends ViewModelWidget { - final TextEditingController assessmentController; +class DuolingoWritingPractice2Answer + extends ViewModelWidget { + final TextEditingController practiceController; - const DuolingoWritingAssessment2Answer( - {super.key, required this.assessmentController}); + const DuolingoWritingPractice2Answer( + {super.key, required this.practiceController}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumnWrapper(viewModel)); - Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildBodyColumn(viewModel), ); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarWrapper(viewModel), _buildQuestionWrapper(viewModel), _buildContinueButtonWrapper(viewModel) ]; - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( title: 'Writing Assessment', onClose: () => viewModel.goTo(0), ); - Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: _buildQuestionChildren(viewModel), ); - List _buildQuestionChildren(DuolingoViewModel viewModel) => [ + List _buildQuestionChildren(CoursePracticeViewModel viewModel) => [ _buildTitle(), verticalSpaceMedium, _buildQuestion(), verticalSpaceMedium, _buildAssessmentFormField(viewModel), if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) + viewModel.focusPractice) verticalSpaceTiny, if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) + viewModel.focusPractice) _buildAssessmentWrapper(viewModel), ]; @@ -82,38 +83,39 @@ class DuolingoWritingAssessment2Answer textAlign: TextAlign.center, ); - Widget _buildQuestion() => const DuolingoAssessmentQuestionCard( + Widget _buildQuestion() => const DuolingoPracticeQuestionCard( subtitle: 'Describe a memorable experience from your school or work life and explain why it was meaningful to you.'); - Widget _buildAssessmentFormField(DuolingoViewModel viewModel) => + Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) => TextFormField( maxLines: 5, maxLength: 250, - controller: assessmentController, - onTap: viewModel.setAssessmentFocus, + controller: practiceController, + onTap: viewModel.setPracticeFocus, decoration: inputDecoration( focus: true, hint: 'Start writing here...', - filled: assessmentController.text.isNotEmpty), + filled: practiceController.text.isNotEmpty), ); - Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) => + Widget _buildAssessmentWrapper(CoursePracticeViewModel viewModel) => viewModel.hasAssessmentValidationMessage ? _buildAssessmentValidator(viewModel) : Container(); - Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text( + Widget _buildAssessmentValidator(CoursePracticeViewModel viewModel) => Text( viewModel.assessmentValidationMessage!, style: style12R700, ); - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( padding: const EdgeInsets.only(bottom: 50), child: _buildContinueButton(viewModel), ); - Widget _buildContinueButton(DuolingoViewModel viewModel) => + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => CustomElevatedButton( height: 55, text: 'Submit', diff --git a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_2_question.dart b/lib/ui/views/course_practice/screens/duolingo_writing_practice_2_question.dart similarity index 59% rename from lib/ui/views/duolingo/screens/duolingo_writing_assessment_2_question.dart rename to lib/ui/views/course_practice/screens/duolingo_writing_practice_2_question.dart index 5d4d66c..8c6eff6 100644 --- a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_2_question.dart +++ b/lib/ui/views/course_practice/screens/duolingo_writing_practice_2_question.dart @@ -3,65 +3,66 @@ import 'package:stacked/stacked.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; import '../../../widgets/custom_elevated_button.dart'; -import '../../../widgets/duolingo_assessment_question_card.dart'; -import '../duolingo_viewmodel.dart'; +import '../../../widgets/duolingo_practice_question_card.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoWritingAssessment2Question - extends ViewModelWidget { - final TextEditingController assessmentController; +class DuolingoWritingPractice2Question + extends ViewModelWidget { + final TextEditingController practiceController; - const DuolingoWritingAssessment2Question( - {super.key, required this.assessmentController}); + const DuolingoWritingPractice2Question( + {super.key, required this.practiceController}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumnWrapper(viewModel)); - Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildBodyColumn(viewModel), ); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarWrapper(viewModel), _buildQuestionWrapper(viewModel), _buildContinueButtonWrapper(viewModel) ]; - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( title: 'Writing Assessment', onClose: () => viewModel.goTo(0), ); - Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: _buildQuestionChildren(viewModel), ); - List _buildQuestionChildren(DuolingoViewModel viewModel) => [ + List _buildQuestionChildren(CoursePracticeViewModel viewModel) => [ _buildTitle(), _buildSubtitle(), verticalSpaceMedium, @@ -81,16 +82,17 @@ class DuolingoWritingAssessment2Question textAlign: TextAlign.center, ); - Widget _buildQuestion() => const DuolingoAssessmentQuestionCard( + Widget _buildQuestion() => const DuolingoPracticeQuestionCard( subtitle: 'Describe a memorable experience from your school or work life and explain why it was meaningful to you.'); - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( padding: const EdgeInsets.only(bottom: 50), child: _buildContinueButton(viewModel), ); - Widget _buildContinueButton(DuolingoViewModel viewModel) => + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => CustomElevatedButton( height: 55, text: 'Submit', diff --git a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_2_review.dart b/lib/ui/views/course_practice/screens/duolingo_writing_practice_2_review.dart similarity index 56% rename from lib/ui/views/duolingo/screens/duolingo_writing_assessment_2_review.dart rename to lib/ui/views/course_practice/screens/duolingo_writing_practice_2_review.dart index f3a3ceb..dfe1485 100644 --- a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_2_review.dart +++ b/lib/ui/views/course_practice/screens/duolingo_writing_practice_2_review.dart @@ -1,48 +1,48 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/widgets/duolingo_assessment_review_section.dart'; +import 'package:yimaru_app/ui/widgets/duolingo_practice_review_section.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../../../widgets/duolingo_assessment_question_card.dart'; -import '../duolingo_viewmodel.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../../../widgets/duolingo_practice_question_card.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoWritingAssessment2Review - extends ViewModelWidget { - final TextEditingController assessmentController; +class DuolingoWritingPractice2Review + extends ViewModelWidget { + final TextEditingController practiceController; - const DuolingoWritingAssessment2Review( - {super.key, required this.assessmentController}); + const DuolingoWritingPractice2Review( + {super.key, required this.practiceController}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumn(viewModel)); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarIndenter(viewModel), _buildExpandedBody(viewModel), ]; - Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding( + Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildAppBarWrapper(viewModel), ); - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), @@ -50,27 +50,29 @@ class DuolingoWritingAssessment2Review ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( title: 'Feedback', onClose: () => viewModel.goTo(0), ); - Widget _buildExpandedBody(DuolingoViewModel viewModel) => + Widget _buildExpandedBody(CoursePracticeViewModel viewModel) => Expanded(child: _buildBodyScroller(viewModel)); - Widget _buildBodyScroller(DuolingoViewModel viewModel) => + Widget _buildBodyScroller(CoursePracticeViewModel viewModel) => SingleChildScrollView( child: _buildQuestionSectionWrapper(viewModel), ); - Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) => + Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: _buildQuestionQuestionSectionChildren(viewModel), ); List _buildQuestionQuestionSectionChildren( - DuolingoViewModel viewModel) => + CoursePracticeViewModel viewModel) => [ verticalSpaceLarge, _buildTitle(), @@ -93,28 +95,28 @@ class DuolingoWritingAssessment2Review child: _buildQuestion(), ); - Widget _buildQuestion() => const DuolingoAssessmentQuestionCard( + Widget _buildQuestion() => const DuolingoPracticeQuestionCard( subtitle: 'Describe a memorable experience from your school or work life and explain why it was meaningful to you.'); - Widget _buildAssessmentFormFieldWrapper(DuolingoViewModel viewModel) => + Widget _buildAssessmentFormFieldWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildAssessmentFormField(viewModel), ); - Widget _buildAssessmentFormField(DuolingoViewModel viewModel) => + Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) => TextFormField( maxLines: 5, enabled: false, maxLength: 250, - controller: assessmentController, + controller: practiceController, decoration: inputDecoration( focus: true, hint: 'Start writing here...', - filled: assessmentController.text.isNotEmpty), + filled: practiceController.text.isNotEmpty), ); - Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) => - DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5)); + Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) => + DuolingoPracticeReviewSection(onTap: () => viewModel.goTo(5)); } diff --git a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_3_question.dart b/lib/ui/views/course_practice/screens/duolingo_writing_practice_3_question.dart similarity index 58% rename from lib/ui/views/duolingo/screens/duolingo_writing_assessment_3_question.dart rename to lib/ui/views/course_practice/screens/duolingo_writing_practice_3_question.dart index 377bbf9..af1e955 100644 --- a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_3_question.dart +++ b/lib/ui/views/course_practice/screens/duolingo_writing_practice_3_question.dart @@ -4,75 +4,76 @@ import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; import '../../../widgets/custom_elevated_button.dart'; -import '../../../widgets/duolingo_assessment_question_card.dart'; -import '../duolingo_viewmodel.dart'; +import '../../../widgets/duolingo_practice_question_card.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoWritingAssessment3Question - extends ViewModelWidget { - final TextEditingController assessmentController; +class DuolingoWritingPractice3Question + extends ViewModelWidget { + final TextEditingController practiceController; - const DuolingoWritingAssessment3Question( - {super.key, required this.assessmentController}); + const DuolingoWritingPractice3Question( + {super.key, required this.practiceController}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumnWrapper(viewModel)); - Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildBodyColumn(viewModel), ); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarWrapper(viewModel), _buildQuestionWrapper(viewModel), _buildContinueButtonWrapper(viewModel) ]; - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( title: 'Writing Assessment', onClose: () => viewModel.goTo(0), ); - Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: _buildQuestionChildren(viewModel), ); - List _buildQuestionChildren(DuolingoViewModel viewModel) => [ + List _buildQuestionChildren(CoursePracticeViewModel viewModel) => [ _buildTitle(), verticalSpaceMedium, _buildQuestion(), verticalSpaceMedium, _buildAssessmentFormField(viewModel), if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) + viewModel.focusPractice) verticalSpaceTiny, if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) + viewModel.focusPractice) _buildAssessmentWrapper(viewModel), ]; @@ -82,38 +83,39 @@ class DuolingoWritingAssessment3Question textAlign: TextAlign.center, ); - Widget _buildQuestion() => const DuolingoAssessmentQuestionCard( + Widget _buildQuestion() => const DuolingoPracticeQuestionCard( title: '1. Can you tell me about your favorite type of music?', ); - Widget _buildAssessmentFormField(DuolingoViewModel viewModel) => + Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) => TextFormField( maxLines: 5, maxLength: 250, - controller: assessmentController, - onTap: viewModel.setAssessmentFocus, + controller: practiceController, + onTap: viewModel.setPracticeFocus, decoration: inputDecoration( focus: true, hint: 'Start writing here...', - filled: assessmentController.text.isNotEmpty), + filled: practiceController.text.isNotEmpty), ); - Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) => + Widget _buildAssessmentWrapper(CoursePracticeViewModel viewModel) => viewModel.hasAssessmentValidationMessage ? _buildAssessmentValidator(viewModel) : Container(); - Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text( + Widget _buildAssessmentValidator(CoursePracticeViewModel viewModel) => Text( viewModel.assessmentValidationMessage!, style: style12R700, ); - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( padding: const EdgeInsets.only(bottom: 50), child: _buildContinueButton(viewModel), ); - Widget _buildContinueButton(DuolingoViewModel viewModel) => + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => CustomElevatedButton( height: 55, text: 'Submit', diff --git a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_3_review.dart b/lib/ui/views/course_practice/screens/duolingo_writing_practice_3_review.dart similarity index 55% rename from lib/ui/views/duolingo/screens/duolingo_writing_assessment_3_review.dart rename to lib/ui/views/course_practice/screens/duolingo_writing_practice_3_review.dart index 30bf60c..31098f5 100644 --- a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_3_review.dart +++ b/lib/ui/views/course_practice/screens/duolingo_writing_practice_3_review.dart @@ -1,48 +1,48 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/widgets/duolingo_assessment_review_section.dart'; +import 'package:yimaru_app/ui/widgets/duolingo_practice_review_section.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../../../widgets/duolingo_assessment_question_card.dart'; -import '../duolingo_viewmodel.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../../../widgets/duolingo_practice_question_card.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoWritingAssessment3Review - extends ViewModelWidget { - final TextEditingController assessmentController; +class DuolingoWritingPractice3Review + extends ViewModelWidget { + final TextEditingController practiceController; - const DuolingoWritingAssessment3Review( - {super.key, required this.assessmentController}); + const DuolingoWritingPractice3Review( + {super.key, required this.practiceController}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumn(viewModel)); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarIndenter(viewModel), _buildExpandedBody(viewModel), ]; - Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding( + Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildAppBarWrapper(viewModel), ); - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), @@ -50,27 +50,29 @@ class DuolingoWritingAssessment3Review ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( title: 'Feedback', onClose: () => viewModel.goTo(0), ); - Widget _buildExpandedBody(DuolingoViewModel viewModel) => + Widget _buildExpandedBody(CoursePracticeViewModel viewModel) => Expanded(child: _buildBodyScroller(viewModel)); - Widget _buildBodyScroller(DuolingoViewModel viewModel) => + Widget _buildBodyScroller(CoursePracticeViewModel viewModel) => SingleChildScrollView( child: _buildQuestionSectionWrapper(viewModel), ); - Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) => + Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: _buildQuestionQuestionSectionChildren(viewModel), ); List _buildQuestionQuestionSectionChildren( - DuolingoViewModel viewModel) => + CoursePracticeViewModel viewModel) => [ verticalSpaceLarge, _buildTitle(), @@ -93,27 +95,27 @@ class DuolingoWritingAssessment3Review child: _buildQuestion(), ); - Widget _buildQuestion() => const DuolingoAssessmentQuestionCard( + Widget _buildQuestion() => const DuolingoPracticeQuestionCard( title: 'Can you tell me about your favorite type of music?'); - Widget _buildAssessmentFormFieldWrapper(DuolingoViewModel viewModel) => + Widget _buildAssessmentFormFieldWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildAssessmentFormField(viewModel), ); - Widget _buildAssessmentFormField(DuolingoViewModel viewModel) => + Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) => TextFormField( maxLines: 5, enabled: false, maxLength: 250, - controller: assessmentController, + controller: practiceController, decoration: inputDecoration( focus: true, hint: 'Start writing here...', - filled: assessmentController.text.isNotEmpty), + filled: practiceController.text.isNotEmpty), ); - Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) => - DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5)); + Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) => + DuolingoPracticeReviewSection(onTap: () => viewModel.goTo(5)); } diff --git a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_4_question.dart b/lib/ui/views/course_practice/screens/duolingo_writing_practice_4_question.dart similarity index 58% rename from lib/ui/views/duolingo/screens/duolingo_writing_assessment_4_question.dart rename to lib/ui/views/course_practice/screens/duolingo_writing_practice_4_question.dart index f159fbc..cc18bff 100644 --- a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_4_question.dart +++ b/lib/ui/views/course_practice/screens/duolingo_writing_practice_4_question.dart @@ -4,75 +4,76 @@ import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; import '../../../widgets/custom_elevated_button.dart'; -import '../../../widgets/duolingo_assessment_question_card.dart'; -import '../duolingo_viewmodel.dart'; +import '../../../widgets/duolingo_practice_question_card.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoWritingAssessment4Question - extends ViewModelWidget { - final TextEditingController assessmentController; +class DuolingoWritingPractice4Question + extends ViewModelWidget { + final TextEditingController practiceController; - const DuolingoWritingAssessment4Question( - {super.key, required this.assessmentController}); + const DuolingoWritingPractice4Question( + {super.key, required this.practiceController}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumnWrapper(viewModel)); - Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildBodyColumn(viewModel), ); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarWrapper(viewModel), _buildQuestionWrapper(viewModel), _buildContinueButtonWrapper(viewModel) ]; - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( title: 'Writing Assessment', onClose: () => viewModel.goTo(0), ); - Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: _buildQuestionChildren(viewModel), ); - List _buildQuestionChildren(DuolingoViewModel viewModel) => [ + List _buildQuestionChildren(CoursePracticeViewModel viewModel) => [ _buildTitle(), verticalSpaceMedium, _buildQuestion(), verticalSpaceMedium, _buildAssessmentFormField(viewModel), if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) + viewModel.focusPractice) verticalSpaceTiny, if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) + viewModel.focusPractice) _buildAssessmentWrapper(viewModel), ]; @@ -82,38 +83,39 @@ class DuolingoWritingAssessment4Question textAlign: TextAlign.center, ); - Widget _buildQuestion() => const DuolingoAssessmentQuestionCard( + Widget _buildQuestion() => const DuolingoPracticeQuestionCard( title: '1. Can you tell me about your favorite type of music?', ); - Widget _buildAssessmentFormField(DuolingoViewModel viewModel) => + Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) => TextFormField( maxLines: 5, maxLength: 250, - controller: assessmentController, - onTap: viewModel.setAssessmentFocus, + controller: practiceController, + onTap: viewModel.setPracticeFocus, decoration: inputDecoration( focus: true, hint: 'Start writing here...', - filled: assessmentController.text.isNotEmpty), + filled: practiceController.text.isNotEmpty), ); - Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) => + Widget _buildAssessmentWrapper(CoursePracticeViewModel viewModel) => viewModel.hasAssessmentValidationMessage ? _buildAssessmentValidator(viewModel) : Container(); - Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text( + Widget _buildAssessmentValidator(CoursePracticeViewModel viewModel) => Text( viewModel.assessmentValidationMessage!, style: style12R700, ); - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( + Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => + Padding( padding: const EdgeInsets.only(bottom: 50), child: _buildContinueButton(viewModel), ); - Widget _buildContinueButton(DuolingoViewModel viewModel) => + Widget _buildContinueButton(CoursePracticeViewModel viewModel) => CustomElevatedButton( height: 55, text: 'Submit', diff --git a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_4_review.dart b/lib/ui/views/course_practice/screens/duolingo_writing_practice_4_review.dart similarity index 55% rename from lib/ui/views/duolingo/screens/duolingo_writing_assessment_4_review.dart rename to lib/ui/views/course_practice/screens/duolingo_writing_practice_4_review.dart index eb252df..e1be9e2 100644 --- a/lib/ui/views/duolingo/screens/duolingo_writing_assessment_4_review.dart +++ b/lib/ui/views/course_practice/screens/duolingo_writing_practice_4_review.dart @@ -1,48 +1,48 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/widgets/duolingo_assessment_review_section.dart'; +import 'package:yimaru_app/ui/widgets/duolingo_practice_review_section.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../../../widgets/duolingo_assessment_question_card.dart'; -import '../duolingo_viewmodel.dart'; +import '../../../widgets/duolingo_practice_app_bar.dart'; +import '../../../widgets/duolingo_practice_question_card.dart'; +import '../course_practice_viewmodel.dart'; -class DuolingoWritingAssessment4Review - extends ViewModelWidget { - final TextEditingController assessmentController; +class DuolingoWritingPractice4Review + extends ViewModelWidget { + final TextEditingController practiceController; - const DuolingoWritingAssessment4Review( - {super.key, required this.assessmentController}); + const DuolingoWritingPractice4Review( + {super.key, required this.practiceController}); @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => _buildScaffoldWrapper(viewModel); - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( + Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold( backgroundColor: kcBackgroundColor, body: _buildScaffold(viewModel), ); - Widget _buildScaffold(DuolingoViewModel viewModel) => + Widget _buildScaffold(CoursePracticeViewModel viewModel) => SafeArea(child: _buildBodyColumn(viewModel)); - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( + Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildBodyColumnChildren(viewModel), ); - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ + List _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [ _buildAppBarIndenter(viewModel), _buildExpandedBody(viewModel), ]; - Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding( + Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildAppBarWrapper(viewModel), ); - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column( children: [ verticalSpaceMedium, _buildAppBar(viewModel), @@ -50,27 +50,29 @@ class DuolingoWritingAssessment4Review ], ); - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( + Widget _buildAppBar(CoursePracticeViewModel viewModel) => + DuolingoPracticeAppBar( title: 'Feedback', onClose: () => viewModel.goTo(0), ); - Widget _buildExpandedBody(DuolingoViewModel viewModel) => + Widget _buildExpandedBody(CoursePracticeViewModel viewModel) => Expanded(child: _buildBodyScroller(viewModel)); - Widget _buildBodyScroller(DuolingoViewModel viewModel) => + Widget _buildBodyScroller(CoursePracticeViewModel viewModel) => SingleChildScrollView( child: _buildQuestionSectionWrapper(viewModel), ); - Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column( + Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) => + Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: _buildQuestionQuestionSectionChildren(viewModel), ); List _buildQuestionQuestionSectionChildren( - DuolingoViewModel viewModel) => + CoursePracticeViewModel viewModel) => [ verticalSpaceLarge, _buildTitle(), @@ -93,27 +95,27 @@ class DuolingoWritingAssessment4Review child: _buildQuestion(), ); - Widget _buildQuestion() => const DuolingoAssessmentQuestionCard( + Widget _buildQuestion() => const DuolingoPracticeQuestionCard( title: 'Can you tell me about your favorite type of music?'); - Widget _buildAssessmentFormFieldWrapper(DuolingoViewModel viewModel) => + Widget _buildAssessmentFormFieldWrapper(CoursePracticeViewModel viewModel) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildAssessmentFormField(viewModel), ); - Widget _buildAssessmentFormField(DuolingoViewModel viewModel) => + Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) => TextFormField( maxLines: 5, enabled: false, maxLength: 250, - controller: assessmentController, + controller: practiceController, decoration: inputDecoration( focus: true, hint: 'Start writing here...', - filled: assessmentController.text.isNotEmpty), + filled: practiceController.text.isNotEmpty), ); - Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) => - DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5)); + Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) => + DuolingoPracticeReviewSection(onTap: () => viewModel.goTo(5)); } diff --git a/lib/ui/views/duolingo/duolingo_view.dart b/lib/ui/views/duolingo/duolingo_view.dart index 032bc0c..c8959bf 100644 --- a/lib/ui/views/duolingo/duolingo_view.dart +++ b/lib/ui/views/duolingo/duolingo_view.dart @@ -2,37 +2,7 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import 'package:stacked/stacked_annotations.dart'; import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_assessments_screens.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_finish_screen.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_intro_screen.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_listening_assessment_1_question.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_listening_assessment_1_review.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_listening_assessment_2_question.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_listening_assessment_2_review.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_listening_assessment_3_question.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_retake_screen.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_1_answer.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_1_question.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_1_review.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_2_answer.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_2_question.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_2_review.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_3_answer.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_3_question.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_3_review.dart' - hide DuolingoSpeakingAssessment2Question; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_4_answer.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_4_question.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_4_review.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_1_question.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_1_review.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_2_answer.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_2_question.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_2_review.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_3_question.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_3_review.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_4_question.dart'; -import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_4_review.dart'; + import '../../common/app_colors.dart'; import '../../common/validators/form_validator.dart'; @@ -44,130 +14,7 @@ import 'duolingo_viewmodel.dart'; class DuolingoView extends StackedView with $DuolingoView { const DuolingoView({Key? key}) : super(key: key); - Widget _buildQuestionScreen(DuolingoViewModel viewModel) { - if (viewModel.selectedAssessment['intro_title'] == - 'Speak About the Photo') { - return const DuolingoSpeakingAssessment1Question(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Read, Then Speak') { - return const DuolingoSpeakingAssessment2Question(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Speaking Sample') { - return const DuolingoSpeakingAssessment3Question(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Interactive Speaking') { - return const DuolingoSpeakingAssessment4Question(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Write About the Photo') { - return DuolingoWritingAssessment1Question( - assessmentController: assessmentController); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Writing Sample') { - return DuolingoWritingAssessment2Question( - assessmentController: assessmentController); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Interactive Writing Part 1') { - return DuolingoWritingAssessment3Question( - assessmentController: assessmentController); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Interactive Writing Part 2') { - return DuolingoWritingAssessment4Question( - assessmentController: assessmentController); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Listen and Type') { - return DuolingoListeningAssessment1Question( - assessmentController: assessmentController); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Interactive Listening - Part 1') { - return DuolingoListeningAssessment2Question( - assessmentController: assessmentController); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Interactive Listening - Part 2') { - return DuolingoListeningAssessment3Question( - assessmentController: assessmentController); - } - return Container(); - } - - Widget _buildAnswerScreen(DuolingoViewModel viewModel) { - if (viewModel.selectedAssessment['intro_title'] == - 'Speak About the Photo') { - return const DuolingoSpeakingAssessment1Answer(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Read, Then Speak') { - return const DuolingoSpeakingAssessment2Answer(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Speaking Sample') { - return const DuolingoSpeakingAssessment3Answer(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Interactive Speaking') { - return const DuolingoSpeakingAssessment4Answer(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Write About the Photo') { - return Container(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Writing Sample') { - return DuolingoWritingAssessment2Answer( - assessmentController: assessmentController); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Interactive Writing Part 1') { - return Container(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Interactive Writing Part 2') { - return Container(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Listen and Type') { - return Container(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Interactive Listening - Part 1') { - return Container(); - } - - return Container(); - } - - Widget _buildReviewScreen(DuolingoViewModel viewModel) { - if (viewModel.selectedAssessment['intro_title'] == - 'Speak About the Photo') { - return const DuolingoSpeakingAssessment1Review(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Read, Then Speak') { - return const DuolingoSpeakingAssessment2Review(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Speaking Sample') { - return const DuolingoSpeakingAssessment3Review(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Interactive Speaking') { - return const DuolingoSpeakingAssessment4Review(); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Write About the Photo') { - return DuolingoWritingAssessment1Review( - assessmentController: assessmentController); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Writing Sample') { - return DuolingoWritingAssessment2Review( - assessmentController: assessmentController); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Interactive Writing Part 1') { - return DuolingoWritingAssessment3Review( - assessmentController: assessmentController); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Interactive Writing Part 2') { - return DuolingoWritingAssessment4Review( - assessmentController: assessmentController); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Listen and Type') { - return DuolingoListeningAssessment1Review( - assessmentController: assessmentController); - } else if (viewModel.selectedAssessment['intro_title'] == - 'Interactive Listening - Part 1') { - return DuolingoListeningAssessment2Review( - assessmentController: assessmentController); - } - - return Container(); - } @override DuolingoViewModel viewModelBuilder(BuildContext context) => @@ -198,40 +45,40 @@ class DuolingoView extends StackedView with $DuolingoView { index: viewModel.currentPage, children: _buildScreens(viewModel)); List _buildScreens(DuolingoViewModel viewModel) => [ - _buildDuolingoAssessmentsScreen(), - _buildDuolingoIntroScreen(viewModel), - _buildDuolingoQuestionScreen(viewModel), - _buildDuolingoAnswerScreen(viewModel), - _buildDuolingoReviewScreen(viewModel), - _buildDuolingoRetakeScreen(viewModel), - _buildDuolingoFinishScreen(viewModel), + // _buildDuolingoAssessmentsScreen(), + // _buildDuolingoIntroScreen(viewModel), + // _buildDuolingoQuestionScreen(viewModel), + // _buildDuolingoAnswerScreen(viewModel), + // _buildDuolingoReviewScreen(viewModel), + // _buildDuolingoRetakeScreen(viewModel), + // _buildDuolingoFinishScreen(viewModel), ]; - Widget _buildDuolingoAssessmentsScreen() => - const DuolingoAssessmentsScreens(); - - Widget _buildDuolingoIntroScreen(DuolingoViewModel viewModel) => - DuolingoIntroScreen( - type: viewModel.selectedAssessment['type'], - title: viewModel.selectedAssessment['intro_title'], - subtitle: viewModel.selectedAssessment['intro_subtitle']); - - Widget _buildDuolingoQuestionScreen(DuolingoViewModel viewModel) => - _buildQuestionScreen(viewModel); - - Widget _buildDuolingoAnswerScreen(DuolingoViewModel viewModel) => - _buildAnswerScreen(viewModel); - - Widget _buildDuolingoReviewScreen(DuolingoViewModel viewModel) => - _buildReviewScreen(viewModel); - - Widget _buildDuolingoRetakeScreen(DuolingoViewModel viewModel) => - DuolingoRetakeScreen( - title: viewModel.selectedAssessment['outro_title'], - subtitle: viewModel.selectedAssessment['outro_subtitle']); - - Widget _buildDuolingoFinishScreen(DuolingoViewModel viewModel) => - DuolingoFinishScreen( - title: viewModel.selectedAssessment['outro_title'], - subtitle: viewModel.selectedAssessment['outro_subtitle']); + // Widget _buildDuolingoAssessmentsScreen() => + // const DuolingoAssessmentsScreens(); + // + // Widget _buildDuolingoIntroScreen(DuolingoViewModel viewModel) => + // DuolingoIntroScreen( + // type: viewModel.selectedAssessment['type'], + // title: viewModel.selectedAssessment['intro_title'], + // subtitle: viewModel.selectedAssessment['intro_subtitle']); + // + // Widget _buildDuolingoQuestionScreen(DuolingoViewModel viewModel) => + // _buildQuestionScreen(viewModel); + // + // Widget _buildDuolingoAnswerScreen(DuolingoViewModel viewModel) => + // _buildAnswerScreen(viewModel); + // + // Widget _buildDuolingoReviewScreen(DuolingoViewModel viewModel) => + // _buildReviewScreen(viewModel); + // + // Widget _buildDuolingoRetakeScreen(DuolingoViewModel viewModel) => + // DuolingoRetakeScreen( + // title: viewModel.selectedAssessment['outro_title'], + // subtitle: viewModel.selectedAssessment['outro_subtitle']); + // + // Widget _buildDuolingoFinishScreen(DuolingoViewModel viewModel) => + // DuolingoFinishScreen( + // title: viewModel.selectedAssessment['outro_title'], + // subtitle: viewModel.selectedAssessment['outro_subtitle']); } diff --git a/lib/ui/views/duolingo/screens/duolingo_finish_screen.dart b/lib/ui/views/duolingo/screens/duolingo_finish_screen.dart deleted file mode 100644 index 12c96f7..0000000 --- a/lib/ui/views/duolingo/screens/duolingo_finish_screen.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:stacked/stacked.dart'; - -import '../../../common/app_colors.dart'; -import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../../../widgets/custom_elevated_button.dart'; -import '../duolingo_viewmodel.dart'; - -class DuolingoFinishScreen extends ViewModelWidget { - final String title; - final String subtitle; - const DuolingoFinishScreen( - {super.key, required this.title, required this.subtitle}); - - @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => - _buildScaffoldWrapper(viewModel); - - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( - backgroundColor: kcBackgroundColor, - body: _buildSafeWrapper(viewModel), - ); - Widget _buildSafeWrapper(DuolingoViewModel viewModel) => - SafeArea(child: _buildScaffold(viewModel)); - - Widget _buildScaffold(DuolingoViewModel viewModel) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildScaffoldChildren(viewModel), - ); - - List _buildScaffoldChildren(DuolingoViewModel viewModel) => [ - _buildAppBar(viewModel), - verticalSpaceMedium, - _buildExpandedBody(viewModel) - ]; - - Widget _buildExpandedBody(DuolingoViewModel viewModel) => - Expanded(child: _buildBodyWrapper(viewModel)); - - Widget _buildBodyWrapper(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildBody(viewModel), - ); - - Widget _buildBody(DuolingoViewModel viewModel) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildBodyChildren(viewModel), - ); - - List _buildBodyChildren(DuolingoViewModel viewModel) => - [_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)]; - - Widget _buildUpperColumn(DuolingoViewModel viewModel) => Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: _buildUpperColumnChildren(viewModel), - ); - - List _buildUpperColumnChildren(DuolingoViewModel viewModel) => [ - verticalSpaceLarge, - _buildIcon(), - verticalSpaceMedium, - _buildTitle(), - verticalSpaceSmall, - _buildSubtitle(), - ]; - - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - onClose: () => viewModel.goTo(0), - ); - - Widget _buildIcon() => SvgPicture.asset( - 'assets/icons/complete.svg', - ); - - Widget _buildTitle() => Text( - title, - style: style25DG600, - textAlign: TextAlign.center, - ); - - Widget _buildSubtitle() => Text( - subtitle, - style: style14MG400, - textAlign: TextAlign.center, - ); - - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.only(bottom: 50), - child: _buildContinueButton(viewModel), - ); - - Widget _buildContinueButton(DuolingoViewModel viewModel) => - CustomElevatedButton( - height: 55, - text: 'Continue', - borderRadius: 12, - backgroundColor: kcWhite, - borderColor: kcPrimaryColor, - onTap: () => viewModel.goTo(0), - foregroundColor: kcPrimaryColor, - ); -} diff --git a/lib/ui/views/duolingo/screens/duolingo_intro_screen.dart b/lib/ui/views/duolingo/screens/duolingo_intro_screen.dart deleted file mode 100644 index 2a977ac..0000000 --- a/lib/ui/views/duolingo/screens/duolingo_intro_screen.dart +++ /dev/null @@ -1,125 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:iconsax/iconsax.dart'; -import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/common/enmus.dart'; -import 'package:yimaru_app/ui/views/duolingo/duolingo_viewmodel.dart'; -import 'package:yimaru_app/ui/widgets/duolingo_assessment_app_bar.dart'; - -import '../../../common/app_colors.dart'; -import '../../../common/ui_helpers.dart'; -import '../../../widgets/custom_elevated_button.dart'; -import '../../../widgets/wave_wrapper.dart'; - -class DuolingoIntroScreen extends ViewModelWidget { - final String title; - final String subtitle; - final DuolingoAssessments type; - - const DuolingoIntroScreen( - {super.key, - required this.type, - required this.title, - required this.subtitle}); - - IconData _getIcon() { - if (type == DuolingoAssessments.speaking) { - return Icons.waves; - } else if (type == DuolingoAssessments.writing) { - return Iconsax.pen_add; - } else if (type == DuolingoAssessments.listening) { - return Icons.hearing; - } else { - return Icons.book; - } - } - - @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => - _buildScaffoldWrapper(viewModel); - - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( - backgroundColor: kcBackgroundColor, - body: _buildScaffold(viewModel), - ); - - Widget _buildScaffold(DuolingoViewModel viewModel) => - SafeArea(child: _buildBodyColumnWrapper(viewModel)); - - Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildBodyColumn(viewModel), - ); - - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildBodyColumnChildren(viewModel), - ); - - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ - _buildAppBarWrapper(viewModel), - _buildSpeakingIndicatorWrapper(viewModel), - _buildContinueButtonWrapper(viewModel) - ]; - - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( - children: [ - verticalSpaceMedium, - _buildAppBar(viewModel), - verticalSpaceMedium, - ], - ); - - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - title: 'Speaking Assessment', - onClose: () => viewModel.goTo(0), - ); - - Widget _buildSpeakingIndicatorWrapper(DuolingoViewModel viewModel) => Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - children: _buildSpeakingIndicatorChildren(), - ); - - List _buildSpeakingIndicatorChildren() => [ - _buildSpeakingIconWrapper(), - verticalSpaceMedium, - _buildTitle(), - _buildSubtitle(), - ]; - - Widget _buildTitle() => Text( - title, - style: style18DG700, - textAlign: TextAlign.center, - ); - - Widget _buildSubtitle() => Text( - subtitle, - style: style14DG400, - textAlign: TextAlign.center, - ); - - Widget _buildSpeakingIconWrapper() => - WaveWrapper(height: 125, child: _buildSpeakingIcon()); - - Widget _buildSpeakingIcon() => Icon( - _getIcon(), - size: 30, - color: kcPrimaryColor, - ); - - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.only(bottom: 50), - child: _buildContinueButton(viewModel), - ); - - Widget _buildContinueButton(DuolingoViewModel viewModel) => - CustomElevatedButton( - height: 55, - borderRadius: 12, - foregroundColor: kcWhite, - text: 'Start Preparation', - onTap: () => viewModel.goTo(2), - backgroundColor: kcPrimaryColor, - ); -} diff --git a/lib/ui/views/duolingo/screens/duolingo_listening_assessment_2_question.dart b/lib/ui/views/duolingo/screens/duolingo_listening_assessment_2_question.dart deleted file mode 100644 index a2613d1..0000000 --- a/lib/ui/views/duolingo/screens/duolingo_listening_assessment_2_question.dart +++ /dev/null @@ -1,150 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart'; -import 'package:yimaru_app/ui/widgets/listenable_assessment_card.dart'; - -import '../../../common/app_colors.dart'; -import '../../../common/ui_helpers.dart'; -import '../../../widgets/custom_elevated_button.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../duolingo_viewmodel.dart'; - -class DuolingoListeningAssessment2Question - extends ViewModelWidget { - final TextEditingController assessmentController; - - const DuolingoListeningAssessment2Question( - {super.key, required this.assessmentController}); - - @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => - _buildScaffoldWrapper(viewModel); - - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( - backgroundColor: kcBackgroundColor, - body: _buildScaffold(viewModel), - ); - - Widget _buildScaffold(DuolingoViewModel viewModel) => - SafeArea(child: _buildBodyColumnWrapper(viewModel)); - - Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildBodyColumn(viewModel), - ); - - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildBodyColumnChildren(viewModel), - ); - - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ - _buildAppBarWrapper(viewModel), - _buildQuestionWrapper(viewModel), - _buildContinueButtonWrapper(viewModel) - ]; - - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( - children: [ - verticalSpaceMedium, - _buildAppBar(viewModel), - ], - ); - - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - title: 'Listening Assessment', - onClose: () => viewModel.goTo(0), - ); - - Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildQuestionChildren(viewModel), - ); - - List _buildQuestionChildren(DuolingoViewModel viewModel) => [ - _buildTitle(), - verticalSpaceMedium, - _buildQuestion(), - verticalSpaceLarge, - _buildLabel('1. Focus of the conference:'), - verticalSpaceTiny, - _buildAssessmentFormField(viewModel), - if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) - verticalSpaceTiny, - if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) - _buildAssessmentWrapper(viewModel), - verticalSpaceSmall, - _buildLabel('2. Number of presentations:'), - verticalSpaceTiny, - _buildAssessmentFormField(viewModel), - if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) - verticalSpaceTiny, - if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) - _buildAssessmentWrapper(viewModel), - verticalSpaceSmall, - _buildLabel('3. Keynote speaker’s field:'), - verticalSpaceTiny, - _buildAssessmentFormField(viewModel), - if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) - verticalSpaceTiny, - if (viewModel.hasAssessmentValidationMessage && - viewModel.focusAssessment) - _buildAssessmentWrapper(viewModel), - ]; - - Widget _buildTitle() => Text( - 'Listen to the audio message and type your response.', - style: style18DG700, - textAlign: TextAlign.center, - ); - - Widget _buildQuestion() => const ListenableAssessmentCard(); - - Widget _buildLabel(String question) => Text( - question, - style: style14MG400, - textAlign: TextAlign.center, - ); - - Widget _buildAssessmentFormField(DuolingoViewModel viewModel) => - TextFormField( - controller: assessmentController, - onTap: viewModel.setAssessmentFocus, - decoration: inputDecoration( - focus: true, - hint: 'Start writing here...', - filled: assessmentController.text.isNotEmpty), - ); - - Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) => - viewModel.hasAssessmentValidationMessage - ? _buildAssessmentValidator(viewModel) - : Container(); - - Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text( - viewModel.assessmentValidationMessage!, - style: style12R700, - ); - - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.only(bottom: 50), - child: _buildContinueButton(viewModel), - ); - - Widget _buildContinueButton(DuolingoViewModel viewModel) => - CustomElevatedButton( - height: 55, - text: 'Submit', - borderRadius: 12, - foregroundColor: kcWhite, - onTap: () => viewModel.goTo(4), - backgroundColor: kcPrimaryColor, - ); -} diff --git a/lib/ui/views/duolingo/screens/duolingo_retake_screen.dart b/lib/ui/views/duolingo/screens/duolingo_retake_screen.dart deleted file mode 100644 index 45c8a95..0000000 --- a/lib/ui/views/duolingo/screens/duolingo_retake_screen.dart +++ /dev/null @@ -1,128 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:stacked/stacked.dart'; - -import '../../../common/app_colors.dart'; -import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../../../widgets/custom_elevated_button.dart'; -import '../duolingo_viewmodel.dart'; - -class DuolingoRetakeScreen extends ViewModelWidget { - final String title; - final String subtitle; - const DuolingoRetakeScreen( - {super.key, required this.title, required this.subtitle}); - - @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => - _buildScaffoldWrapper(viewModel); - - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( - backgroundColor: kcBackgroundColor, - body: _buildSafeWrapper(viewModel), - ); - Widget _buildSafeWrapper(DuolingoViewModel viewModel) => - SafeArea(child: _buildScaffold(viewModel)); - - Widget _buildScaffold(DuolingoViewModel viewModel) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildScaffoldChildren(viewModel), - ); - - List _buildScaffoldChildren(DuolingoViewModel viewModel) => [ - _buildAppBar(viewModel), - verticalSpaceMedium, - _buildExpandedBody(viewModel) - ]; - - Widget _buildExpandedBody(DuolingoViewModel viewModel) => - Expanded(child: _buildBodyWrapper(viewModel)); - - Widget _buildBodyWrapper(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildBody(viewModel), - ); - - Widget _buildBody(DuolingoViewModel viewModel) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildBodyChildren(viewModel), - ); - - List _buildBodyChildren(DuolingoViewModel viewModel) => - [_buildUpperColumn(viewModel), _buildLowerColumn(viewModel)]; - - Widget _buildUpperColumn(DuolingoViewModel viewModel) => Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: _buildUpperColumnChildren(viewModel), - ); - - List _buildUpperColumnChildren(DuolingoViewModel viewModel) => [ - verticalSpaceLarge, - _buildIcon(), - verticalSpaceMedium, - _buildTitle(), - verticalSpaceSmall, - _buildSubtitle(), - ]; - - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - onClose: () => viewModel.goTo(0), - ); - - Widget _buildIcon() => SvgPicture.asset( - 'assets/icons/complete.svg', - ); - - Widget _buildTitle() => Text( - title, - style: style25DG600, - textAlign: TextAlign.center, - ); - - Widget _buildSubtitle() => Text( - subtitle, - style: style14MG400, - textAlign: TextAlign.center, - ); - - Widget _buildLowerColumn(DuolingoViewModel viewModel) => Column( - mainAxisSize: MainAxisSize.min, - children: _buildLowerColumnChildren(viewModel), - ); - - List _buildLowerColumnChildren(DuolingoViewModel viewModel) => [ - _buildContinueButton(viewModel), - verticalSpaceSmall, - _buildSkipButtonWrapper(viewModel) - ]; - - Widget _buildContinueButton(DuolingoViewModel viewModel) => - CustomElevatedButton( - height: 55, - safe: false, - borderRadius: 12, - text: 'Practice Again', - foregroundColor: kcWhite, - onTap: () => viewModel.goTo(0), - backgroundColor: kcPrimaryColor, - ); - - Widget _buildSkipButtonWrapper(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.only(bottom: 50), - child: _buildSkipButton(viewModel), - ); - - Widget _buildSkipButton(DuolingoViewModel viewModel) => CustomElevatedButton( - height: 55, - text: 'Continue', - borderRadius: 12, - backgroundColor: kcWhite, - borderColor: kcPrimaryColor, - onTap: () => viewModel.goTo(6), - foregroundColor: kcPrimaryColor, - ); -} diff --git a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_1_answer.dart b/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_1_answer.dart deleted file mode 100644 index b20ad8b..0000000 --- a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_1_answer.dart +++ /dev/null @@ -1,123 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/widgets/countdown_timer.dart'; -import 'package:yimaru_app/ui/widgets/speaking_indicator.dart'; -import 'package:yimaru_app/ui/widgets/speaking_label.dart'; - -import '../../../common/app_colors.dart'; -import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../../../widgets/custom_elevated_button.dart'; -import '../duolingo_viewmodel.dart'; - -class DuolingoSpeakingAssessment1Answer - extends ViewModelWidget { - const DuolingoSpeakingAssessment1Answer({ - super.key, - }); - - @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => - _buildScaffoldWrapper(viewModel); - - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( - backgroundColor: kcBackgroundColor, - body: _buildScaffold(viewModel), - ); - - Widget _buildScaffold(DuolingoViewModel viewModel) => - SafeArea(child: _buildBodyColumnWrapper(viewModel)); - - Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildBodyColumn(viewModel), - ); - - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildBodyColumnChildren(viewModel), - ); - - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ - _buildAppBarWrapper(viewModel), - _buildSpeakingIndicatorWrapper(viewModel), - _buildContinueButtonWrapper(viewModel) - ]; - - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( - children: [ - verticalSpaceMedium, - _buildAppBar(viewModel), - verticalSpaceSmall, - _buildCountdownWrapper(), - ], - ); - - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - title: 'Speaking Assessment', - onClose: () => viewModel.goTo(0), - ); - - Widget _buildSpeakingIndicatorWrapper(DuolingoViewModel viewModel) => Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - children: _buildSpeakingIndicatorChildren(), - ); - - List _buildSpeakingIndicatorChildren() => [ - _buildTitle(), - verticalSpaceMedium, - _buildImageContainer(), - verticalSpaceSmall, - _buildSpeakerLabel(), - verticalSpaceSmall, - _buildSpeakingIndicator() - ]; - - Widget _buildCountdownWrapper() => - const Align(alignment: Alignment.centerRight, child: CountdownTimer()); - - Widget _buildTitle() => Text( - 'Speak about the photo below', - style: style18DG700, - textAlign: TextAlign.center, - ); - - Widget _buildImageContainer() => SizedBox( - width: 250, - height: 300, - child: _buildImageWrapper(), - ); - - Widget _buildImageWrapper() => ClipRRect( - borderRadius: BorderRadius.circular(5), - child: _buildImage(), - ); - - Widget _buildImage() => Image.asset( - 'assets/images/image_1.png', - fit: BoxFit.fill, - width: double.maxFinite, - ); - - Widget _buildSpeakerLabel() => const SpeakingLabel(); - - Widget _buildSpeakingIndicator() => const SpeakingIndicator( - size: 25, - ); - - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.only(bottom: 50), - child: _buildContinueButton(viewModel), - ); - - Widget _buildContinueButton(DuolingoViewModel viewModel) => - CustomElevatedButton( - height: 55, - text: 'Submit', - borderRadius: 12, - foregroundColor: kcWhite, - onTap: () => viewModel.goTo(4), - backgroundColor: kcPrimaryColor, - ); -} diff --git a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_1_question.dart b/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_1_question.dart deleted file mode 100644 index b56d400..0000000 --- a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_1_question.dart +++ /dev/null @@ -1,142 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stacked/stacked.dart'; - -import '../../../common/app_colors.dart'; -import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../../../widgets/custom_elevated_button.dart'; -import '../duolingo_viewmodel.dart'; - -class DuolingoSpeakingAssessment1Question - extends ViewModelWidget { - const DuolingoSpeakingAssessment1Question({ - super.key, - }); - - @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => - _buildScaffoldWrapper(viewModel); - - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( - backgroundColor: kcBackgroundColor, - body: _buildScaffold(viewModel), - ); - - Widget _buildScaffold(DuolingoViewModel viewModel) => - SafeArea(child: _buildBodyColumnWrapper(viewModel)); - - Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildBodyColumn(viewModel), - ); - - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildBodyColumnChildren(viewModel), - ); - - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ - _buildAppBarWrapper(viewModel), - _buildSpeakingIndicatorWrapper(viewModel), - _buildContinueButtonWrapper(viewModel) - ]; - - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( - children: [ - verticalSpaceMedium, - _buildAppBar(viewModel), - ], - ); - - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - title: 'Speaking Assessment', - onClose: () => viewModel.goTo(0), - ); - - Widget _buildSpeakingIndicatorWrapper(DuolingoViewModel viewModel) => Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - children: _buildSpeakingIndicatorChildren(), - ); - - List _buildSpeakingIndicatorChildren() => [ - _buildTitle(), - verticalSpaceMedium, - _buildTimerStackWrapper(), - verticalSpaceSmall, - _buildSubtitle(), - verticalSpaceSmall, - _buildImageContainer(), - ]; - - Widget _buildTitle() => Text( - 'Prepare to speak about the photo', - style: style18DG700, - textAlign: TextAlign.center, - ); - - Widget _buildTimerStackWrapper() => SizedBox( - height: 50, - child: _buildTimerStack(), - ); - - Widget _buildTimerStack() => Stack( - alignment: Alignment.center, - children: _buildTimerStackChildren(), - ); - - List _buildTimerStackChildren() => - [_buildTimer(), _buildCountdownTime()]; - - Widget _buildTimer() => const CircularProgressIndicator( - value: 1.0, - strokeWidth: 5, - color: kcPrimaryColor, - padding: EdgeInsets.zero, - constraints: BoxConstraints(minWidth: 50, minHeight: 50), - ); - - Widget _buildCountdownTime() => Text( - '0:20', - style: style16P600, - textAlign: TextAlign.center, - ); - - Widget _buildSubtitle() => Text( - 'Prep time', - style: style16DG400, - textAlign: TextAlign.center, - ); - - Widget _buildImageContainer() => SizedBox( - width: 250, - height: 300, - child: _buildImageWrapper(), - ); - - Widget _buildImageWrapper() => ClipRRect( - borderRadius: BorderRadius.circular(5), - child: _buildImage(), - ); - - Widget _buildImage() => Image.asset( - 'assets/images/image_1.png', - fit: BoxFit.fill, - width: double.maxFinite, - ); - - Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.only(bottom: 50), - child: _buildContinueButton(viewModel), - ); - - Widget _buildContinueButton(DuolingoViewModel viewModel) => - CustomElevatedButton( - height: 55, - borderRadius: 12, - text: 'Start Speaking', - foregroundColor: kcWhite, - onTap: () => viewModel.goTo(3), - backgroundColor: kcPrimaryColor, - ); -} diff --git a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_1_review.dart b/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_1_review.dart deleted file mode 100644 index 3110775..0000000 --- a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_1_review.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/widgets/countdown_timer.dart'; - -import '../../../common/app_colors.dart'; -import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../../../widgets/speaking_assessment_review_section.dart'; -import '../duolingo_viewmodel.dart'; - -class DuolingoSpeakingAssessment1Review - extends ViewModelWidget { - const DuolingoSpeakingAssessment1Review({ - super.key, - }); - - @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => - _buildScaffoldWrapper(viewModel); - - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( - backgroundColor: kcBackgroundColor, - body: _buildScaffold(viewModel), - ); - - Widget _buildScaffold(DuolingoViewModel viewModel) => - SafeArea(child: _buildBodyColumn(viewModel)); - - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildBodyColumnChildren(viewModel), - ); - - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ - _buildAppBarIndenter(viewModel), - _buildImageSectionIndenter(viewModel), - _buildAssessmentReviewSection(viewModel) - ]; - - Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildAppBarWrapper(viewModel), - ); - - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( - children: [ - verticalSpaceMedium, - _buildAppBar(viewModel), - verticalSpaceSmall, - _buildCountdownWrapper(), - ], - ); - - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - title: 'Feedback', - onClose: () => viewModel.goTo(0), - ); - - Widget _buildImageSectionIndenter(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildImageSectionWrapper(viewModel), - ); - - Widget _buildImageSectionWrapper(DuolingoViewModel viewModel) => Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - children: _buildImageSectionChildren(), - ); - - List _buildImageSectionChildren() => [ - _buildTitle(), - verticalSpaceMedium, - _buildImageContainer(), - ]; - - Widget _buildCountdownWrapper() => - const Align(alignment: Alignment.centerRight, child: CountdownTimer()); - - Widget _buildTitle() => Text( - 'Speak about the photo below', - style: style18DG700, - textAlign: TextAlign.center, - ); - - Widget _buildImageContainer() => SizedBox( - width: 150, - height: 200, - child: _buildImageWrapper(), - ); - - Widget _buildImageWrapper() => ClipRRect( - borderRadius: BorderRadius.circular(5), - child: _buildImage(), - ); - - Widget _buildImage() => Image.asset( - 'assets/images/image_1.png', - fit: BoxFit.fill, - width: double.maxFinite, - ); - - Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) => - SpeakingAssessmentReviewSection(onTap: () => viewModel.goTo(5)); -} diff --git a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_3_review.dart b/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_3_review.dart deleted file mode 100644 index 9ccd356..0000000 --- a/lib/ui/views/duolingo/screens/duolingo_speaking_assessment_3_review.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stacked/stacked.dart'; - -import '../../../common/app_colors.dart'; -import '../../../common/ui_helpers.dart'; -import '../../../widgets/duolingo_assessment_app_bar.dart'; -import '../../../widgets/speaking_assessment_review_section.dart'; -import '../../../widgets/duolingo_assessment_question_card.dart'; -import '../duolingo_viewmodel.dart'; - -class DuolingoSpeakingAssessment3Review - extends ViewModelWidget { - const DuolingoSpeakingAssessment3Review({super.key}); - - @override - Widget build(BuildContext context, DuolingoViewModel viewModel) => - _buildScaffoldWrapper(viewModel); - - Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold( - backgroundColor: kcBackgroundColor, - body: _buildScaffold(viewModel), - ); - - Widget _buildScaffold(DuolingoViewModel viewModel) => - SafeArea(child: _buildBodyColumn(viewModel)); - - Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildBodyColumnChildren(viewModel), - ); - - List _buildBodyColumnChildren(DuolingoViewModel viewModel) => [ - _buildAppBarIndenter(viewModel), - _buildQuestionIndenter(viewModel), - _buildAssessmentReviewSection(viewModel) - ]; - - Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildAppBarWrapper(viewModel), - ); - - Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column( - children: [ - verticalSpaceMedium, - _buildAppBar(viewModel), - verticalSpaceSmall, - ], - ); - - Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar( - title: 'Feedback', - onClose: () => viewModel.goTo(0), - ); - - Widget _buildQuestionIndenter(DuolingoViewModel viewModel) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildQuestion(), - ); - - Widget _buildQuestion() => const DuolingoAssessmentQuestionCard( - subtitle: - 'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?', - ); - - Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) => - SpeakingAssessmentReviewSection(onTap: () => viewModel.goTo(5)); -} diff --git a/lib/ui/views/failure/failure_view.dart b/lib/ui/views/failure/failure_view.dart index bf09e78..f567ea2 100644 --- a/lib/ui/views/failure/failure_view.dart +++ b/lib/ui/views/failure/failure_view.dart @@ -1,14 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_carousel_widget/flutter_carousel_widget.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:stacked/stacked.dart'; import 'package:yimaru_app/ui/views/failure/screens/first_failure_screen.dart'; import 'package:yimaru_app/ui/views/failure/screens/second_failure_screen.dart'; import 'package:yimaru_app/ui/views/failure/screens/third_failure_screen.dart'; import '../../common/app_colors.dart'; -import '../../common/ui_helpers.dart'; -import '../../widgets/custom_circular_progress_indicator.dart'; import 'failure_viewmodel.dart'; class FailureView extends StackedView { diff --git a/lib/ui/views/failure/screens/first_failure_screen.dart b/lib/ui/views/failure/screens/first_failure_screen.dart index 9b88da0..fb8de8f 100644 --- a/lib/ui/views/failure/screens/first_failure_screen.dart +++ b/lib/ui/views/failure/screens/first_failure_screen.dart @@ -1,43 +1,40 @@ -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stacked/stacked.dart'; import 'package:yimaru_app/ui/common/app_colors.dart'; import 'package:yimaru_app/ui/common/ui_helpers.dart'; import 'package:yimaru_app/ui/views/failure/failure_viewmodel.dart'; -import 'package:yimaru_app/ui/views/startup/startup_viewmodel.dart'; -import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart'; -import '../../../common/translations/locale_keys.g.dart'; import '../../../widgets/custom_circular_progress_indicator.dart'; class FirstFailureScreen extends ViewModelWidget { final String label; final GestureTapCallback onTap; - const FirstFailureScreen({super.key,required this.onTap,required this.label}); + const FirstFailureScreen( + {super.key, required this.onTap, required this.label}); @override - Widget build(BuildContext context, FailureViewModel viewModel) => + Widget build(BuildContext context, FailureViewModel viewModel) => _buildScaffoldWrapper(); - Widget _buildScaffoldWrapper( ) => Scaffold( + Widget _buildScaffoldWrapper() => Scaffold( backgroundColor: kcPrimaryColor, body: _buildScaffoldPadding(), ); - Widget _buildScaffoldPadding( ) => Padding( + Widget _buildScaffoldPadding() => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildScaffold(), ); - Widget _buildScaffold( ) => Column( + Widget _buildScaffold() => Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildScaffoldChildren(), ); - List _buildScaffoldChildren( ) => + List _buildScaffoldChildren() => [_buildUpperColumn(), _buildLowerColumnWrapper()]; Widget _buildUpperColumn() => Column( @@ -59,17 +56,17 @@ class FirstFailureScreen extends ViewModelWidget { height: 25, ); - Widget _buildLowerColumnWrapper( ) => Expanded( + Widget _buildLowerColumnWrapper() => Expanded( child: _buildLowerColumn(), ); - Widget _buildLowerColumn( ) => Column( + Widget _buildLowerColumn() => Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: _buildLowerColumnChildren(), ); - List _buildLowerColumnChildren( ) => [ + List _buildLowerColumnChildren() => [ _buildTitle(), verticalSpaceMedium, _buildImageWrapper(), @@ -110,56 +107,51 @@ class FirstFailureScreen extends ViewModelWidget { fit: BoxFit.cover, ); - Widget _buildSafeWrapper( ) => - SafeArea(child: _buildContinueButtonWrapper()); + Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper()); - Widget _buildContinueButtonWrapper( ) => Align( + Widget _buildContinueButtonWrapper() => Align( alignment: Alignment.bottomCenter, child: _buildLoadingTextContainer(), ); - - Widget _buildLoadingTextContainer() => Padding( - padding: const EdgeInsets.only(bottom: 50), - child: _buildLoadingTextWrapper(), - ); + padding: const EdgeInsets.only(bottom: 50), + child: _buildLoadingTextWrapper(), + ); Widget _buildLoadingTextWrapper() => Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildLoadingTextChildren(), - ); + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: _buildLoadingTextChildren(), + ); List _buildLoadingTextChildren() => [ - _buildLoadingText(), - horizontalSpaceSmall, - _buildIndicatorWrapper(), - horizontalSpaceSmall, - _buildRetryButtonWrapper() - ]; + _buildLoadingText(), + horizontalSpaceSmall, + _buildIndicatorWrapper(), + horizontalSpaceSmall, + _buildRetryButtonWrapper() + ]; - Widget _buildLoadingText() => - Text('$label...', style: style16W600); + Widget _buildLoadingText() => Text('$label...', style: style16W600); Widget _buildIndicatorWrapper() => SizedBox( - width: 16, - height: 16, - child: _buildIndicator(), - ); + width: 16, + height: 16, + child: _buildIndicator(), + ); Widget _buildIndicator() => const CustomCircularProgressIndicator(color: kcWhite); - Widget _buildRetryButtonWrapper() => GestureDetector( - onTap: onTap, - child: _buildRetryButton(), - ); + onTap: onTap, + child: _buildRetryButton(), + ); Widget _buildRetryButton() => Text( - 'Retry', - style: style16W600.copyWith( - fontStyle: FontStyle.italic, decoration: TextDecoration.underline), - ); + 'Retry', + style: style16W600.copyWith( + fontStyle: FontStyle.italic, decoration: TextDecoration.underline), + ); } diff --git a/lib/ui/views/failure/screens/second_failure_screen.dart b/lib/ui/views/failure/screens/second_failure_screen.dart index 43e835f..31c8bea 100644 --- a/lib/ui/views/failure/screens/second_failure_screen.dart +++ b/lib/ui/views/failure/screens/second_failure_screen.dart @@ -1,13 +1,10 @@ -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stacked/stacked.dart'; import 'package:yimaru_app/ui/common/app_colors.dart'; import 'package:yimaru_app/ui/common/ui_helpers.dart'; import 'package:yimaru_app/ui/views/failure/failure_viewmodel.dart'; -import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart'; -import '../../../common/translations/locale_keys.g.dart'; import '../../../widgets/custom_circular_progress_indicator.dart'; class SecondFailureScreen extends ViewModelWidget { diff --git a/lib/ui/views/failure/screens/third_failure_screen.dart b/lib/ui/views/failure/screens/third_failure_screen.dart index d7a64c4..e5e7782 100644 --- a/lib/ui/views/failure/screens/third_failure_screen.dart +++ b/lib/ui/views/failure/screens/third_failure_screen.dart @@ -1,13 +1,10 @@ -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stacked/stacked.dart'; import 'package:yimaru_app/ui/common/app_colors.dart'; import 'package:yimaru_app/ui/common/ui_helpers.dart'; import 'package:yimaru_app/ui/views/failure/failure_viewmodel.dart'; -import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart'; -import '../../../common/translations/locale_keys.g.dart'; import '../../../widgets/custom_circular_progress_indicator.dart'; class ThirdFailureScreen extends ViewModelWidget { @@ -21,145 +18,140 @@ class ThirdFailureScreen extends ViewModelWidget { Widget build(BuildContext context, FailureViewModel viewModel) => _buildScaffoldWrapper(); - Widget _buildScaffoldWrapper( ) => Scaffold( - backgroundColor:kcWhite, - body: _buildScaffoldPadding(), - ); - Widget _buildScaffoldPadding( ) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildScaffold(), - ); + Widget _buildScaffoldWrapper() => Scaffold( + backgroundColor: kcWhite, + body: _buildScaffoldPadding(), + ); + Widget _buildScaffoldPadding() => Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _buildScaffold(), + ); - Widget _buildScaffold( ) => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildScaffoldChildren(), - ); + Widget _buildScaffold() => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: _buildScaffoldChildren(), + ); - List _buildScaffoldChildren( ) => + List _buildScaffoldChildren() => [_buildUpperColumn(), _buildLowerColumnWrapper()]; Widget _buildUpperColumn() => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: _buildUpperColumnChildren(), - ); + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: _buildUpperColumnChildren(), + ); List _buildUpperColumnChildren() => [verticalSpaceLarge, _buildIconWrapper(), verticalSpaceLarge]; Widget _buildIconWrapper() => Align( - alignment: Alignment.topLeft, - child: _buildIcon(), - ); + alignment: Alignment.topLeft, + child: _buildIcon(), + ); Widget _buildIcon() => SvgPicture.asset( - 'assets/icons/logo_purple.svg', - height: 25, - ); + 'assets/icons/logo_purple.svg', + height: 25, + ); - Widget _buildLowerColumnWrapper( ) => Expanded( - child: _buildLowerColumn(), - ); + Widget _buildLowerColumnWrapper() => Expanded( + child: _buildLowerColumn(), + ); - Widget _buildLowerColumn( ) => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: _buildLowerColumnChildren(), - ); - - List _buildLowerColumnChildren( ) => [ - _buildTitle(), - verticalSpaceMedium, - _buildImageWrapper(), - verticalSpaceMedium, - _buildSafeWrapper() - ]; + Widget _buildLowerColumn() => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: _buildLowerColumnChildren(), + ); + List _buildLowerColumnChildren() => [ + _buildTitle(), + verticalSpaceMedium, + _buildImageWrapper(), + verticalSpaceMedium, + _buildSafeWrapper() + ]; Widget _buildTitle() => Text.rich( - TextSpan( - text: 'እንግሊዝኛ\n', - style: style25P600, - children: [ TextSpan( - text: 'በማንኛውም', - style: style25P400, - ), - TextSpan( - text: ' ቦታ ', + text: 'እንግሊዝኛ\n', style: style25P600, + children: [ + TextSpan( + text: 'በማንኛውም', + style: style25P400, + ), + TextSpan( + text: ' ቦታ ', + style: style25P600, + ), + TextSpan( + text: 'ይማሩ!', + style: style25P400, + ), + ], ), - TextSpan( - text: 'ይማሩ!', - style: style25P400, - ), - ], - ), - ); + ); Widget _buildImageWrapper() => Expanded(child: _buildImageClipper()); Widget _buildImageClipper() => ClipRRect( - borderRadius: BorderRadius.circular(25), - child: _buildImage(), - ); + borderRadius: BorderRadius.circular(25), + child: _buildImage(), + ); Widget _buildImage() => Image.asset( - 'assets/images/landing_3.png', - fit: BoxFit.cover, - ); - - Widget _buildSafeWrapper( ) => - SafeArea(child: _buildContinueButtonWrapper()); - - Widget _buildContinueButtonWrapper( ) => Align( - alignment: Alignment.bottomCenter, - child: _buildLoadingTextContainer(), - ); + 'assets/images/landing_3.png', + fit: BoxFit.cover, + ); + Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper()); + Widget _buildContinueButtonWrapper() => Align( + alignment: Alignment.bottomCenter, + child: _buildLoadingTextContainer(), + ); Widget _buildLoadingTextContainer() => Padding( - padding: const EdgeInsets.only(bottom: 50), - child: _buildLoadingTextWrapper(), - ); + padding: const EdgeInsets.only(bottom: 50), + child: _buildLoadingTextWrapper(), + ); Widget _buildLoadingTextWrapper() => Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildLoadingTextChildren(), - ); + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: _buildLoadingTextChildren(), + ); List _buildLoadingTextChildren() => [ - _buildLoadingText(), - horizontalSpaceSmall, - _buildIndicatorWrapper(), - horizontalSpaceSmall,_buildRetryButtonWrapper() - ]; + _buildLoadingText(), + horizontalSpaceSmall, + _buildIndicatorWrapper(), + horizontalSpaceSmall, + _buildRetryButtonWrapper() + ]; - Widget _buildLoadingText() => - Text('$label...', style: style16P600); + Widget _buildLoadingText() => Text('$label...', style: style16P600); Widget _buildIndicatorWrapper() => SizedBox( - width: 16, - height: 16, - child: _buildIndicator(), - ); + width: 16, + height: 16, + child: _buildIndicator(), + ); Widget _buildIndicator() => const CustomCircularProgressIndicator(color: kcPrimaryColor); Widget _buildRetryButtonWrapper() => GestureDetector( - onTap: onTap, - child: _buildRetryButton(), - ); + onTap: onTap, + child: _buildRetryButton(), + ); Widget _buildRetryButton() => Text( - 'Retry', - style: style16P600.copyWith( - fontStyle: FontStyle.italic, decoration: TextDecoration.underline), - ); + 'Retry', + style: style16P600.copyWith( + fontStyle: FontStyle.italic, decoration: TextDecoration.underline), + ); } - diff --git a/lib/ui/views/home/home_view.dart b/lib/ui/views/home/home_view.dart index 8c32f15..645846b 100644 --- a/lib/ui/views/home/home_view.dart +++ b/lib/ui/views/home/home_view.dart @@ -5,12 +5,10 @@ import 'package:yimaru_app/ui/common/app_colors.dart'; import 'package:yimaru_app/ui/common/enmus.dart'; import 'package:yimaru_app/ui/common/translations/locale_keys.g.dart'; import 'package:yimaru_app/ui/views/course/course_view.dart'; -import 'package:yimaru_app/ui/views/course_catalog/course_catalog_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/widgets/page_loading_indicator.dart'; -import '../../widgets/coming_soon.dart'; import 'home_viewmodel.dart'; class HomeView extends StackedView { diff --git a/lib/ui/views/home/home_viewmodel.dart b/lib/ui/views/home/home_viewmodel.dart index d6b517a..ee3c4ab 100644 --- a/lib/ui/views/home/home_viewmodel.dart +++ b/lib/ui/views/home/home_viewmodel.dart @@ -15,7 +15,6 @@ class HomeViewModel extends ReactiveViewModel { // Dependency injection final _statusChecker = locator(); - final _bottomSheetService = locator(); final _inAppUpdateService = locator(); @@ -26,7 +25,7 @@ class HomeViewModel extends ReactiveViewModel { @override List get listenableServices => - [_authenticationService,_inAppNotificationService]; + [_authenticationService, _inAppNotificationService]; // Current user User? get _user => _authenticationService.user; @@ -66,7 +65,6 @@ class HomeViewModel extends ReactiveViewModel { } } - // Unread notifications Future getUnreadNotifications() async { if (await _statusChecker.checkConnection()) { diff --git a/lib/ui/views/landing/landing_view.dart b/lib/ui/views/landing/landing_view.dart index 8b18299..30df35a 100644 --- a/lib/ui/views/landing/landing_view.dart +++ b/lib/ui/views/landing/landing_view.dart @@ -53,5 +53,4 @@ class LandingView extends StackedView { Widget _buildSecondLanding() => const SecondLandingScreen(); Widget _buildThirdLanding() => const ThirdLandingScreen(); - } diff --git a/lib/ui/views/learn_module/learn_module_view.dart b/lib/ui/views/learn_module/learn_module_view.dart index 88f5b8c..65360f5 100644 --- a/lib/ui/views/learn_module/learn_module_view.dart +++ b/lib/ui/views/learn_module/learn_module_view.dart @@ -26,7 +26,7 @@ class LearnModuleView extends StackedView { {required BuildContext context, required LearnModule module, required LearnModuleViewModel viewModel}) async { - if (module.access?.isCompleted ?? false ) { + if (module.access?.isCompleted ?? false) { await viewModel.navigateToLearnPractice( id: module.id ?? 0, module: module.name ?? ''); } else { diff --git a/lib/ui/views/learn_practice/learn_practice_view.dart b/lib/ui/views/learn_practice/learn_practice_view.dart index 826742f..7e22e71 100644 --- a/lib/ui/views/learn_practice/learn_practice_view.dart +++ b/lib/ui/views/learn_practice/learn_practice_view.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import 'package:yimaru_app/ui/common/enmus.dart'; import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_finish_screen.dart'; -import 'package:yimaru_app/ui/views/learn_practice/screens/learn_loading_screen.dart'; +import 'package:yimaru_app/ui/widgets/practice_loading_screen.dart'; import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_appreciation_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_description_screen.dart'; @@ -12,7 +12,7 @@ import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_intro_ import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart'; import '../../common/app_colors.dart'; -import '../../widgets/cancel_learn_practice_sheet.dart'; +import '../../widgets/cancel_practice_sheet.dart'; import 'learn_practice_viewmodel.dart'; class LearnPracticeView extends StackedView { @@ -80,8 +80,7 @@ class LearnPracticeView extends StackedView { }, child: _buildScaffoldWrapper(viewModel)); - Widget _buildSheet(LearnPracticeViewModel viewModel) => - CancelLearnPracticeSheet( + Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet( onClose: viewModel.pop, onContinue: viewModel.pop, user: viewModel.user?.firstName ?? '', @@ -101,7 +100,7 @@ class LearnPracticeView extends StackedView { : _buildBody(viewModel); Widget _buildPageLoadingIndicator(LearnPracticeViewModel viewModel) => - LearnLoadingScreen( + PracticeLoadingScreen( isLoading: viewModel.busy(StateObjects.learnPractice), onTap: () async => await viewModel.getLearnPractices(id: id, practice: practice), diff --git a/lib/ui/views/learn_practice/screens/interact_learn_practice_screen.dart b/lib/ui/views/learn_practice/screens/interact_learn_practice_screen.dart index f0e18d4..70bb52d 100644 --- a/lib/ui/views/learn_practice/screens/interact_learn_practice_screen.dart +++ b/lib/ui/views/learn_practice/screens/interact_learn_practice_screen.dart @@ -5,7 +5,7 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:stacked/stacked.dart'; import 'package:waveform_recorder/waveform_recorder.dart'; import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart'; -import 'package:yimaru_app/ui/widgets/cancel_learn_practice_sheet.dart'; +import 'package:yimaru_app/ui/widgets/cancel_practice_sheet.dart'; import 'package:yimaru_app/ui/widgets/custom_circular_progress_indicator.dart'; import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart'; import 'package:yimaru_app/ui/widgets/wave_wrapper.dart'; @@ -365,8 +365,7 @@ class InteractLearnPracticeScreen await _showSheet(context: context, viewModel: viewModel), ); - Widget _buildSheet(LearnPracticeViewModel viewModel) => - CancelLearnPracticeSheet( + Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet( onClose: viewModel.pop, onContinue: viewModel.pop, user: viewModel.user?.firstName ?? '', diff --git a/lib/ui/views/learn_practice/screens/learn_practice_appreciation_screen.dart b/lib/ui/views/learn_practice/screens/learn_practice_appreciation_screen.dart index a430930..184965a 100644 --- a/lib/ui/views/learn_practice/screens/learn_practice_appreciation_screen.dart +++ b/lib/ui/views/learn_practice/screens/learn_practice_appreciation_screen.dart @@ -7,7 +7,7 @@ import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/cancel_learn_practice_sheet.dart'; +import '../../../widgets/cancel_practice_sheet.dart'; import '../../../widgets/custom_elevated_button.dart'; import '../../../widgets/small_app_bar.dart'; @@ -104,8 +104,7 @@ class LearnPracticeAppreciationScreen await _showSheet(context: context, viewModel: viewModel), ); - Widget _buildSheet(LearnPracticeViewModel viewModel) => - CancelLearnPracticeSheet( + Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet( onClose: viewModel.pop, onContinue: viewModel.pop, user: viewModel.user?.firstName ?? '', diff --git a/lib/ui/views/learn_practice/screens/learn_practice_description_screen.dart b/lib/ui/views/learn_practice/screens/learn_practice_description_screen.dart index 6928e7e..76b13de 100644 --- a/lib/ui/views/learn_practice/screens/learn_practice_description_screen.dart +++ b/lib/ui/views/learn_practice/screens/learn_practice_description_screen.dart @@ -8,7 +8,7 @@ import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart import '../../../common/app_colors.dart'; import '../../../common/translations/locale_keys.g.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/cancel_learn_practice_sheet.dart'; +import '../../../widgets/cancel_practice_sheet.dart'; import '../../../widgets/custom_elevated_button.dart'; import '../../../widgets/small_app_bar.dart'; @@ -87,8 +87,7 @@ class LearnPracticeDescriptionScreen await _showSheet(context: context, viewModel: viewModel), ); - Widget _buildSheet(LearnPracticeViewModel viewModel) => - CancelLearnPracticeSheet( + Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet( onClose: viewModel.pop, onContinue: viewModel.pop, user: viewModel.user?.firstName ?? '', diff --git a/lib/ui/views/learn_practice/screens/learn_practice_intro_screen.dart b/lib/ui/views/learn_practice/screens/learn_practice_intro_screen.dart index 427679d..77ea121 100644 --- a/lib/ui/views/learn_practice/screens/learn_practice_intro_screen.dart +++ b/lib/ui/views/learn_practice/screens/learn_practice_intro_screen.dart @@ -7,7 +7,7 @@ import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart import '../../../common/app_colors.dart'; import '../../../common/enmus.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/cancel_learn_practice_sheet.dart'; +import '../../../widgets/cancel_practice_sheet.dart'; import '../../../widgets/custom_elevated_button.dart'; import '../../../widgets/small_app_bar.dart'; import '../../../widgets/speaking_partner_image.dart'; @@ -106,8 +106,7 @@ class LearnPracticeIntroScreen extends ViewModelWidget { await _showSheet(context: context, viewModel: viewModel), ); - Widget _buildSheet(LearnPracticeViewModel viewModel) => - CancelLearnPracticeSheet( + Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet( onClose: viewModel.pop, onContinue: viewModel.pop, user: viewModel.user?.firstName ?? '', diff --git a/lib/ui/views/learn_practice/screens/learn_practice_result_screen.dart b/lib/ui/views/learn_practice/screens/learn_practice_result_screen.dart index f8a0fb3..30a8998 100644 --- a/lib/ui/views/learn_practice/screens/learn_practice_result_screen.dart +++ b/lib/ui/views/learn_practice/screens/learn_practice_result_screen.dart @@ -9,7 +9,7 @@ import 'package:yimaru_app/ui/widgets/learn_practice_results_wrapper.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/cancel_learn_practice_sheet.dart'; +import '../../../widgets/cancel_practice_sheet.dart'; import '../../../widgets/custom_elevated_button.dart'; import '../../../widgets/page_loading_indicator.dart'; import '../../../widgets/small_app_bar.dart'; @@ -120,8 +120,7 @@ class LearnPracticeResultScreen await _showSheet(context: context, viewModel: viewModel), ); - Widget _buildSheet(LearnPracticeViewModel viewModel) => - CancelLearnPracticeSheet( + Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet( onClose: viewModel.pop, onContinue: viewModel.pop, user: viewModel.user?.firstName ?? '', diff --git a/lib/ui/views/learn_practice/screens/start_learn_practice_screen.dart b/lib/ui/views/learn_practice/screens/start_learn_practice_screen.dart index 82e7c86..8c3a4b0 100644 --- a/lib/ui/views/learn_practice/screens/start_learn_practice_screen.dart +++ b/lib/ui/views/learn_practice/screens/start_learn_practice_screen.dart @@ -8,7 +8,7 @@ import 'package:yimaru_app/ui/widgets/custom_column_button.dart'; import '../../../../models/learn_question.dart'; import '../../../common/app_colors.dart'; import '../../../common/ui_helpers.dart'; -import '../../../widgets/cancel_learn_practice_sheet.dart'; +import '../../../widgets/cancel_practice_sheet.dart'; import '../../../widgets/small_app_bar.dart'; class StartLearnPracticeScreen extends ViewModelWidget { @@ -111,8 +111,7 @@ class StartLearnPracticeScreen extends ViewModelWidget { title: '${LocaleKeys.practice_speaking.tr()} ($index/${viewModel.questions.length})'); - Widget _buildSheet(LearnPracticeViewModel viewModel) => - CancelLearnPracticeSheet( + Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet( onClose: viewModel.pop, onContinue: viewModel.pop, user: viewModel.user?.firstName ?? '', diff --git a/lib/ui/views/learn_program/learn_program_viewmodel.dart b/lib/ui/views/learn_program/learn_program_viewmodel.dart index b244f7f..a569895 100644 --- a/lib/ui/views/learn_program/learn_program_viewmodel.dart +++ b/lib/ui/views/learn_program/learn_program_viewmodel.dart @@ -26,7 +26,7 @@ class LearnProgramViewModel extends ReactiveViewModel { @override List get listenableServices => - [_learnService, _authenticationService,_inAppNotificationService]; + [_learnService, _authenticationService, _inAppNotificationService]; // Current user User? get _user => _authenticationService.user; diff --git a/lib/ui/views/notification/notification_view.dart b/lib/ui/views/notification/notification_view.dart index e3bd6f5..e76fc64 100644 --- a/lib/ui/views/notification/notification_view.dart +++ b/lib/ui/views/notification/notification_view.dart @@ -60,8 +60,8 @@ class NotificationView extends StackedView { List _buildColumnChildren(NotificationViewModel viewModel) => [ verticalSpaceMedium, _buildAppBarWrapper(viewModel), - verticalSpaceMedium, - _buildNotificationsColumnWrapper(viewModel) + verticalSpaceMedium, + _buildNotificationsColumnWrapper(viewModel) ]; Widget _buildAppBarWrapper(NotificationViewModel viewModel) => Padding( @@ -75,7 +75,6 @@ class NotificationView extends StackedView { title: LocaleKeys.notifications.tr(), ); - Widget _buildNotificationsColumnWrapper(NotificationViewModel viewModel) => Expanded(child: _buildNotificationsColumnScrollView(viewModel)); @@ -87,26 +86,24 @@ class NotificationView extends StackedView { Widget _buildListViewBuilder(NotificationViewModel viewModel) => viewModel.busy(StateObjects.notifications) ? _buildProgressIndicator() - : _buildListView(viewModel); + : _buildListView(viewModel); Widget _buildProgressIndicator() => const Center( child: CustomCircularProgressIndicator(color: kcPrimaryColor), ); Widget _buildListView(NotificationViewModel viewModel) => ListView.separated( - shrinkWrap: true, - itemCount: viewModel.notifications.length, - physics: const NeverScrollableScrollPhysics(), - separatorBuilder: (context, index) => verticalSpaceSmall, - itemBuilder: (context, index) => _buildCard( - notification: viewModel.notifications[index], - ), - ); + shrinkWrap: true, + itemCount: viewModel.notifications.length, + physics: const NeverScrollableScrollPhysics(), + separatorBuilder: (context, index) => verticalSpaceSmall, + itemBuilder: (context, index) => _buildCard( + notification: viewModel.notifications[index], + ), + ); Widget _buildCard({ required InAppNotification notification, }) => NotificationCard(notification: notification); - - } diff --git a/lib/ui/views/profile/profile_viewmodel.dart b/lib/ui/views/profile/profile_viewmodel.dart index 454e925..d1b0d77 100644 --- a/lib/ui/views/profile/profile_viewmodel.dart +++ b/lib/ui/views/profile/profile_viewmodel.dart @@ -10,7 +10,6 @@ import '../../../app/app.locator.dart'; import '../../../models/user.dart'; import '../../../services/api_service.dart'; import '../../../services/authentication_service.dart'; -import '../../../services/google_auth_service.dart'; import '../../../services/in_app_notification_service.dart'; import '../../../services/status_checker_service.dart'; import '../../common/app_colors.dart'; @@ -32,7 +31,7 @@ class ProfileViewModel extends ReactiveViewModel { @override List get listenableServices => - [_authenticationService,_inAppNotificationService]; + [_authenticationService, _inAppNotificationService]; // Current user User? get _user => _authenticationService.user; diff --git a/lib/ui/views/startup/screens/first_startup_screen.dart b/lib/ui/views/startup/screens/first_startup_screen.dart index 9c4ca74..e118b5b 100644 --- a/lib/ui/views/startup/screens/first_startup_screen.dart +++ b/lib/ui/views/startup/screens/first_startup_screen.dart @@ -5,7 +5,6 @@ import 'package:stacked/stacked.dart'; import 'package:yimaru_app/ui/common/app_colors.dart'; import 'package:yimaru_app/ui/common/ui_helpers.dart'; import 'package:yimaru_app/ui/views/startup/startup_viewmodel.dart'; -import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart'; import '../../../common/translations/locale_keys.g.dart'; import '../../../widgets/custom_circular_progress_indicator.dart'; @@ -13,29 +12,29 @@ import '../../../widgets/custom_circular_progress_indicator.dart'; class FirstStartupScreen extends ViewModelWidget { final String? label; - const FirstStartupScreen({super.key,this.label}); + const FirstStartupScreen({super.key, this.label}); @override Widget build(BuildContext context, StartupViewModel viewModel) => _buildScaffoldWrapper(); - Widget _buildScaffoldWrapper( ) => Scaffold( + Widget _buildScaffoldWrapper() => Scaffold( backgroundColor: kcPrimaryColor, body: _buildScaffoldPadding(), ); - Widget _buildScaffoldPadding( ) => Padding( + Widget _buildScaffoldPadding() => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: _buildScaffold(), ); - Widget _buildScaffold( ) => Column( + Widget _buildScaffold() => Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildScaffoldChildren(), ); - List _buildScaffoldChildren( ) => + List _buildScaffoldChildren() => [_buildUpperColumn(), _buildLowerColumnWrapper()]; Widget _buildUpperColumn() => Column( @@ -57,17 +56,17 @@ class FirstStartupScreen extends ViewModelWidget { height: 25, ); - Widget _buildLowerColumnWrapper( ) => Expanded( + Widget _buildLowerColumnWrapper() => Expanded( child: _buildLowerColumn(), ); - Widget _buildLowerColumn( ) => Column( + Widget _buildLowerColumn() => Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: _buildLowerColumnChildren(), ); - List _buildLowerColumnChildren( ) => [ + List _buildLowerColumnChildren() => [ _buildTitle(), verticalSpaceMedium, _buildImageWrapper(), @@ -108,44 +107,39 @@ class FirstStartupScreen extends ViewModelWidget { fit: BoxFit.cover, ); - Widget _buildSafeWrapper( ) => - SafeArea(child: _buildContinueButtonWrapper()); + Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper()); - Widget _buildContinueButtonWrapper( ) => Align( + Widget _buildContinueButtonWrapper() => Align( alignment: Alignment.bottomCenter, child: _buildLoadingTextContainer(), ); - - Widget _buildLoadingTextContainer() => Padding( - padding: const EdgeInsets.only(bottom: 50), - child: _buildLoadingTextWrapper(), - ); + padding: const EdgeInsets.only(bottom: 50), + child: _buildLoadingTextWrapper(), + ); Widget _buildLoadingTextWrapper() => Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildLoadingTextChildren(), - ); + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: _buildLoadingTextChildren(), + ); List _buildLoadingTextChildren() => [ - _buildLoadingText(), - horizontalSpaceSmall, - _buildIndicatorWrapper(), - ]; + _buildLoadingText(), + horizontalSpaceSmall, + _buildIndicatorWrapper(), + ]; Widget _buildLoadingText() => Text('${label ?? LocaleKeys.loading.tr()} ...', style: style16W600); Widget _buildIndicatorWrapper() => SizedBox( - width: 16, - height: 16, - child: _buildIndicator(), - ); + width: 16, + height: 16, + child: _buildIndicator(), + ); Widget _buildIndicator() => const CustomCircularProgressIndicator(color: kcWhite); - - } diff --git a/lib/ui/views/startup/screens/second_startup_screen.dart b/lib/ui/views/startup/screens/second_startup_screen.dart index 387e79c..1479986 100644 --- a/lib/ui/views/startup/screens/second_startup_screen.dart +++ b/lib/ui/views/startup/screens/second_startup_screen.dart @@ -4,7 +4,6 @@ import 'package:flutter_svg/svg.dart'; import 'package:stacked/stacked.dart'; import 'package:yimaru_app/ui/common/app_colors.dart'; import 'package:yimaru_app/ui/common/ui_helpers.dart'; -import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart'; import '../../../common/translations/locale_keys.g.dart'; import '../../../widgets/custom_circular_progress_indicator.dart'; @@ -13,141 +12,134 @@ import '../startup_viewmodel.dart'; class SecondStartupScreen extends ViewModelWidget { final String? label; - const SecondStartupScreen({super.key,this.label}); + const SecondStartupScreen({super.key, this.label}); @override Widget build(BuildContext context, StartupViewModel viewModel) => _buildScaffoldWrapper(); - Widget _buildScaffoldWrapper( ) => Scaffold( - backgroundColor: Colors.amber, - body: _buildScaffoldPadding(), - ); - Widget _buildScaffoldPadding( ) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildScaffold(), - ); + Widget _buildScaffoldWrapper() => Scaffold( + backgroundColor: Colors.amber, + body: _buildScaffoldPadding(), + ); + Widget _buildScaffoldPadding() => Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _buildScaffold(), + ); - Widget _buildScaffold( ) => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildScaffoldChildren(), - ); + Widget _buildScaffold() => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: _buildScaffoldChildren(), + ); - List _buildScaffoldChildren( ) => + List _buildScaffoldChildren() => [_buildUpperColumn(), _buildLowerColumnWrapper()]; Widget _buildUpperColumn() => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: _buildUpperColumnChildren(), - ); + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: _buildUpperColumnChildren(), + ); List _buildUpperColumnChildren() => [verticalSpaceLarge, _buildIconWrapper(), verticalSpaceLarge]; Widget _buildIconWrapper() => Align( - alignment: Alignment.topLeft, - child: _buildIcon(), - ); + alignment: Alignment.topLeft, + child: _buildIcon(), + ); Widget _buildIcon() => SvgPicture.asset( - 'assets/icons/logo_purple.svg', - height: 25, - ); + 'assets/icons/logo_purple.svg', + height: 25, + ); - Widget _buildLowerColumnWrapper( ) => Expanded( - child: _buildLowerColumn(), - ); + Widget _buildLowerColumnWrapper() => Expanded( + child: _buildLowerColumn(), + ); - Widget _buildLowerColumn( ) => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: _buildLowerColumnChildren(), - ); - - List _buildLowerColumnChildren( ) => [ - _buildTitle(), - verticalSpaceMedium, - _buildImageWrapper(), - verticalSpaceMedium, - _buildSafeWrapper() - ]; + Widget _buildLowerColumn() => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: _buildLowerColumnChildren(), + ); + List _buildLowerColumnChildren() => [ + _buildTitle(), + verticalSpaceMedium, + _buildImageWrapper(), + verticalSpaceMedium, + _buildSafeWrapper() + ]; Widget _buildTitle() => Text.rich( - TextSpan( - text: 'እንግሊዝኛ\n', - style: style25P600, - children: [ TextSpan( - text: 'በማንኛውም', - style: style25P400, - ), - TextSpan( - text: ' እድሜ ', + text: 'እንግሊዝኛ\n', style: style25P600, + children: [ + TextSpan( + text: 'በማንኛውም', + style: style25P400, + ), + TextSpan( + text: ' እድሜ ', + style: style25P600, + ), + TextSpan( + text: 'ይማሩ!', + style: style25P400, + ), + ], ), - TextSpan( - text: 'ይማሩ!', - style: style25P400, - ), - ], - ), - ); + ); Widget _buildImageWrapper() => Expanded(child: _buildImageClipper()); Widget _buildImageClipper() => ClipRRect( - borderRadius: BorderRadius.circular(25), - child: _buildImage(), - ); + borderRadius: BorderRadius.circular(25), + child: _buildImage(), + ); Widget _buildImage() => Image.asset( - 'assets/images/landing_2.png', - fit: BoxFit.cover, - ); - - Widget _buildSafeWrapper( ) => - SafeArea(child: _buildContinueButtonWrapper()); - - Widget _buildContinueButtonWrapper( ) => Align( - alignment: Alignment.bottomCenter, - child: _buildLoadingTextContainer(), - ); + 'assets/images/landing_2.png', + fit: BoxFit.cover, + ); + Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper()); + Widget _buildContinueButtonWrapper() => Align( + alignment: Alignment.bottomCenter, + child: _buildLoadingTextContainer(), + ); Widget _buildLoadingTextContainer() => Padding( - padding: const EdgeInsets.only(bottom: 50), - child: _buildLoadingTextWrapper(), - ); + padding: const EdgeInsets.only(bottom: 50), + child: _buildLoadingTextWrapper(), + ); Widget _buildLoadingTextWrapper() => Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildLoadingTextChildren(), - ); + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: _buildLoadingTextChildren(), + ); List _buildLoadingTextChildren() => [ - _buildLoadingText(), - horizontalSpaceSmall, - _buildIndicatorWrapper(), - ]; + _buildLoadingText(), + horizontalSpaceSmall, + _buildIndicatorWrapper(), + ]; Widget _buildLoadingText() => Text('${label ?? LocaleKeys.loading.tr()} ...', style: style16P600); Widget _buildIndicatorWrapper() => SizedBox( - width: 16, - height: 16, - child: _buildIndicator(), - ); + width: 16, + height: 16, + child: _buildIndicator(), + ); Widget _buildIndicator() => const CustomCircularProgressIndicator(color: kcPrimaryColor); - - } - diff --git a/lib/ui/views/startup/screens/third_startup_screen.dart b/lib/ui/views/startup/screens/third_startup_screen.dart index ad988d5..defac97 100644 --- a/lib/ui/views/startup/screens/third_startup_screen.dart +++ b/lib/ui/views/startup/screens/third_startup_screen.dart @@ -4,7 +4,6 @@ import 'package:flutter_svg/svg.dart'; import 'package:stacked/stacked.dart'; import 'package:yimaru_app/ui/common/app_colors.dart'; import 'package:yimaru_app/ui/common/ui_helpers.dart'; -import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart'; import '../../../common/translations/locale_keys.g.dart'; import '../../../widgets/custom_circular_progress_indicator.dart'; @@ -13,141 +12,134 @@ import '../startup_viewmodel.dart'; class ThirdStartupScreen extends ViewModelWidget { final String? label; - const ThirdStartupScreen({super.key,this.label}); + const ThirdStartupScreen({super.key, this.label}); @override Widget build(BuildContext context, StartupViewModel viewModel) => _buildScaffoldWrapper(); - Widget _buildScaffoldWrapper( ) => Scaffold( - backgroundColor:kcWhite, - body: _buildScaffoldPadding(), - ); - Widget _buildScaffoldPadding( ) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildScaffold(), - ); + Widget _buildScaffoldWrapper() => Scaffold( + backgroundColor: kcWhite, + body: _buildScaffoldPadding(), + ); + Widget _buildScaffoldPadding() => Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _buildScaffold(), + ); - Widget _buildScaffold( ) => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildScaffoldChildren(), - ); + Widget _buildScaffold() => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: _buildScaffoldChildren(), + ); - List _buildScaffoldChildren( ) => + List _buildScaffoldChildren() => [_buildUpperColumn(), _buildLowerColumnWrapper()]; Widget _buildUpperColumn() => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: _buildUpperColumnChildren(), - ); + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: _buildUpperColumnChildren(), + ); List _buildUpperColumnChildren() => [verticalSpaceLarge, _buildIconWrapper(), verticalSpaceLarge]; Widget _buildIconWrapper() => Align( - alignment: Alignment.topLeft, - child: _buildIcon(), - ); + alignment: Alignment.topLeft, + child: _buildIcon(), + ); Widget _buildIcon() => SvgPicture.asset( - 'assets/icons/logo_purple.svg', - height: 25, - ); + 'assets/icons/logo_purple.svg', + height: 25, + ); - Widget _buildLowerColumnWrapper( ) => Expanded( - child: _buildLowerColumn(), - ); + Widget _buildLowerColumnWrapper() => Expanded( + child: _buildLowerColumn(), + ); - Widget _buildLowerColumn( ) => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: _buildLowerColumnChildren(), - ); - - List _buildLowerColumnChildren( ) => [ - _buildTitle(), - verticalSpaceMedium, - _buildImageWrapper(), - verticalSpaceMedium, - _buildSafeWrapper() - ]; + Widget _buildLowerColumn() => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: _buildLowerColumnChildren(), + ); + List _buildLowerColumnChildren() => [ + _buildTitle(), + verticalSpaceMedium, + _buildImageWrapper(), + verticalSpaceMedium, + _buildSafeWrapper() + ]; Widget _buildTitle() => Text.rich( - TextSpan( - text: 'እንግሊዝኛ\n', - style: style25P600, - children: [ TextSpan( - text: 'በማንኛውም', - style: style25P400, - ), - TextSpan( - text: ' ቦታ ', + text: 'እንግሊዝኛ\n', style: style25P600, + children: [ + TextSpan( + text: 'በማንኛውም', + style: style25P400, + ), + TextSpan( + text: ' ቦታ ', + style: style25P600, + ), + TextSpan( + text: 'ይማሩ!', + style: style25P400, + ), + ], ), - TextSpan( - text: 'ይማሩ!', - style: style25P400, - ), - ], - ), - ); + ); Widget _buildImageWrapper() => Expanded(child: _buildImageClipper()); Widget _buildImageClipper() => ClipRRect( - borderRadius: BorderRadius.circular(25), - child: _buildImage(), - ); + borderRadius: BorderRadius.circular(25), + child: _buildImage(), + ); Widget _buildImage() => Image.asset( - 'assets/images/landing_3.png', - fit: BoxFit.cover, - ); - - Widget _buildSafeWrapper( ) => - SafeArea(child: _buildContinueButtonWrapper()); - - Widget _buildContinueButtonWrapper( ) => Align( - alignment: Alignment.bottomCenter, - child: _buildLoadingTextContainer(), - ); + 'assets/images/landing_3.png', + fit: BoxFit.cover, + ); + Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper()); + Widget _buildContinueButtonWrapper() => Align( + alignment: Alignment.bottomCenter, + child: _buildLoadingTextContainer(), + ); Widget _buildLoadingTextContainer() => Padding( - padding: const EdgeInsets.only(bottom: 50), - child: _buildLoadingTextWrapper(), - ); + padding: const EdgeInsets.only(bottom: 50), + child: _buildLoadingTextWrapper(), + ); Widget _buildLoadingTextWrapper() => Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildLoadingTextChildren(), - ); + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: _buildLoadingTextChildren(), + ); List _buildLoadingTextChildren() => [ - _buildLoadingText(), - horizontalSpaceSmall, - _buildIndicatorWrapper(), - ]; + _buildLoadingText(), + horizontalSpaceSmall, + _buildIndicatorWrapper(), + ]; Widget _buildLoadingText() => Text('${label ?? LocaleKeys.loading.tr()} ...', style: style16P600); Widget _buildIndicatorWrapper() => SizedBox( - width: 16, - height: 16, - child: _buildIndicator(), - ); + width: 16, + height: 16, + child: _buildIndicator(), + ); Widget _buildIndicator() => const CustomCircularProgressIndicator(color: kcPrimaryColor); - - } - diff --git a/lib/ui/views/startup/startup_view.dart b/lib/ui/views/startup/startup_view.dart index a79078f..81863df 100644 --- a/lib/ui/views/startup/startup_view.dart +++ b/lib/ui/views/startup/startup_view.dart @@ -1,18 +1,12 @@ -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_carousel_widget/flutter_carousel_widget.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/common/ui_helpers.dart'; import 'package:yimaru_app/ui/views/startup/screens/first_startup_screen.dart'; import 'package:yimaru_app/ui/views/startup/screens/second_startup_screen.dart'; import 'package:yimaru_app/ui/views/startup/screens/third_startup_screen.dart'; -import 'package:yimaru_app/ui/widgets/custom_circular_progress_indicator.dart'; import '../../common/app_colors.dart'; -import '../../common/enmus.dart'; -import '../../common/translations/locale_keys.g.dart'; import 'startup_viewmodel.dart'; class StartupView extends StackedView { diff --git a/lib/ui/views/startup/startup_viewmodel.dart b/lib/ui/views/startup/startup_viewmodel.dart index dab3195..012781b 100644 --- a/lib/ui/views/startup/startup_viewmodel.dart +++ b/lib/ui/views/startup/startup_viewmodel.dart @@ -8,7 +8,6 @@ import '../../../app/app.router.dart'; import '../../../models/user.dart'; import '../../../services/api_service.dart'; import '../../../services/image_downloader_service.dart'; -import '../../../services/in_app_notification_service.dart'; import '../../../services/localization_service.dart'; import '../../../services/status_checker_service.dart'; import '../../common/enmus.dart'; @@ -29,7 +28,6 @@ class StartupViewModel extends ReactiveViewModel { final _imageDownloaderService = locator(); - @override List get listenableServices => [_onboardingService, _authenticationService]; @@ -144,6 +142,4 @@ class StartupViewModel extends ReactiveViewModel { await replaceWithFailure(); } } - - } diff --git a/lib/ui/widgets/cancel_learn_practice_sheet.dart b/lib/ui/widgets/cancel_practice_sheet.dart similarity index 96% rename from lib/ui/widgets/cancel_learn_practice_sheet.dart rename to lib/ui/widgets/cancel_practice_sheet.dart index 9dacb96..bb55950 100644 --- a/lib/ui/widgets/cancel_learn_practice_sheet.dart +++ b/lib/ui/widgets/cancel_practice_sheet.dart @@ -8,13 +8,13 @@ import '../common/ui_helpers.dart'; import 'custom_bottom_sheet.dart'; import 'custom_elevated_button.dart'; -class CancelLearnPracticeSheet extends StatelessWidget { +class CancelPracticeSheet extends StatelessWidget { final String user; final GestureTapCallback? onClose; final GestureTapCallback? onCancel; final GestureTapCallback? onContinue; - const CancelLearnPracticeSheet( + const CancelPracticeSheet( {super.key, this.onClose, this.onCancel, diff --git a/lib/ui/widgets/countdown_timer.dart b/lib/ui/widgets/countdown_timer.dart index 0fbdf82..533e324 100644 --- a/lib/ui/widgets/countdown_timer.dart +++ b/lib/ui/widgets/countdown_timer.dart @@ -1,40 +1,76 @@ import 'package:flutter/material.dart'; +import 'package:flutter_timer_countdown/flutter_timer_countdown.dart'; +import 'package:stacked/stacked.dart'; import 'package:yimaru_app/ui/common/app_colors.dart'; +import 'package:yimaru_app/ui/views/course_practice/course_practice_viewmodel.dart'; +import '../common/helper_functions.dart'; import '../common/ui_helpers.dart'; -class CountdownTimer extends StatelessWidget { - const CountdownTimer({super.key}); +class CountdownTimer extends ViewModelWidget { + final String? time; + const CountdownTimer({super.key, this.time}); + Future _stopRecording(CoursePracticeViewModel viewModel)async{ + await viewModel.stopRecording(); + viewModel.setNextButton(); + } @override - Widget build(BuildContext context) => _buildContainer(); + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => + _buildContainer(viewModel); - Widget _buildContainer() => Container( - width: 100, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(25), - color: kcPrimaryColor.withValues(alpha: 0.1), + Widget _buildContainer(CoursePracticeViewModel viewModel) => Container( + width: 100, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(25), + color: kcPrimaryColor.withValues(alpha: 0.1), + ), + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15), + child: _buildRow(viewModel), + ); + + Widget _buildRow(CoursePracticeViewModel viewModel) => Row( + children: [ + _buildClockIcon(), + horizontalSpaceTiny, + _buildCountdownState(viewModel), + ], + ); + + + Widget _buildCountdownState(CoursePracticeViewModel viewModel) => time == null ? _buildCountdownTime(viewModel):_buildCountdownText(); + + Widget _buildCountdownText() => Text( + time ?? '', + style: style16P600, + textAlign: TextAlign.center, + ); + + Widget _buildCountdownTime(CoursePracticeViewModel viewModel) => + TimerCountdown( + spacerWidth: 0, + enableDescriptions: false, + timeTextStyle: style14P600, + colonsTextStyle: style14P400, + format: CountDownTimerFormat.minutesSeconds, + onEnd:()async => await _stopRecording(viewModel) , + endTime: DateTime.now().add( + Duration( + minutes: getMinutes( + viewModel.questions[viewModel.currentQuestion].dynamicPayload + ?.stimulus?.last.value['seconds'], + ), + seconds: getSeconds( + viewModel.questions[viewModel.currentQuestion].dynamicPayload + ?.stimulus?.last.value['seconds'], + ), + ), ), - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15), - child: _buildRow(), - ); - - Widget _buildRow() => Row( - children: [ - _buildClockIcon(), - horizontalSpaceTiny, - _buildCountdownTime(), - ], - ); - - Widget _buildCountdownTime() => Text( - '0:20', - style: style16P600, - textAlign: TextAlign.center, ); Widget _buildClockIcon() => const Icon( - Icons.timer_outlined, - color: kcPrimaryColor, - ); + Icons.timer_outlined, + color: kcPrimaryColor, + ); } + diff --git a/lib/ui/widgets/course_module_tile_large.dart b/lib/ui/widgets/course_module_tile_large.dart index 78ad57b..bf0a66f 100644 --- a/lib/ui/widgets/course_module_tile_large.dart +++ b/lib/ui/widgets/course_module_tile_large.dart @@ -191,14 +191,14 @@ class CourseModuleTileLarge extends ViewModelWidget { shrinkWrap: true, itemCount: lessons.length, physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) => _buildCourseModuleCard( + itemBuilder: (context, index) => _buildCourseLessonTile( lesson: lessons[index], onVideoTap: () async => await viewModel.navigateToCourseLessonDetail(lessons[index]), - onPracticeTap: () {}), + onPracticeTap: ()async=>await viewModel.navigateToCoursePractice(lessons[index].id ?? 0)), ); - Widget _buildCourseModuleCard({ + Widget _buildCourseLessonTile({ required CourseLesson lesson, required GestureTapCallback onVideoTap, required GestureTapCallback onPracticeTap, diff --git a/lib/ui/widgets/custom_response_card.dart b/lib/ui/widgets/custom_response_card.dart index c36b9db..38ec321 100644 --- a/lib/ui/widgets/custom_response_card.dart +++ b/lib/ui/widgets/custom_response_card.dart @@ -1,68 +1,89 @@ +import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; import 'package:yimaru_app/ui/common/app_colors.dart'; import 'package:yimaru_app/ui/common/ui_helpers.dart'; +import 'package:yimaru_app/ui/views/course_practice/course_practice_viewmodel.dart'; -class CustomResponseCard extends StatelessWidget { +import '../common/enmus.dart'; +import 'custom_circular_progress_indicator.dart'; + +class CustomResponseCard extends ViewModelWidget { + final Voice voice; final String title; - final String subtitle; const CustomResponseCard( - {super.key, required this.title, required this.subtitle}); + {super.key, + required this.voice, + required this.title, + }); @override - Widget build(BuildContext context) => _buildContainer(); + Widget build(BuildContext context, CoursePracticeViewModel viewModel) => + _buildContainer(viewModel); - Widget _buildContainer() => Container( - decoration: BoxDecoration( - color: kcWhite, - borderRadius: BorderRadius.circular(12), - ), - padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), - child: _buildRow(), - ); + Widget _buildContainer(CoursePracticeViewModel viewModel) => Container( + decoration: BoxDecoration( + color: kcWhite, + borderRadius: BorderRadius.circular(12), + ), + padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), + child: _buildRow(viewModel), + ); - Widget _buildRow() => Row( - children: [_buildPlayButton(), _buildColumnWrapper()], - ); + Widget _buildRow(CoursePracticeViewModel viewModel) => Row( + children: [_buildPlayButton(viewModel), _buildColumnWrapper()], + ); - Widget _buildPlayButton() => ElevatedButton( - onPressed: () {}, - style: const ButtonStyle( - shape: WidgetStatePropertyAll(CircleBorder()), - padding: WidgetStatePropertyAll(EdgeInsets.all(5)), - shadowColor: WidgetStatePropertyAll(kcPrimaryColor), - backgroundColor: WidgetStatePropertyAll(kcPrimaryColor), - ), - child: _buildPlayIcon(), - ); + Widget _buildPlayButton(CoursePracticeViewModel viewModel) => ElevatedButton( + style: const ButtonStyle( + shape: WidgetStatePropertyAll(CircleBorder()), + padding: WidgetStatePropertyAll(EdgeInsets.all(5)), + shadowColor: WidgetStatePropertyAll(kcPrimaryColor), + backgroundColor: WidgetStatePropertyAll(kcPrimaryColor), + ), + onPressed: () async => + viewModel.player.state == PlayerState.playing + ? await viewModel.pauseAudio() + : await viewModel.playResult( voice), + child: _buildButtonState(viewModel), + ); + + Widget _buildButtonState(CoursePracticeViewModel viewModel) => + + viewModel.playing == voice + ? viewModel.busy(StateObjects.coursePracticeReview) + ? _buildProgressIndicatorWrapper() + : viewModel.player.state == PlayerState.playing + ? _buildPauseIcon() + : _buildPlayIcon() + : _buildPlayIcon(); + + Widget _buildProgressIndicatorWrapper() => Center( + child: _buildProgressIndicator(), + ); + + Widget _buildProgressIndicator() => + const CustomCircularProgressIndicator(color: kcWhite); Widget _buildPlayIcon() => const Icon( - Icons.play_arrow_rounded, - size: 25, - color: kcWhite, - ); + Icons.play_arrow_rounded, + size: 25, + color: kcWhite, + ); - Widget _buildColumnWrapper() => Expanded(child: _buildColumn()); + Widget _buildPauseIcon() => const Icon( + Icons.pause, + size: 25, + color: kcWhite, + ); - Widget _buildColumn() => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildColumnChildren(), - ); - - List _buildColumnChildren() => [_buildTitle(), _buildSubtitle()]; + Widget _buildColumnWrapper() => Expanded(child: _buildTitle()); Widget _buildTitle() => Text( - title, - maxLines: 1, - softWrap: false, - style: style12RP600, - ); - - Widget _buildSubtitle() => Text( - subtitle, - maxLines: 1, - softWrap: false, - style: style12RP600, - ); + title, + maxLines: 1, + softWrap: false, + style: style12RP600, + ); } diff --git a/lib/ui/widgets/duolingo_assessment_app_bar.dart b/lib/ui/widgets/duolingo_practice_app_bar.dart similarity index 90% rename from lib/ui/widgets/duolingo_assessment_app_bar.dart rename to lib/ui/widgets/duolingo_practice_app_bar.dart index e91d0ea..d138ed2 100644 --- a/lib/ui/widgets/duolingo_assessment_app_bar.dart +++ b/lib/ui/widgets/duolingo_practice_app_bar.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:yimaru_app/ui/common/app_colors.dart'; -class DuolingoAssessmentAppBar extends StatelessWidget { +class DuolingoPracticeAppBar extends StatelessWidget { final String? title; final GestureTapCallback? onClose; - const DuolingoAssessmentAppBar({super.key, this.onClose, this.title}); + const DuolingoPracticeAppBar({super.key, this.onClose, this.title}); @override Widget build(BuildContext context) => _buildAppBar(); diff --git a/lib/ui/widgets/duolingo_assessment_card.dart b/lib/ui/widgets/duolingo_practice_card.dart similarity index 91% rename from lib/ui/widgets/duolingo_assessment_card.dart rename to lib/ui/widgets/duolingo_practice_card.dart index 3a1fc41..e986857 100644 --- a/lib/ui/widgets/duolingo_assessment_card.dart +++ b/lib/ui/widgets/duolingo_practice_card.dart @@ -4,11 +4,11 @@ import 'package:yimaru_app/ui/common/app_colors.dart'; import '../common/ui_helpers.dart'; import 'custom_elevated_button.dart'; -class DuolingoAssessmentCard extends StatelessWidget { +class DuolingoPracticeCard extends StatelessWidget { final String title; final GestureTapCallback? onTap; - const DuolingoAssessmentCard({super.key, this.onTap, required this.title}); + const DuolingoPracticeCard({super.key, this.onTap, required this.title}); @override Widget build(BuildContext context) => _buildContainer(); diff --git a/lib/ui/widgets/duolingo_assessment_question_card.dart b/lib/ui/widgets/duolingo_practice_question_card.dart similarity index 88% rename from lib/ui/widgets/duolingo_assessment_question_card.dart rename to lib/ui/widgets/duolingo_practice_question_card.dart index 3350dc3..2b3b774 100644 --- a/lib/ui/widgets/duolingo_assessment_question_card.dart +++ b/lib/ui/widgets/duolingo_practice_question_card.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:yimaru_app/ui/common/app_colors.dart'; import 'package:yimaru_app/ui/common/ui_helpers.dart'; -class DuolingoAssessmentQuestionCard extends StatelessWidget { +class DuolingoPracticeQuestionCard extends StatelessWidget { final String? title; final String? subtitle; - const DuolingoAssessmentQuestionCard({super.key, this.title, this.subtitle}); + const DuolingoPracticeQuestionCard({super.key, this.title, this.subtitle}); @override Widget build(BuildContext context) => _buildContainer(); diff --git a/lib/ui/widgets/duolingo_assessment_review_section.dart b/lib/ui/widgets/duolingo_practice_review_section.dart similarity index 91% rename from lib/ui/widgets/duolingo_assessment_review_section.dart rename to lib/ui/widgets/duolingo_practice_review_section.dart index f17ac4f..603dcf2 100644 --- a/lib/ui/widgets/duolingo_assessment_review_section.dart +++ b/lib/ui/widgets/duolingo_practice_review_section.dart @@ -4,9 +4,9 @@ import '../common/app_colors.dart'; import '../common/ui_helpers.dart'; import 'custom_elevated_button.dart'; -class DuolingoAssessmentReviewSection extends StatelessWidget { +class DuolingoPracticeReviewSection extends StatelessWidget { final GestureTapCallback? onTap; - const DuolingoAssessmentReviewSection({super.key, this.onTap}); + const DuolingoPracticeReviewSection({super.key, this.onTap}); @override Widget build(BuildContext context) => _buildReviewContainer(); diff --git a/lib/ui/widgets/learn_practice_answer_card.dart b/lib/ui/widgets/learn_practice_answer_card.dart index 67cccb3..41fe0de 100644 --- a/lib/ui/widgets/learn_practice_answer_card.dart +++ b/lib/ui/widgets/learn_practice_answer_card.dart @@ -43,7 +43,7 @@ class LearnPracticeAnswerCard extends ViewModelWidget { shadowColor: WidgetStatePropertyAll(kcPrimaryColor), backgroundColor: WidgetStatePropertyAll(kcPrimaryColor), ), - onPressed: () async => viewModel.busyObject == answer['busy_object'] && + onPressed: () async => viewModel.player.state == PlayerState.playing ? await viewModel.pauseAudio() : await viewModel.playResult(answer: answer, voice: voice), @@ -51,7 +51,7 @@ class LearnPracticeAnswerCard extends ViewModelWidget { ); Widget _buildButtonState(LearnPracticeViewModel viewModel) => - viewModel.busyObject == answer['busy_object'] && + viewModel.playing == voice ? viewModel.busy(answer['busy_object']) ? _buildProgressIndicatorWrapper() diff --git a/lib/ui/widgets/listenable_assessment_card.dart b/lib/ui/widgets/listenable_practice_card.dart similarity index 96% rename from lib/ui/widgets/listenable_assessment_card.dart rename to lib/ui/widgets/listenable_practice_card.dart index 5222156..e968b70 100644 --- a/lib/ui/widgets/listenable_assessment_card.dart +++ b/lib/ui/widgets/listenable_practice_card.dart @@ -5,8 +5,8 @@ import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart'; import '../common/app_colors.dart'; -class ListenableAssessmentCard extends StatelessWidget { - const ListenableAssessmentCard({super.key}); +class ListenablePracticeCard extends StatelessWidget { + const ListenablePracticeCard({super.key}); @override Widget build(BuildContext context) => _buildRow(); diff --git a/lib/ui/widgets/notification_card.dart b/lib/ui/widgets/notification_card.dart index a63239f..ade9c7b 100644 --- a/lib/ui/widgets/notification_card.dart +++ b/lib/ui/widgets/notification_card.dart @@ -11,13 +11,11 @@ class NotificationCard extends StatelessWidget { @override Widget build(BuildContext context) => _buildContainer(); - - Widget _buildContainer() => Container( - height: 100, - width: double.maxFinite, - margin: const EdgeInsets.symmetric(horizontal: 15), - padding: const EdgeInsets.symmetric(horizontal: 15,vertical: 15), + height: 100, + width: double.maxFinite, + margin: const EdgeInsets.symmetric(horizontal: 15), + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), color: (notification.isRead ?? false) @@ -33,15 +31,12 @@ class NotificationCard extends StatelessWidget { ); Widget _buildRow() => Row( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: _buildRowChildren(), ); - List _buildRowChildren()=> [ - _buildIcon(), - horizontalSpaceSmall, - _buildColumnWrapper() - ]; + List _buildRowChildren() => + [_buildIcon(), horizontalSpaceSmall, _buildColumnWrapper()]; Widget _buildIcon() => const Icon( Icons.notifications_none, @@ -49,15 +44,14 @@ class NotificationCard extends StatelessWidget { color: kcMediumGrey, ); - Widget _buildColumnWrapper()=> Expanded(child: _buildColumn()); + Widget _buildColumnWrapper() => Expanded(child: _buildColumn()); Widget _buildColumn() => Column( crossAxisAlignment: CrossAxisAlignment.start, children: _buildColumnChildren(), ); - List _buildColumnChildren() => - [ _buildTitle(), _buildSubtitle()]; + List _buildColumnChildren() => [_buildTitle(), _buildSubtitle()]; Widget _buildTitle() => Text( notification.payload?.headline ?? '', diff --git a/lib/ui/widgets/notification_icon.dart b/lib/ui/widgets/notification_icon.dart index a2b8814..ac44d4c 100644 --- a/lib/ui/widgets/notification_icon.dart +++ b/lib/ui/widgets/notification_icon.dart @@ -1,34 +1,33 @@ import 'package:badges/badges.dart' as badges; import 'package:flutter/material.dart'; -import 'package:badges/badges.dart'; import 'package:yimaru_app/ui/common/ui_helpers.dart'; import '../common/app_colors.dart'; + class NotificationIcon extends StatelessWidget { final String count; final GestureTapCallback? onTap; - const NotificationIcon({super.key,this.onTap,required this.count}); + const NotificationIcon({super.key, this.onTap, required this.count}); @override Widget build(BuildContext context) => _buildNotificationIconWrapper(); - Widget _buildNotificationIconWrapper() => Align( - alignment: Alignment.bottomRight, - child: _buildNotificationButton()); + alignment: Alignment.bottomRight, child: _buildNotificationButton()); - Widget _buildNotificationButton() => - GestureDetector( + Widget _buildNotificationButton() => GestureDetector( onTap: onTap, child: _buildNotificationBadge(), ); - Widget _buildNotificationBadge()=> badges.Badge( - badgeContent: Text(count,style: style12W600,), - - child: _buildNotificationIcon(), - ); + Widget _buildNotificationBadge() => badges.Badge( + badgeContent: Text( + count, + style: style12W600, + ), + child: _buildNotificationIcon(), + ); Widget _buildNotificationIcon() => const Icon( - Icons.notifications_none, - color: kcDarkGrey, - ); + Icons.notifications_none, + color: kcDarkGrey, + ); } diff --git a/lib/ui/views/learn_practice/screens/learn_loading_screen.dart b/lib/ui/widgets/practice_loading_screen.dart similarity index 89% rename from lib/ui/views/learn_practice/screens/learn_loading_screen.dart rename to lib/ui/widgets/practice_loading_screen.dart index b264998..445fe5b 100644 --- a/lib/ui/views/learn_practice/screens/learn_loading_screen.dart +++ b/lib/ui/widgets/practice_loading_screen.dart @@ -4,17 +4,17 @@ 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/translations/locale_keys.g.dart'; -import '../../../common/ui_helpers.dart'; +import '../common/app_colors.dart'; +import '../common/translations/locale_keys.g.dart'; +import '../common/ui_helpers.dart'; -class LearnLoadingScreen extends StatelessWidget { +class PracticeLoadingScreen extends StatelessWidget { final bool isEmpty; final bool isLoading; final GestureTapCallback? onPop; final GestureTapCallback? onTap; - const LearnLoadingScreen( + const PracticeLoadingScreen( {super.key, this.onTap, this.onPop, diff --git a/lib/ui/widgets/practice_result_card.dart b/lib/ui/widgets/practice_result_card.dart deleted file mode 100644 index 1bafcbe..0000000 --- a/lib/ui/widgets/practice_result_card.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stacked/stacked.dart'; -import 'package:yimaru_app/ui/common/ui_helpers.dart'; -import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart'; -import 'package:yimaru_app/ui/widgets/custom_response_card.dart'; - -class PracticeResultCard extends ViewModelWidget { - final Map data; - const PracticeResultCard({super.key, required this.data}); - - @override - Widget build(BuildContext context, LearnPracticeViewModel viewModel) => - _buildColumnWrapper(); - - Widget _buildColumnWrapper() => SizedBox( - height: 100, - width: double.maxFinite, - child: _buildColumn(), - ); - - Widget _buildColumn() => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildColumnChildren(), - ); - - List _buildColumnChildren() => - [_buildQuestion(), verticalSpaceSmall, _buildRow()]; - - Widget _buildQuestion() => Text( - data['question_text'], - style: style14DG400, - ); - - Widget _buildRow() => Row( - children: _buildRowChildren(), - ); - - List _buildRowChildren() => [ - _buildSampleResponseWrapper(), - horizontalSpaceSmall, - _buildActualResponseWrapper() - ]; - - Widget _buildSampleResponseWrapper() => - Expanded(child: _buildSampleResponse()); - - Widget _buildSampleResponse() => - const CustomResponseCard(title: 'Sample Answer', subtitle: '0:54'); - - Widget _buildActualResponseWrapper() => - Expanded(child: _buildActualResponse()); - - Widget _buildActualResponse() => - const CustomResponseCard(title: 'Sample Answer', subtitle: '0:54'); -} diff --git a/lib/ui/widgets/profile_app_bar.dart b/lib/ui/widgets/profile_app_bar.dart index 0a500d6..0d9c57f 100644 --- a/lib/ui/widgets/profile_app_bar.dart +++ b/lib/ui/widgets/profile_app_bar.dart @@ -16,7 +16,11 @@ class ProfileAppBar extends StatelessWidget { final GestureTapCallback? onTap; const ProfileAppBar( - {super.key, this.onTap, required this.name,required this.unreadCount, required this.profileImage}); + {super.key, + this.onTap, + required this.name, + required this.unreadCount, + required this.profileImage}); @override Widget build(BuildContext context) => _buildStack(); @@ -94,8 +98,8 @@ class ProfileAppBar extends StatelessWidget { style: style14DG400, ); - - Widget _buildNotificationIconWrapper() => - NotificationIcon(count: unreadCount,onTap:onTap ,) - ; + Widget _buildNotificationIconWrapper() => NotificationIcon( + count: unreadCount, + onTap: onTap, + ); } diff --git a/lib/ui/widgets/speaking_assessment_review_section.dart b/lib/ui/widgets/speaking_practice_review_section.dart similarity index 78% rename from lib/ui/widgets/speaking_assessment_review_section.dart rename to lib/ui/widgets/speaking_practice_review_section.dart index c8bec4b..16d18b3 100644 --- a/lib/ui/widgets/speaking_assessment_review_section.dart +++ b/lib/ui/widgets/speaking_practice_review_section.dart @@ -2,12 +2,13 @@ import 'package:flutter/material.dart'; import 'package:yimaru_app/ui/widgets/custom_response_card.dart'; import '../common/app_colors.dart'; +import '../common/enmus.dart'; import '../common/ui_helpers.dart'; import 'custom_elevated_button.dart'; -class SpeakingAssessmentReviewSection extends StatelessWidget { +class SpeakingPracticeReviewSection extends StatelessWidget { final GestureTapCallback? onTap; - const SpeakingAssessmentReviewSection({super.key, this.onTap}); + const SpeakingPracticeReviewSection({super.key, this.onTap}); @override Widget build(BuildContext context) => _buildReviewContainer(); @@ -38,10 +39,14 @@ class SpeakingAssessmentReviewSection extends StatelessWidget { ); Widget _buildSampleAnswer() => - const CustomResponseCard(title: 'Sample Answer', subtitle: '0:54'); + const CustomResponseCard( + title: 'Sample Answer', voice: Voice.sample, + ); Widget _buildYourResponse() => - const CustomResponseCard(title: 'Your Response', subtitle: '0:54'); + const CustomResponseCard( + title: 'Your Response', voice: Voice.recorded, + ); Widget _buildContinueButton() => CustomElevatedButton( height: 55, diff --git a/test/viewmodels/course_practice_viewmodel_test.dart b/test/viewmodels/course_practice_viewmodel_test.dart new file mode 100644 index 0000000..7068e34 --- /dev/null +++ b/test/viewmodels/course_practice_viewmodel_test.dart @@ -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('CoursePracticeViewModel Tests -', () { + setUp(() => registerServices()); + tearDown(() => locator.reset()); + }); +}