From e917209d104b09766d7822c7d880aeda63f5f9b8 Mon Sep 17 00:00:00 2001 From: BisratHailu Date: Sat, 9 May 2026 01:04:53 +0300 Subject: [PATCH] fix: Apply UAT comments --- assets/images/pattern.png | Bin 0 -> 16510 bytes lib/app/app.router.dart | 89 ++++- lib/services/dio_service.dart | 1 + lib/services/phone_caller_service.dart | 4 +- lib/ui/common/app_constants.dart | 2 +- lib/ui/common/app_strings.dart | 1 - lib/ui/common/ui_helpers.dart | 12 +- .../screens/assessment_questions_screen.dart | 86 ++--- .../views/call_support/call_support_view.dart | 8 +- .../call_support/call_support_viewmodel.dart | 7 + .../forget_password_viewmodel.dart | 24 +- .../screens/reset_password_screen.dart | 18 - .../views/learn_course/learn_course_view.dart | 5 +- .../learn_course/learn_course_viewmodel.dart | 12 +- .../views/learn_lesson/learn_lesson_view.dart | 13 +- .../learn_lesson/learn_lesson_viewmodel.dart | 17 +- .../learn_lesson_detail_view.dart | 14 +- .../learn_lesson_detail_viewmodel.dart | 11 +- .../views/learn_module/learn_module_view.dart | 5 +- .../learn_module/learn_module_viewmodel.dart | 11 +- .../learn_practice/learn_practice_view.dart | 31 +- .../learn_practice_viewmodel.dart | 2 +- .../learn_practice_completion_screen.dart | 5 +- .../screens/learn_practice_intro_screen.dart | 52 ++- .../screens/learn_practice_result_screen.dart | 19 +- .../screens/country_region_form_screen.dart | 6 +- lib/ui/views/profile/profile_viewmodel.dart | 11 - .../profile_detail/profile_detail_view.dart | 48 +-- .../profile_detail_view.form.dart | 34 -- .../profile_detail_viewmodel.dart | 358 +++++++++--------- lib/ui/views/register/register_viewmodel.dart | 2 +- .../screens/create_password_screen.dart | 1 - .../telegram_support_view.dart | 40 +- .../telegram_support_viewmodel.dart | 11 + lib/ui/widgets/app_bar_pattern.dart | 44 +++ lib/ui/widgets/large_app_bar.dart | 20 +- lib/ui/widgets/learn_course_tile.dart | 4 +- lib/ui/widgets/learn_lesson_tile.dart | 17 +- .../widgets/learn_practice_result_card.dart | 5 +- lib/ui/widgets/mini_thumbnail.dart | 9 +- pubspec.yaml | 2 +- 41 files changed, 605 insertions(+), 456 deletions(-) create mode 100644 assets/images/pattern.png create mode 100644 lib/ui/widgets/app_bar_pattern.dart diff --git a/assets/images/pattern.png b/assets/images/pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..5b1ca37705e8ed2b5244e6815ea682dcc8f979d9 GIT binary patch literal 16510 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 literal 0 HcmV?d00001 diff --git a/lib/app/app.router.dart b/lib/app/app.router.dart index 3c56080..04c7c04 100644 --- a/lib/app/app.router.dart +++ b/lib/app/app.router.dart @@ -510,8 +510,8 @@ class StackedRouter extends _i1.RouterBase { _i22.LearnLessonDetailView: (data) { final args = data.getArgs(nullOk: false); return _i37.MaterialPageRoute( - builder: (context) => - _i22.LearnLessonDetailView(key: args.key, lesson: args.lesson), + builder: (context) => _i22.LearnLessonDetailView( + key: args.key, lesson: args.lesson, hasPractice: args.hasPractice), settings: data, ); }, @@ -519,7 +519,13 @@ class StackedRouter extends _i1.RouterBase { final args = data.getArgs(nullOk: false); return _i37.MaterialPageRoute( builder: (context) => _i23.LearnPracticeView( - key: args.key, id: args.id, practice: args.practice), + key: args.key, + level: args.level, + id: args.id, + label: args.label, + title: args.title, + practice: args.practice, + subtitle: args.subtitle), settings: data, ); }, @@ -1098,56 +1104,85 @@ class LearnLessonDetailViewArguments { const LearnLessonDetailViewArguments({ this.key, required this.lesson, + required this.hasPractice, }); final _i37.Key? key; final _i40.LearnLesson lesson; + final bool hasPractice; + @override String toString() { - return '{"key": "$key", "lesson": "$lesson"}'; + return '{"key": "$key", "lesson": "$lesson", "hasPractice": "$hasPractice"}'; } @override bool operator ==(covariant LearnLessonDetailViewArguments other) { if (identical(this, other)) return true; - return other.key == key && other.lesson == lesson; + return other.key == key && + other.lesson == lesson && + other.hasPractice == hasPractice; } @override int get hashCode { - return key.hashCode ^ lesson.hashCode; + return key.hashCode ^ lesson.hashCode ^ hasPractice.hashCode; } } class LearnPracticeViewArguments { const LearnPracticeViewArguments({ this.key, + this.level, required this.id, + required this.label, + required this.title, required this.practice, + required this.subtitle, }); final _i37.Key? key; + final String? level; + final int id; + final String label; + + final String title; + final _i41.LearnPractices practice; + final String subtitle; + @override String toString() { - return '{"key": "$key", "id": "$id", "practice": "$practice"}'; + return '{"key": "$key", "level": "$level", "id": "$id", "label": "$label", "title": "$title", "practice": "$practice", "subtitle": "$subtitle"}'; } @override bool operator ==(covariant LearnPracticeViewArguments other) { if (identical(this, other)) return true; - return other.key == key && other.id == id && other.practice == practice; + return other.key == key && + other.level == level && + other.id == id && + other.label == label && + other.title == title && + other.practice == practice && + other.subtitle == subtitle; } @override int get hashCode { - return key.hashCode ^ id.hashCode ^ practice.hashCode; + return key.hashCode ^ + level.hashCode ^ + id.hashCode ^ + label.hashCode ^ + title.hashCode ^ + practice.hashCode ^ + subtitle.hashCode; } } @@ -1817,6 +1852,7 @@ extension NavigatorStateExtension on _i46.NavigationService { Future navigateToLearnLessonDetailView({ _i37.Key? key, required _i40.LearnLesson lesson, + required bool hasPractice, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1824,7 +1860,8 @@ extension NavigatorStateExtension on _i46.NavigationService { transition, }) async { return navigateTo(Routes.learnLessonDetailView, - arguments: LearnLessonDetailViewArguments(key: key, lesson: lesson), + arguments: LearnLessonDetailViewArguments( + key: key, lesson: lesson, hasPractice: hasPractice), id: routerId, preventDuplicates: preventDuplicates, parameters: parameters, @@ -1833,8 +1870,12 @@ extension NavigatorStateExtension on _i46.NavigationService { Future navigateToLearnPracticeView({ _i37.Key? key, + String? level, required int id, + required String label, + required String title, required _i41.LearnPractices practice, + required String subtitle, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -1842,8 +1883,14 @@ extension NavigatorStateExtension on _i46.NavigationService { transition, }) async { return navigateTo(Routes.learnPracticeView, - arguments: - LearnPracticeViewArguments(key: key, id: id, practice: practice), + arguments: LearnPracticeViewArguments( + key: key, + level: level, + id: id, + label: label, + title: title, + practice: practice, + subtitle: subtitle), id: routerId, preventDuplicates: preventDuplicates, parameters: parameters, @@ -2395,6 +2442,7 @@ extension NavigatorStateExtension on _i46.NavigationService { Future replaceWithLearnLessonDetailView({ _i37.Key? key, required _i40.LearnLesson lesson, + required bool hasPractice, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2402,7 +2450,8 @@ extension NavigatorStateExtension on _i46.NavigationService { transition, }) async { return replaceWith(Routes.learnLessonDetailView, - arguments: LearnLessonDetailViewArguments(key: key, lesson: lesson), + arguments: LearnLessonDetailViewArguments( + key: key, lesson: lesson, hasPractice: hasPractice), id: routerId, preventDuplicates: preventDuplicates, parameters: parameters, @@ -2411,8 +2460,12 @@ extension NavigatorStateExtension on _i46.NavigationService { Future replaceWithLearnPracticeView({ _i37.Key? key, + String? level, required int id, + required String label, + required String title, required _i41.LearnPractices practice, + required String subtitle, int? routerId, bool preventDuplicates = true, Map? parameters, @@ -2420,8 +2473,14 @@ extension NavigatorStateExtension on _i46.NavigationService { transition, }) async { return replaceWith(Routes.learnPracticeView, - arguments: - LearnPracticeViewArguments(key: key, id: id, practice: practice), + arguments: LearnPracticeViewArguments( + key: key, + level: level, + id: id, + label: label, + title: title, + practice: practice, + subtitle: subtitle), id: routerId, preventDuplicates: preventDuplicates, parameters: parameters, diff --git a/lib/services/dio_service.dart b/lib/services/dio_service.dart index 2f57469..6bf2d54 100644 --- a/lib/services/dio_service.dart +++ b/lib/services/dio_service.dart @@ -158,6 +158,7 @@ class DioService { return true; } catch (e) { + print('REFRESH EXCEPTION: ${e.toString()}'); await _authenticationService.logout(); await _navigationService.replaceWithLoginView(); return false; diff --git a/lib/services/phone_caller_service.dart b/lib/services/phone_caller_service.dart index 0db16b0..d5da266 100644 --- a/lib/services/phone_caller_service.dart +++ b/lib/services/phone_caller_service.dart @@ -2,7 +2,5 @@ import 'package:flutter_phone_direct_caller/flutter_phone_direct_caller.dart'; class PhoneCallerService { Future call(String phone) async => - await FlutterPhoneDirectCaller.callNumber(phone); - - + await FlutterPhoneDirectCaller.callNumber(phone); } diff --git a/lib/ui/common/app_constants.dart b/lib/ui/common/app_constants.dart index 1f07f4a..1f75a8b 100644 --- a/lib/ui/common/app_constants.dart +++ b/lib/ui/common/app_constants.dart @@ -88,4 +88,4 @@ String kServerClientId = // Other String kPhoneSupport = '+251946396655'; -String kTelegramSupport = '@yimaruacademy2026'; \ No newline at end of file +String kTelegramSupport = '@yimaruacademy2026'; diff --git a/lib/ui/common/app_strings.dart b/lib/ui/common/app_strings.dart index 995114f..0aee3bb 100644 --- a/lib/ui/common/app_strings.dart +++ b/lib/ui/common/app_strings.dart @@ -1,4 +1,3 @@ - const String ksHomeBottomSheetTitle = 'Build Great Apps!'; const String ksSuggestion = diff --git a/lib/ui/common/ui_helpers.dart b/lib/ui/common/ui_helpers.dart index 0318ae6..ff851cd 100644 --- a/lib/ui/common/ui_helpers.dart +++ b/lib/ui/common/ui_helpers.dart @@ -238,6 +238,12 @@ TextStyle style25P600 = const TextStyle( fontWeight: FontWeight.w600, ); +TextStyle style40P900 = const TextStyle( + fontSize: 40, + color: kcPrimaryColor, + fontWeight: FontWeight.w900, +); + TextStyle style25DG600 = const TextStyle( fontSize: 25, color: kcDarkGrey, @@ -309,16 +315,14 @@ TextStyle style14LG400 = const TextStyle( TextStyle style14MG400 = const TextStyle( color: kcMediumGrey, ); +TextStyle style14DG400 = const TextStyle(color: kcDarkGrey); + TextStyle style14DG500 = const TextStyle(color: kcDarkGrey, fontWeight: FontWeight.w500); TextStyle style18MG500 = const TextStyle( fontSize: 18, color: kcMediumGrey, fontWeight: FontWeight.w500); -TextStyle style14DG400 = const TextStyle( - color: kcDarkGrey, -); - TextStyle style14DG600 = const TextStyle( color: kcDarkGrey, fontWeight: FontWeight.w600, diff --git a/lib/ui/views/assessment/screens/assessment_questions_screen.dart b/lib/ui/views/assessment/screens/assessment_questions_screen.dart index 2cb28bb..2235c4a 100644 --- a/lib/ui/views/assessment/screens/assessment_questions_screen.dart +++ b/lib/ui/views/assessment/screens/assessment_questions_screen.dart @@ -53,62 +53,61 @@ class AssessmentQuestionsScreen extends ViewModelWidget { controller: viewModel.pageController, itemCount: viewModel.assessmentQuestions.length, physics: const NeverScrollableScrollPhysics(), - itemBuilder: (cotext, index) => - _buildBodyScroller( viewModel), + itemBuilder: (cotext, index) => _buildBodyScroller(viewModel), ); - Widget _buildBodyScroller( - AssessmentViewModel viewModel) => + Widget _buildBodyScroller(AssessmentViewModel viewModel) => SingleChildScrollView( - child: _buildBody( viewModel), + child: _buildBody(viewModel), ); - Widget _buildBody( - AssessmentViewModel viewModel) => - Column( + Widget _buildBody(AssessmentViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, - children: _buildBodyChildren( viewModel), + children: _buildBodyChildren(viewModel), ); - List _buildBodyChildren( - AssessmentViewModel viewModel) =>[ - + List _buildBodyChildren(AssessmentViewModel viewModel) => [ verticalSpaceMedium, - _buildTitleState( viewModel), + _buildTitleState(viewModel), verticalSpaceMedium, - _buildAnswersState( viewModel), - _buildContinueButtonWrapper( viewModel) + _buildAnswersState(viewModel), + _buildContinueButtonWrapper(viewModel) ]; - Widget _buildTitleState( AssessmentViewModel viewModel)=> viewModel.currentQuestionIndex == - viewModel.assessmentQuestions.length ?Container(): _buildTitle(viewModel); - Widget _buildTitle( - AssessmentViewModel viewModel) => - Text( + Widget _buildTitleState(AssessmentViewModel viewModel) => + viewModel.currentQuestionIndex == viewModel.assessmentQuestions.length + ? Container() + : _buildTitle(viewModel); + Widget _buildTitle(AssessmentViewModel viewModel) => Text( 'Q${viewModel.currentQuestionIndex + 1}. ${viewModel.assessmentQuestions[viewModel.currentQuestionIndex].questionText} ', style: style16DG600, ); - Widget _buildAnswersState( AssessmentViewModel viewModel)=> viewModel.currentQuestionIndex == - viewModel.assessmentQuestions.length ?Container(): _buildAnswers(viewModel); + Widget _buildAnswersState(AssessmentViewModel viewModel) => + viewModel.currentQuestionIndex == viewModel.assessmentQuestions.length + ? Container() + : _buildAnswers(viewModel); - Widget _buildAnswers( - AssessmentViewModel viewModel) => - ListView.builder( + Widget _buildAnswers(AssessmentViewModel viewModel) => ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), - itemCount: viewModel.assessmentQuestions[viewModel.currentQuestionIndex].options?.length, + itemCount: viewModel.assessmentQuestions[viewModel.currentQuestionIndex] + .options?.length, itemBuilder: (context, inner) => _buildAnswer( onTap: () => viewModel.setSelectedAnswer( question: viewModel.currentQuestionIndex + 1, - option: viewModel.assessmentQuestions[viewModel.currentQuestionIndex].options?[inner]), - title: - viewModel.assessmentQuestions[viewModel.currentQuestionIndex].options?[inner].optionText ?? - '', + option: viewModel + .assessmentQuestions[viewModel.currentQuestionIndex] + .options?[inner]), + title: viewModel.assessmentQuestions[viewModel.currentQuestionIndex] + .options?[inner].optionText ?? + '', selected: viewModel.isSelectedAnswer( - question: viewModel.currentQuestionIndex + 1, + question: viewModel.currentQuestionIndex + 1, answer: viewModel - .assessmentQuestions[viewModel.currentQuestionIndex ].options?[inner].optionText ?? + .assessmentQuestions[viewModel.currentQuestionIndex] + .options?[inner] + .optionText ?? ''), ), ); @@ -123,30 +122,27 @@ class AssessmentQuestionsScreen extends ViewModelWidget { selected: selected, ); - Widget _buildContinueButtonWrapper( - AssessmentViewModel viewModel) => - Padding( + Widget _buildContinueButtonWrapper(AssessmentViewModel viewModel) => Padding( padding: const EdgeInsets.only(bottom: 50), - child: _buildContinueButton( viewModel), + child: _buildContinueButton(viewModel), ); - Widget _buildContinueButton( - AssessmentViewModel viewModel) => + Widget _buildContinueButton(AssessmentViewModel viewModel) => CustomElevatedButton( height: 55, borderRadius: 12, foregroundColor: kcWhite, text: viewModel.currentQuestionIndex == - viewModel.assessmentQuestions.length - 1 + viewModel.assessmentQuestions.length - 1 ? 'Finish Level' : 'Continue', - backgroundColor: - viewModel.selectedAnswers.containsKey('${viewModel.currentQuestionIndex + 1}') - ? kcPrimaryColor - : kcPrimaryColor.withOpacity(0.1), - onTap: viewModel.selectedAnswers.containsKey('${viewModel.currentQuestionIndex + 1}') + backgroundColor: viewModel.selectedAnswers + .containsKey('${viewModel.currentQuestionIndex + 1}') + ? kcPrimaryColor + : kcPrimaryColor.withOpacity(0.1), + onTap: viewModel.selectedAnswers + .containsKey('${viewModel.currentQuestionIndex + 1}') ? () => viewModel.nextQuestion() : null, - ); } diff --git a/lib/ui/views/call_support/call_support_view.dart b/lib/ui/views/call_support/call_support_view.dart index 57c1c3e..68641b0 100644 --- a/lib/ui/views/call_support/call_support_view.dart +++ b/lib/ui/views/call_support/call_support_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; +import 'package:yimaru_app/ui/common/app_constants.dart'; import '../../common/app_colors.dart'; import '../../common/ui_helpers.dart'; @@ -83,9 +84,7 @@ class CallSupportView extends StackedView { verticalSpaceMedium, _buildTitle(), verticalSpaceMedium, - _buildSubtitle('+2519012345678'), - verticalSpaceSmall, - _buildSubtitle('+2519012345678'), + _buildSubtitle(kPhoneSupport), ]; Widget _buildIcon() => @@ -109,12 +108,13 @@ class CallSupportView extends StackedView { ); Widget _buildContinueButton(CallSupportViewModel viewModel) => - const CustomElevatedButton( + CustomElevatedButton( height: 55, borderRadius: 12, text: 'Tap to Call', leadingIcon: Icons.call, foregroundColor: kcWhite, backgroundColor: kcPrimaryColor, + onTap: () async => await viewModel.callSupport(), ); } diff --git a/lib/ui/views/call_support/call_support_viewmodel.dart b/lib/ui/views/call_support/call_support_viewmodel.dart index f618f8c..b36a784 100644 --- a/lib/ui/views/call_support/call_support_viewmodel.dart +++ b/lib/ui/views/call_support/call_support_viewmodel.dart @@ -2,11 +2,18 @@ import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; import '../../../app/app.locator.dart'; +import '../../../services/phone_caller_service.dart'; +import '../../common/app_constants.dart'; class CallSupportViewModel extends BaseViewModel { // Dependency injection final _navigationService = locator(); + final _phoneCallerService = locator(); + + // Call support + Future callSupport() => _phoneCallerService.call(kPhoneSupport); + // Navigation void pop() => _navigationService.back(); } diff --git a/lib/ui/views/forget_password/forget_password_viewmodel.dart b/lib/ui/views/forget_password/forget_password_viewmodel.dart index 4e2fef3..10f672a 100644 --- a/lib/ui/views/forget_password/forget_password_viewmodel.dart +++ b/lib/ui/views/forget_password/forget_password_viewmodel.dart @@ -40,14 +40,6 @@ class ForgetPasswordViewModel extends FormViewModel { bool get length => _length; - bool _number = false; - - bool get number => _number; - - bool _specialChar = false; - - bool get specialChar => _specialChar; - bool _focusPassword = false; bool get focusPassword => _focusPassword; @@ -105,11 +97,9 @@ class ForgetPasswordViewModel extends FormViewModel { int completed = 0; if (_length) completed++; - if (_number) completed++; - if (_specialChar) completed++; if (_passwordMatch) completed++; - return completed / 4; // returns 0.0 → 1.0 + return completed / 2; // returns 0.0 → 1.0 } void validatePassword( @@ -120,17 +110,7 @@ class ForgetPasswordViewModel extends FormViewModel { _length = false; } - if (RegExp(r'\d').hasMatch(password)) { - _number = true; - } else { - _number = false; - } - if (RegExp(r'[!@#$%^&*(),.?":{}|<>]').hasMatch(password)) { - _specialChar = true; - } else { - _specialChar = false; - } if (password == confirmPassword) { _passwordMatch = true; @@ -156,8 +136,6 @@ class ForgetPasswordViewModel extends FormViewModel { // Reset reset password screen void resetResetPasswordScreen() { _length = false; - _number = false; - _specialChar = false; _passwordMatch = false; _focusPassword = false; _focusResetCode = false; diff --git a/lib/ui/views/forget_password/screens/reset_password_screen.dart b/lib/ui/views/forget_password/screens/reset_password_screen.dart index 159c936..1e89530 100644 --- a/lib/ui/views/forget_password/screens/reset_password_screen.dart +++ b/lib/ui/views/forget_password/screens/reset_password_screen.dart @@ -140,8 +140,6 @@ class ResetPasswordScreen extends ViewModelWidget { _buildLinearProgressIndicator(viewModel), verticalSpaceSmall, _buildCharLengthValidator(viewModel), - _buildNumberValidator(viewModel), - _buildSymbolValidator(viewModel), _buildPasswordMatchValidator(viewModel), verticalSpaceSmall, _buildSignUpButton(viewModel), @@ -256,16 +254,6 @@ class ResetPasswordScreen extends ViewModelWidget { backgroundColor: viewModel.length ? kcPrimaryColor : kcLightGrey, label: '8 characters minimum'); - Widget _buildNumberValidator(ForgetPasswordViewModel viewModel) => - ValidatorListTile( - backgroundColor: viewModel.number ? kcPrimaryColor : kcLightGrey, - label: 'a number'); - - Widget _buildSymbolValidator(ForgetPasswordViewModel viewModel) => - ValidatorListTile( - backgroundColor: viewModel.specialChar ? kcPrimaryColor : kcLightGrey, - label: 'one symbol minimum'); - Widget _buildPasswordMatchValidator(ForgetPasswordViewModel viewModel) => ValidatorListTile( backgroundColor: @@ -281,20 +269,14 @@ class ResetPasswordScreen extends ViewModelWidget { onTap: passwordController.text.isNotEmpty && confirmPasswordController.text.isNotEmpty && resetCodeController.text.isNotEmpty && - viewModel.number && viewModel.length && - viewModel.specialChar && - viewModel.specialChar && viewModel.passwordMatch ? () async => await _reset(viewModel) : null, backgroundColor: passwordController.text.isNotEmpty && confirmPasswordController.text.isNotEmpty && resetCodeController.text.isNotEmpty && - viewModel.number && viewModel.length && - viewModel.specialChar && - viewModel.specialChar && viewModel.passwordMatch ? kcPrimaryColor : kcPrimaryColor.withOpacity(0.1), diff --git a/lib/ui/views/learn_course/learn_course_view.dart b/lib/ui/views/learn_course/learn_course_view.dart index b36b9f8..1e745aa 100644 --- a/lib/ui/views/learn_course/learn_course_view.dart +++ b/lib/ui/views/learn_course/learn_course_view.dart @@ -84,8 +84,9 @@ class LearnCourseView extends StackedView { course: viewModel.learnCourses[index], onViewTap: () async => await viewModel .navigateToLearnModule(viewModel.learnCourses[index]), - onPracticeTap: () async => await viewModel - .navigateToLearnPractice(viewModel.learnCourses[index].id ?? 0), + onPracticeTap: () async => await viewModel.navigateToLearnPractice( + id: viewModel.learnCourses[index].id ?? 0, + level: viewModel.learnCourses[index].name ?? ''), ), separatorBuilder: (context, index) => verticalSpaceSmall, ); diff --git a/lib/ui/views/learn_course/learn_course_viewmodel.dart b/lib/ui/views/learn_course/learn_course_viewmodel.dart index c27c5b6..03c0066 100644 --- a/lib/ui/views/learn_course/learn_course_viewmodel.dart +++ b/lib/ui/views/learn_course/learn_course_viewmodel.dart @@ -27,8 +27,16 @@ class LearnCourseViewModel extends BaseViewModel { Future navigateToLearnModule(LearnCourse course) async => _navigationService.navigateToLearnModuleView(course: course); - Future navigateToLearnPractice(int id) async => await _navigationService - .navigateToLearnPracticeView(id: id, practice: LearnPractices.course); + Future navigateToLearnPractice( + {required int id, required String level}) async => + await _navigationService.navigateToLearnPracticeView( + id: id, + level: level, + label: 'Begin Level Practice', + practice: LearnPractices.course, + title: 'Let’s Practice Course $level', + subtitle: 'Let’s quickly review what you’ve learned in this level!', + ); // Remote api call diff --git a/lib/ui/views/learn_lesson/learn_lesson_view.dart b/lib/ui/views/learn_lesson/learn_lesson_view.dart index 98ee354..3e85e0c 100644 --- a/lib/ui/views/learn_lesson/learn_lesson_view.dart +++ b/lib/ui/views/learn_lesson/learn_lesson_view.dart @@ -115,9 +115,9 @@ class LearnLessonView extends StackedView { _buildSubtitle(), verticalSpaceSmall, _buildModuleProgress(), - verticalSpaceMedium, + verticalSpaceLarge, _buildMotivationCard(), - verticalSpaceMedium, + verticalSpaceLarge, _buildHeader(), verticalSpaceMedium, _buildListViewBuilder(viewModel), @@ -156,20 +156,25 @@ class LearnLessonView extends StackedView { itemCount: viewModel.lessons.length, physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) => _buildTile( + index: index, lesson: viewModel.lessons[index], - onLessonTap: () async => await viewModel - .navigateToLearnLessonDetail(viewModel.lessons[index]), + onLessonTap: () async => await viewModel.navigateToLearnLessonDetail( + lesson: viewModel.lessons[index], + hasPractice: + index != viewModel.lessons.length - 1 ? true : false), onPracticeTap: () async => await viewModel .navigateToLearnPractice(viewModel.lessons[index].id ?? 0), ), ); Widget _buildTile({ + required int index, required LearnLesson lesson, required GestureTapCallback? onLessonTap, required GestureTapCallback? onPracticeTap, }) => LearnLessonTile( + index: index, lesson: lesson, onLessonTap: onLessonTap, onPracticeTap: onPracticeTap, diff --git a/lib/ui/views/learn_lesson/learn_lesson_viewmodel.dart b/lib/ui/views/learn_lesson/learn_lesson_viewmodel.dart index b909469..6b00a18 100644 --- a/lib/ui/views/learn_lesson/learn_lesson_viewmodel.dart +++ b/lib/ui/views/learn_lesson/learn_lesson_viewmodel.dart @@ -24,11 +24,20 @@ class LearnLessonViewModel extends BaseViewModel { // Navigation void pop() => _navigationService.back(); - Future navigateToLearnPractice(int id) async => await _navigationService - .navigateToLearnPracticeView(id: id, practice: LearnPractices.lesson); + Future navigateToLearnPractice(int id) async => + await _navigationService.navigateToLearnPracticeView( + id: id, + label: 'Start Practice', + practice: LearnPractices.lesson, + title: 'Let\'s practice what you just learnt!', + subtitle: + 'I’ll ask you a few questions, and you can respond naturally.', + ); - Future navigateToLearnLessonDetail(LearnLesson lesson) async => - await _navigationService.navigateToLearnLessonDetailView(lesson: lesson); + Future navigateToLearnLessonDetail( + {required bool hasPractice, required LearnLesson lesson}) async => + await _navigationService.navigateToLearnLessonDetailView( + lesson: lesson, hasPractice: hasPractice); // Remote api call diff --git a/lib/ui/views/learn_lesson_detail/learn_lesson_detail_view.dart b/lib/ui/views/learn_lesson_detail/learn_lesson_detail_view.dart index e28e4ab..f301d99 100644 --- a/lib/ui/views/learn_lesson_detail/learn_lesson_detail_view.dart +++ b/lib/ui/views/learn_lesson_detail/learn_lesson_detail_view.dart @@ -13,9 +13,11 @@ import '../../widgets/small_app_bar.dart'; import 'learn_lesson_detail_viewmodel.dart'; class LearnLessonDetailView extends StackedView { + final bool hasPractice; final LearnLesson lesson; - const LearnLessonDetailView({Key? key, required this.lesson}) + const LearnLessonDetailView( + {Key? key, required this.lesson, required this.hasPractice}) : super(key: key); Future _navigate(LearnLessonDetailViewModel viewModel) async { @@ -86,7 +88,7 @@ class LearnLessonDetailView extends StackedView { List _buildBodyColumnChildren(LearnLessonDetailViewModel viewModel) => [ _buildLevelsColumnWrapper(viewModel), - _buildContinueButtonWrapper(viewModel) + if (hasPractice) _buildPracticeButtonWrapper(viewModel) ]; Widget _buildLevelsColumnWrapper(LearnLessonDetailViewModel viewModel) => @@ -157,21 +159,21 @@ class LearnLessonDetailView extends StackedView { style: style14DG400, ); - Widget _buildContinueButtonWrapper(LearnLessonDetailViewModel viewModel) => + Widget _buildPracticeButtonWrapper(LearnLessonDetailViewModel viewModel) => Padding( padding: const EdgeInsets.only( left: 15, right: 15, bottom: 50, ), - child: _buildContinueButton(viewModel), + child: _buildPracticeButton(viewModel), ); - Widget _buildContinueButton(LearnLessonDetailViewModel viewModel) => + Widget _buildPracticeButton(LearnLessonDetailViewModel viewModel) => CustomElevatedButton( height: 55, borderRadius: 12, - text: 'Practices', + text: 'Take Practice', foregroundColor: kcWhite, backgroundColor: kcPrimaryColor, onTap: () async => await _navigate(viewModel), diff --git a/lib/ui/views/learn_lesson_detail/learn_lesson_detail_viewmodel.dart b/lib/ui/views/learn_lesson_detail/learn_lesson_detail_viewmodel.dart index defc758..aec507f 100644 --- a/lib/ui/views/learn_lesson_detail/learn_lesson_detail_viewmodel.dart +++ b/lib/ui/views/learn_lesson_detail/learn_lesson_detail_viewmodel.dart @@ -67,6 +67,13 @@ class LearnLessonDetailViewModel extends BaseViewModel { // Navigation void pop() => _navigationService.back(); - Future navigateToLearnPractice(int id) async => await _navigationService - .navigateToLearnPracticeView(id: id, practice: LearnPractices.lesson); + Future navigateToLearnPractice(int id) async => + await _navigationService.navigateToLearnPracticeView( + id: id, + label: 'Start Practice', + practice: LearnPractices.lesson, + title: 'Let\'s practice what you just learnt!', + subtitle: + 'I’ll ask you a few questions, and you can respond naturally.', + ); } diff --git a/lib/ui/views/learn_module/learn_module_view.dart b/lib/ui/views/learn_module/learn_module_view.dart index 8086d38..e7e337a 100644 --- a/lib/ui/views/learn_module/learn_module_view.dart +++ b/lib/ui/views/learn_module/learn_module_view.dart @@ -116,8 +116,9 @@ class LearnModuleView extends StackedView { physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) => _buildTile( module: viewModel.modules[index], - onPracticeTap: () async => await viewModel - .navigateToLearnPractice(viewModel.modules[index].id ?? 0), + onPracticeTap: () async => await viewModel.navigateToLearnPractice( + id: viewModel.modules[index].id ?? 0, + module: viewModel.modules[index].name ?? ''), onModuleTap: () async => await viewModel.navigateToLearnLesson(viewModel.modules[index]), ), diff --git a/lib/ui/views/learn_module/learn_module_viewmodel.dart b/lib/ui/views/learn_module/learn_module_viewmodel.dart index f350174..a5b4ce5 100644 --- a/lib/ui/views/learn_module/learn_module_viewmodel.dart +++ b/lib/ui/views/learn_module/learn_module_viewmodel.dart @@ -27,8 +27,15 @@ class LearnModuleViewModel extends BaseViewModel { Future navigateToLearnLesson(LearnModule module) async => await _navigationService.navigateToLearnLessonView(module: module); - Future navigateToLearnPractice(int id) async => await _navigationService - .navigateToLearnPracticeView(id: id, practice: LearnPractices.module); + Future navigateToLearnPractice( + {required int id, required String module}) async => + await _navigationService.navigateToLearnPracticeView( + id: id, + label: 'Begin Module Practice', + practice: LearnPractices.module, + title: 'Let’s Practice $module', + subtitle: 'Let’s quickly review what you’ve learned in this module! ', + ); // Remote api call diff --git a/lib/ui/views/learn_practice/learn_practice_view.dart b/lib/ui/views/learn_practice/learn_practice_view.dart index 02b4629..8354807 100644 --- a/lib/ui/views/learn_practice/learn_practice_view.dart +++ b/lib/ui/views/learn_practice/learn_practice_view.dart @@ -16,10 +16,21 @@ import 'learn_practice_viewmodel.dart'; class LearnPracticeView extends StackedView { final int id; + final String label; + final String title; + final String? level; + final String subtitle; final LearnPractices practice; - const LearnPracticeView({Key? key, required this.id, required this.practice}) - : super(key: key); + const LearnPracticeView({ + Key? key, + this.level, + required this.id, + required this.label, + required this.title, + required this.practice, + required this.subtitle, + }) : super(key: key); Future _cancel(LearnPracticeViewModel viewModel) async { await viewModel.stopRecording(); @@ -108,10 +119,17 @@ class LearnPracticeView extends StackedView { _buildLearnPracticeQuestionsScreen(), _buildFinishLearnPracticeScreen(), _buildLearnPracticeResultScreen(), - _buildLearnPracticeCompletionScreen() + if (practice == LearnPractices.course) + _buildLearnPracticeCompletionScreen() ]; - Widget _buildLearnPracticeIntroScreen() => const LearnPracticeIntroScreen(); + Widget _buildLearnPracticeIntroScreen() => LearnPracticeIntroScreen( + level: level, + title: title, + label: label, + practice: practice, + subtitle: subtitle, + ); Widget _buildLearnPracticeElementsScreen() => const LearnPracticeDescriptionScreen(); @@ -121,8 +139,9 @@ class LearnPracticeView extends StackedView { Widget _buildFinishLearnPracticeScreen() => const FinishLearnPracticeScreen(); - Widget _buildLearnPracticeResultScreen() => const LearnPracticeResultScreen(); + Widget _buildLearnPracticeResultScreen() => + LearnPracticeResultScreen(practice: practice); Widget _buildLearnPracticeCompletionScreen() => - const LearnPracticeCompletionScreen(); + LearnPracticeCompletionScreen(level: level ?? ''); } diff --git a/lib/ui/views/learn_practice/learn_practice_viewmodel.dart b/lib/ui/views/learn_practice/learn_practice_viewmodel.dart index f37f157..8ec654a 100644 --- a/lib/ui/views/learn_practice/learn_practice_viewmodel.dart +++ b/lib/ui/views/learn_practice/learn_practice_viewmodel.dart @@ -225,7 +225,7 @@ class LearnPracticeViewModel extends ReactiveViewModel { await stopRecording(); _answers.add({ 'busy_object': question.id.toString(), - 'sample_text_answer': question.audioCorrectAnswerText, + 'question_text': question.questionText, 'sample_voice_answer': question.sampleAnswerVoicePrompt, 'recorded_voice_answer': await _voiceRecorderService.getRecordedAudio(), }); diff --git a/lib/ui/views/learn_practice/screens/learn_practice_completion_screen.dart b/lib/ui/views/learn_practice/screens/learn_practice_completion_screen.dart index 0e29b1f..62b1bc2 100644 --- a/lib/ui/views/learn_practice/screens/learn_practice_completion_screen.dart +++ b/lib/ui/views/learn_practice/screens/learn_practice_completion_screen.dart @@ -9,7 +9,8 @@ import '../../../widgets/custom_elevated_button.dart'; class LearnPracticeCompletionScreen extends ViewModelWidget { - const LearnPracticeCompletionScreen({super.key}); + final String level; + const LearnPracticeCompletionScreen({super.key, required this.level}); @override Widget build(BuildContext context, LearnPracticeViewModel viewModel) => @@ -54,7 +55,7 @@ class LearnPracticeCompletionScreen ); Widget _buildTitle() => Text( - 'Yay, you’ve completed A1 ', + 'Yay, you’ve completed $level ', style: style25DG600, textAlign: TextAlign.center, ); 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 0cb82be..5229abb 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 @@ -3,6 +3,7 @@ import 'package:stacked/stacked.dart'; 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/custom_elevated_button.dart'; @@ -10,7 +11,19 @@ import '../../../widgets/small_app_bar.dart'; import '../../../widgets/speaking_partner_image.dart'; class LearnPracticeIntroScreen extends ViewModelWidget { - const LearnPracticeIntroScreen({super.key}); + final String title; + final String label; + final String? level; + final String subtitle; + final LearnPractices practice; + + const LearnPracticeIntroScreen( + {super.key, + this.level, + required this.label, + required this.title, + required this.subtitle, + required this.practice}); Future _cancel(LearnPracticeViewModel viewModel) async { await viewModel.stopRecording(); @@ -114,21 +127,44 @@ class LearnPracticeIntroScreen extends ViewModelWidget { List _buildPracticeColumnChildren(LearnPracticeViewModel viewModel) => [ verticalSpaceMassive, - _buildImage(), + _buildImageState(), verticalSpaceMedium, - _buildPartnerName(), + _buildPartnerNameState(), verticalSpaceMedium, _buildTitle(), verticalSpaceMedium, _buildSubtitle() ]; - Widget _buildImage() => const SpeakingPartnerImage( + Widget _buildImageState() => + level != null ? _buildCourseSpeakingPartnerImage() : _buildImage(75); + + Widget _buildCourseSpeakingPartnerImage() => CircleAvatar( radius: 75, + backgroundColor: kcPrimaryColorLight, + child: _buildCourseSpeakingPartnerText(), ); + Widget _buildCourseSpeakingPartnerText() => Text( + level?.toUpperCase() ?? '', + style: style40P900, + ); + + Widget _buildImage(double radius) => SpeakingPartnerImage(radius: radius); + + Widget _buildPartnerNameState() => + level != null ? _buildCourseSpeakingPartner() : _buildPartnerName(); + + Widget _buildCourseSpeakingPartner() => Row( + mainAxisSize: MainAxisSize.min, + children: _buildCourseSpeakingPartnerChildren(), + ); + + List _buildCourseSpeakingPartnerChildren() => + [_buildImage(15), horizontalSpaceTiny, _buildPartnerName()]; + Widget _buildPartnerName() => Text.rich( - TextSpan(text: 'Dawit', style: style14DG600, children: [ + TextSpan(text: 'Daniel', style: style14DG600, children: [ TextSpan( text: ' - Your Speaking Partner', style: style14MG400, @@ -137,13 +173,13 @@ class LearnPracticeIntroScreen extends ViewModelWidget { ); Widget _buildTitle() => Text( - 'Let\'s practice what you just learnt!', + title, style: style25DG600, textAlign: TextAlign.center, ); Widget _buildSubtitle() => Text( - 'I’ll ask you a few questions, and you can respond naturally.', + subtitle, maxLines: 1, style: style14DG400, textAlign: TextAlign.center, @@ -158,8 +194,8 @@ class LearnPracticeIntroScreen extends ViewModelWidget { Widget _buildContinueButton(LearnPracticeViewModel viewModel) => CustomElevatedButton( height: 55, + text: label, borderRadius: 12, - text: 'Practice', foregroundColor: kcWhite, onTap: () => viewModel.goTo(1), backgroundColor: kcPrimaryColor, 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 b4d8060..daf14f8 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 @@ -1,5 +1,6 @@ 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/learn_practice_viewmodel.dart'; import 'package:yimaru_app/ui/widgets/learn_practice_tip_section.dart'; import 'package:yimaru_app/ui/widgets/learn_practice_results_wrapper.dart'; @@ -12,9 +13,19 @@ import '../../../widgets/small_app_bar.dart'; class LearnPracticeResultScreen extends ViewModelWidget { - const LearnPracticeResultScreen({super.key}); + final LearnPractices practice; - void _navigate(LearnPracticeViewModel viewModel) async => + const LearnPracticeResultScreen({super.key, required this.practice}); + + Future _navigate(LearnPracticeViewModel viewModel) async { + if (practice == LearnPractices.course) { + viewModel.goTo(5); + } else { + viewModel.pop(); + } + } + + Future _retry(LearnPracticeViewModel viewModel) async => await viewModel.reset(); Future _cancel(LearnPracticeViewModel viewModel) async { @@ -145,8 +156,8 @@ class LearnPracticeResultScreen text: 'Continue', borderRadius: 12, foregroundColor: kcWhite, - onTap: () => viewModel.goTo(5), backgroundColor: kcPrimaryColor, + onTap: () async => await _navigate(viewModel), ); Widget _buildPracticeAgainButton(LearnPracticeViewModel viewModel) => @@ -157,6 +168,6 @@ class LearnPracticeResultScreen backgroundColor: kcWhite, borderColor: kcPrimaryColor, foregroundColor: kcPrimaryColor, - onTap: () => _navigate(viewModel), + onTap: () async => await _retry(viewModel), ); } diff --git a/lib/ui/views/onboarding/screens/country_region_form_screen.dart b/lib/ui/views/onboarding/screens/country_region_form_screen.dart index 5c322e9..6592e5e 100644 --- a/lib/ui/views/onboarding/screens/country_region_form_screen.dart +++ b/lib/ui/views/onboarding/screens/country_region_form_screen.dart @@ -98,9 +98,9 @@ class CountryRegionFormScreen extends ViewModelWidget { _buildCountryDropDown(viewModel), verticalSpaceMedium, _buildRegionFormState(viewModel), - if (viewModel.hasRegionValidationMessage && - !viewModel.dropdownRegion && - viewModel.focusRegion) + if (viewModel.hasRegionValidationMessage && + !viewModel.dropdownRegion && + viewModel.focusRegion) verticalSpaceTiny, if (viewModel.hasRegionValidationMessage && !viewModel.dropdownRegion && diff --git a/lib/ui/views/profile/profile_viewmodel.dart b/lib/ui/views/profile/profile_viewmodel.dart index e16caac..e18b087 100644 --- a/lib/ui/views/profile/profile_viewmodel.dart +++ b/lib/ui/views/profile/profile_viewmodel.dart @@ -26,10 +26,6 @@ class ProfileViewModel extends ReactiveViewModel { final _googleAuthService = locator(); - final _phoneCallerService = locator(); - - final _urlLauncherService = locator(); - final _imagePickerService = locator(); final _authenticationService = locator(); @@ -68,13 +64,6 @@ class ProfileViewModel extends ReactiveViewModel { } } - // Launch telegram - Future launchTelegram() => - _urlLauncherService.launchUri(kTelegramSupport); - - // Call support - Future callSupport() => _phoneCallerService.call(kPhoneSupport); - // Dialog Future showAbortDialog() async { DialogResponse? response = await _dialogService.showDialog( diff --git a/lib/ui/views/profile_detail/profile_detail_view.dart b/lib/ui/views/profile_detail/profile_detail_view.dart index 780a963..63dec45 100644 --- a/lib/ui/views/profile_detail/profile_detail_view.dart +++ b/lib/ui/views/profile_detail/profile_detail_view.dart @@ -66,13 +66,14 @@ class ProfileDetailView extends StackedView content: _buildImagePicker(context: context, viewModel: viewModel), ); - void _checkRegion(ProfileDetailViewModel viewModel){ - bool region = viewModel.checkRegion(region:viewModel.user?.region ?? 'Addis Ababa',country:viewModel.user?.country ?? 'Ethiopia' ); - if(region){ + void _checkRegion(ProfileDetailViewModel viewModel) { + bool region = viewModel.checkRegion( + region: viewModel.user?.region ?? 'Addis Ababa', + country: viewModel.user?.country ?? 'Ethiopia'); + if (region) { viewModel.setSelectedRegion(viewModel.user?.region ?? 'Addis Ababa'); - }else{ + } else { regionController.text = viewModel.user?.region ?? ''; - } } @@ -549,10 +550,12 @@ class ProfileDetailView extends StackedView _buildRegionFormState(viewModel), if (viewModel.hasRegionValidationMessage && !viewModel.dropdownRegion && - viewModel.focusRegion) verticalSpaceTiny, + viewModel.focusRegion) + verticalSpaceTiny, if (viewModel.hasRegionValidationMessage && !viewModel.dropdownRegion && - viewModel.focusRegion) _buildRegionValidatorWrapper(viewModel), + viewModel.focusRegion) + _buildRegionValidatorWrapper(viewModel), ]; Widget _buildRegionFormFieldLabel() => CustomFormLabel( @@ -574,14 +577,15 @@ class ProfileDetailView extends StackedView onChanged: (value) => viewModel.setSelectedRegion(value ?? 'Addis Ababa')); - Widget _buildRegionFormField(ProfileDetailViewModel viewModel) => TextFormField( - controller: regionController, - onTap: viewModel.setRegionFocus, - decoration: inputDecoration( - hint: 'Enter Your City', - focus: viewModel.focusRegion, - filled: regionController.text.isNotEmpty), - ); + Widget _buildRegionFormField(ProfileDetailViewModel viewModel) => + TextFormField( + controller: regionController, + onTap: viewModel.setRegionFocus, + decoration: inputDecoration( + hint: 'Enter Your City', + focus: viewModel.focusRegion, + filled: regionController.text.isNotEmpty), + ); Widget _buildRegionValidatorWrapper(ProfileDetailViewModel viewModel) => viewModel.hasRegionValidationMessage @@ -589,10 +593,9 @@ class ProfileDetailView extends StackedView : Container(); Widget _buildRegionValidator(ProfileDetailViewModel viewModel) => Text( - viewModel.regionValidationMessage!, - style: style12R700, - ); - + viewModel.regionValidationMessage!, + style: style12R700, + ); Widget _buildOccupationDropdownWrapper(ProfileDetailViewModel viewModel) => Column( @@ -608,7 +611,6 @@ class ProfileDetailView extends StackedView _buildOccupationDropdownLabel(), verticalSpaceSmall, _buildOccupationDropdown(viewModel) - ]; Widget _buildOccupationDropdownLabel() => CustomFormLabel( @@ -625,9 +627,9 @@ class ProfileDetailView extends StackedView onChanged: (value) => viewModel.setSelectedOccupation( value ?? 'Students (High school & University)')); Icon _buildSearchIcon() => const Icon( - Icons.search, - color: kcPrimaryColor, - ); + Icons.search, + color: kcPrimaryColor, + ); Widget _buildLowerColumn(ProfileDetailViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, children: _buildLowerColumnChildren(viewModel), diff --git a/lib/ui/views/profile_detail/profile_detail_view.form.dart b/lib/ui/views/profile_detail/profile_detail_view.form.dart index 23b9060..bfc4010 100644 --- a/lib/ui/views/profile_detail/profile_detail_view.form.dart +++ b/lib/ui/views/profile_detail/profile_detail_view.form.dart @@ -18,7 +18,6 @@ const String RegionValueKey = 'region'; const String PhoneNumberValueKey = 'phoneNumber'; const String LastNameValueKey = 'lastName'; const String FirstNameValueKey = 'firstName'; -const String OccupationValueKey = 'occupation'; final Map _ProfileDetailViewTextEditingControllers = {}; @@ -32,7 +31,6 @@ final Map PhoneNumberValueKey: FormValidator.validatePhoneNumberForm, LastNameValueKey: FormValidator.validateForm, FirstNameValueKey: FormValidator.validateForm, - OccupationValueKey: FormValidator.validateForm, }; mixin $ProfileDetailView { @@ -46,15 +44,12 @@ mixin $ProfileDetailView { _getFormTextEditingController(LastNameValueKey); TextEditingController get firstNameController => _getFormTextEditingController(FirstNameValueKey); - TextEditingController get occupationController => - _getFormTextEditingController(OccupationValueKey); FocusNode get emailFocusNode => _getFormFocusNode(EmailValueKey); FocusNode get regionFocusNode => _getFormFocusNode(RegionValueKey); FocusNode get phoneNumberFocusNode => _getFormFocusNode(PhoneNumberValueKey); FocusNode get lastNameFocusNode => _getFormFocusNode(LastNameValueKey); FocusNode get firstNameFocusNode => _getFormFocusNode(FirstNameValueKey); - FocusNode get occupationFocusNode => _getFormFocusNode(OccupationValueKey); TextEditingController _getFormTextEditingController( String key, { @@ -85,7 +80,6 @@ mixin $ProfileDetailView { phoneNumberController.addListener(() => _updateFormData(model)); lastNameController.addListener(() => _updateFormData(model)); firstNameController.addListener(() => _updateFormData(model)); - occupationController.addListener(() => _updateFormData(model)); _updateFormData(model, forceValidate: _autoTextFieldValidation); } @@ -102,7 +96,6 @@ mixin $ProfileDetailView { phoneNumberController.addListener(() => _updateFormData(model)); lastNameController.addListener(() => _updateFormData(model)); firstNameController.addListener(() => _updateFormData(model)); - occupationController.addListener(() => _updateFormData(model)); _updateFormData(model, forceValidate: _autoTextFieldValidation); } @@ -117,7 +110,6 @@ mixin $ProfileDetailView { PhoneNumberValueKey: phoneNumberController.text, LastNameValueKey: lastNameController.text, FirstNameValueKey: firstNameController.text, - OccupationValueKey: occupationController.text, }), ); @@ -165,8 +157,6 @@ extension ValueProperties on FormStateHelper { this.formValueMap[PhoneNumberValueKey] as String?; String? get lastNameValue => this.formValueMap[LastNameValueKey] as String?; String? get firstNameValue => this.formValueMap[FirstNameValueKey] as String?; - String? get occupationValue => - this.formValueMap[OccupationValueKey] as String?; set emailValue(String? value) { this.setData( @@ -226,18 +216,6 @@ extension ValueProperties on FormStateHelper { } } - set occupationValue(String? value) { - this.setData( - this.formValueMap..addAll({OccupationValueKey: value}), - ); - - if (_ProfileDetailViewTextEditingControllers.containsKey( - OccupationValueKey)) { - _ProfileDetailViewTextEditingControllers[OccupationValueKey]?.text = - value ?? ''; - } - } - bool get hasEmail => this.formValueMap.containsKey(EmailValueKey) && (emailValue?.isNotEmpty ?? false); @@ -253,9 +231,6 @@ extension ValueProperties on FormStateHelper { bool get hasFirstName => this.formValueMap.containsKey(FirstNameValueKey) && (firstNameValue?.isNotEmpty ?? false); - bool get hasOccupation => - this.formValueMap.containsKey(OccupationValueKey) && - (occupationValue?.isNotEmpty ?? false); bool get hasEmailValidationMessage => this.fieldsValidationMessages[EmailValueKey]?.isNotEmpty ?? false; @@ -267,8 +242,6 @@ extension ValueProperties on FormStateHelper { this.fieldsValidationMessages[LastNameValueKey]?.isNotEmpty ?? false; bool get hasFirstNameValidationMessage => this.fieldsValidationMessages[FirstNameValueKey]?.isNotEmpty ?? false; - bool get hasOccupationValidationMessage => - this.fieldsValidationMessages[OccupationValueKey]?.isNotEmpty ?? false; String? get emailValidationMessage => this.fieldsValidationMessages[EmailValueKey]; @@ -280,8 +253,6 @@ extension ValueProperties on FormStateHelper { this.fieldsValidationMessages[LastNameValueKey]; String? get firstNameValidationMessage => this.fieldsValidationMessages[FirstNameValueKey]; - String? get occupationValidationMessage => - this.fieldsValidationMessages[OccupationValueKey]; } extension Methods on FormStateHelper { @@ -295,8 +266,6 @@ extension Methods on FormStateHelper { this.fieldsValidationMessages[LastNameValueKey] = validationMessage; void setFirstNameValidationMessage(String? validationMessage) => this.fieldsValidationMessages[FirstNameValueKey] = validationMessage; - void setOccupationValidationMessage(String? validationMessage) => - this.fieldsValidationMessages[OccupationValueKey] = validationMessage; /// Clears text input fields on the Form void clearForm() { @@ -305,7 +274,6 @@ extension Methods on FormStateHelper { phoneNumberValue = ''; lastNameValue = ''; firstNameValue = ''; - occupationValue = ''; } /// Validates text input fields on the Form @@ -316,7 +284,6 @@ extension Methods on FormStateHelper { PhoneNumberValueKey: getValidationMessage(PhoneNumberValueKey), LastNameValueKey: getValidationMessage(LastNameValueKey), FirstNameValueKey: getValidationMessage(FirstNameValueKey), - OccupationValueKey: getValidationMessage(OccupationValueKey), }); } } @@ -341,5 +308,4 @@ void updateValidationData(FormStateHelper model) => PhoneNumberValueKey: getValidationMessage(PhoneNumberValueKey), LastNameValueKey: getValidationMessage(LastNameValueKey), FirstNameValueKey: getValidationMessage(FirstNameValueKey), - OccupationValueKey: getValidationMessage(OccupationValueKey), }); diff --git a/lib/ui/views/profile_detail/profile_detail_viewmodel.dart b/lib/ui/views/profile_detail/profile_detail_viewmodel.dart index 9fc1cd0..db6fa55 100644 --- a/lib/ui/views/profile_detail/profile_detail_viewmodel.dart +++ b/lib/ui/views/profile_detail/profile_detail_viewmodel.dart @@ -57,13 +57,11 @@ class ProfileDetailViewModel extends ReactiveViewModel bool get focusEmail => _focusEmail; - // Occupation String _selectedOccupation = 'Students (High school & University)'; String get selectedOccupation => _selectedOccupation; - // Country String _selectedCountry = 'Ethiopia'; @@ -117,18 +115,16 @@ class ProfileDetailViewModel extends ReactiveViewModel rebuildUi(); } - - // Occupation List getOccupations() => [ - 'Students (High school & University)', - 'Job Seekers / Fresh Graduates', - 'Working Professionals (Corporate/Office)', - 'Government & NGO Workers', - 'Entrepreneurs & Small Business Owners', - 'Hospitality & Tourism Workers', - 'Freelancers / Remote Workers (Digital Economy)' - ]; + 'Students (High school & University)', + 'Job Seekers / Fresh Graduates', + 'Working Professionals (Corporate/Office)', + 'Government & NGO Workers', + 'Entrepreneurs & Small Business Owners', + 'Hospitality & Tourism Workers', + 'Freelancers / Remote Workers (Digital Economy)' + ]; void setSelectedOccupation(String value) { _selectedOccupation = value; @@ -137,158 +133,158 @@ class ProfileDetailViewModel extends ReactiveViewModel // Country List getCountries() => [ - "Afghanistan", - "Albania", - "Algeria", - "Andorra", - "Angola", - "Argentina", - "Armenia", - "Australia", - "Austria", - "Azerbaijan", - "Bahrain", - "Bangladesh", - "Belarus", - "Belgium", - "Belize", - "Benin", - "Bhutan", - "Bolivia", - "Bosnia and Herzegovina", - "Botswana", - "Brazil", - "Brunei", - "Bulgaria", - "Burkina Faso", - "Burundi", - "Cambodia", - "Cameroon", - "Canada", - "Chad", - "Chile", - "China", - "Colombia", - "Comoros", - "Congo", - "Costa Rica", - "Croatia", - "Cuba", - "Cyprus", - "Czech Republic", - "Denmark", - "Djibouti", - "Dominican Republic", - "Ecuador", - "Egypt", - "El Salvador", - "Eritrea", - "Estonia", - "Eswatini", - "Ethiopia", - "Finland", - "France", - "Gabon", - "Gambia", - "Georgia", - "Germany", - "Ghana", - "Greece", - "Guatemala", - "Guinea", - "Haiti", - "Honduras", - "Hungary", - "Iceland", - "India", - "Indonesia", - "Iran", - "Iraq", - "Ireland", - "Israel", - "Italy", - "Jamaica", - "Japan", - "Jordan", - "Kazakhstan", - "Kenya", - "Kuwait", - "Kyrgyzstan", - "Laos", - "Latvia", - "Lebanon", - "Liberia", - "Libya", - "Lithuania", - "Luxembourg", - "Madagascar", - "Malawi", - "Malaysia", - "Maldives", - "Mali", - "Malta", - "Mexico", - "Moldova", - "Monaco", - "Mongolia", - "Morocco", - "Mozambique", - "Myanmar", - "Namibia", - "Nepal", - "Netherlands", - "New Zealand", - "Nicaragua", - "Niger", - "Nigeria", - "North Korea", - "Norway", - "Oman", - "Pakistan", - "Panama", - "Paraguay", - "Peru", - "Philippines", - "Poland", - "Portugal", - "Qatar", - "Romania", - "Russia", - "Rwanda", - "Saudi Arabia", - "Senegal", - "Serbia", - "Singapore", - "Slovakia", - "Slovenia", - "Somalia", - "South Africa", - "South Korea", - "Spain", - "Sri Lanka", - "Sudan", - "Sweden", - "Switzerland", - "Syria", - "Taiwan", - "Tajikistan", - "Tanzania", - "Thailand", - "Tunisia", - "Turkey", - "Uganda", - "Ukraine", - "United Arab Emirates", - "United Kingdom", - "United States", - "Uruguay", - "Uzbekistan", - "Venezuela", - "Vietnam", - "Yemen", - "Zambia", - "Zimbabwe" - ]; + "Afghanistan", + "Albania", + "Algeria", + "Andorra", + "Angola", + "Argentina", + "Armenia", + "Australia", + "Austria", + "Azerbaijan", + "Bahrain", + "Bangladesh", + "Belarus", + "Belgium", + "Belize", + "Benin", + "Bhutan", + "Bolivia", + "Bosnia and Herzegovina", + "Botswana", + "Brazil", + "Brunei", + "Bulgaria", + "Burkina Faso", + "Burundi", + "Cambodia", + "Cameroon", + "Canada", + "Chad", + "Chile", + "China", + "Colombia", + "Comoros", + "Congo", + "Costa Rica", + "Croatia", + "Cuba", + "Cyprus", + "Czech Republic", + "Denmark", + "Djibouti", + "Dominican Republic", + "Ecuador", + "Egypt", + "El Salvador", + "Eritrea", + "Estonia", + "Eswatini", + "Ethiopia", + "Finland", + "France", + "Gabon", + "Gambia", + "Georgia", + "Germany", + "Ghana", + "Greece", + "Guatemala", + "Guinea", + "Haiti", + "Honduras", + "Hungary", + "Iceland", + "India", + "Indonesia", + "Iran", + "Iraq", + "Ireland", + "Israel", + "Italy", + "Jamaica", + "Japan", + "Jordan", + "Kazakhstan", + "Kenya", + "Kuwait", + "Kyrgyzstan", + "Laos", + "Latvia", + "Lebanon", + "Liberia", + "Libya", + "Lithuania", + "Luxembourg", + "Madagascar", + "Malawi", + "Malaysia", + "Maldives", + "Mali", + "Malta", + "Mexico", + "Moldova", + "Monaco", + "Mongolia", + "Morocco", + "Mozambique", + "Myanmar", + "Namibia", + "Nepal", + "Netherlands", + "New Zealand", + "Nicaragua", + "Niger", + "Nigeria", + "North Korea", + "Norway", + "Oman", + "Pakistan", + "Panama", + "Paraguay", + "Peru", + "Philippines", + "Poland", + "Portugal", + "Qatar", + "Romania", + "Russia", + "Rwanda", + "Saudi Arabia", + "Senegal", + "Serbia", + "Singapore", + "Slovakia", + "Slovenia", + "Somalia", + "South Africa", + "South Korea", + "Spain", + "Sri Lanka", + "Sudan", + "Sweden", + "Switzerland", + "Syria", + "Taiwan", + "Tajikistan", + "Tanzania", + "Thailand", + "Tunisia", + "Turkey", + "Uganda", + "Ukraine", + "United Arab Emirates", + "United Kingdom", + "United States", + "Uruguay", + "Uzbekistan", + "Venezuela", + "Vietnam", + "Yemen", + "Zambia", + "Zimbabwe" + ]; void setSelectedCountry(String value) { _selectedCountry = value; @@ -305,24 +301,24 @@ class ProfileDetailViewModel extends ReactiveViewModel // Region List getRegions() => [ - 'Addis Ababa', - 'Afar', - 'Amhara', - 'Benishangul-Gumuz', - 'Central Ethiopia', - 'Dire Dawa', - 'Gambela', - 'Harari', - 'Oromia', - 'Sidama', - 'Somali', - 'South Ethiopia', - 'South West Ethiopia Peoples', - 'Tigray', - ]; + 'Addis Ababa', + 'Afar', + 'Amhara', + 'Benishangul-Gumuz', + 'Central Ethiopia', + 'Dire Dawa', + 'Gambela', + 'Harari', + 'Oromia', + 'Sidama', + 'Somali', + 'South Ethiopia', + 'South West Ethiopia Peoples', + 'Tigray', + ]; - bool checkRegion({required String region,required String country}){ - if(country == 'Ethiopia'){ + bool checkRegion({required String region, required String country}) { + if (country == 'Ethiopia') { return getRegions().contains(region); } return false; diff --git a/lib/ui/views/register/register_viewmodel.dart b/lib/ui/views/register/register_viewmodel.dart index e258e0d..344e9be 100644 --- a/lib/ui/views/register/register_viewmodel.dart +++ b/lib/ui/views/register/register_viewmodel.dart @@ -130,7 +130,6 @@ class RegisterViewModel extends ReactiveViewModel _length = false; } - if (password == confirmPassword) { _passwordMatch = true; } else { @@ -237,6 +236,7 @@ class RegisterViewModel extends ReactiveViewModel } void goBack() { + print('HERE'); if (_currentPage == 1) { _currentPage = 0; rebuildUi(); diff --git a/lib/ui/views/register/screens/create_password_screen.dart b/lib/ui/views/register/screens/create_password_screen.dart index 14a606e..ab8c9f0 100644 --- a/lib/ui/views/register/screens/create_password_screen.dart +++ b/lib/ui/views/register/screens/create_password_screen.dart @@ -213,7 +213,6 @@ class CreatePasswordScreen extends ViewModelWidget { backgroundColor: viewModel.length ? kcPrimaryColor : kcLightGrey, label: '8 characters minimum'); - Widget _buildPasswordMatchValidator(RegisterViewModel viewModel) => ValidatorListTile( backgroundColor: diff --git a/lib/ui/views/telegram_support/telegram_support_view.dart b/lib/ui/views/telegram_support/telegram_support_view.dart index ffe63b7..1b3e518 100644 --- a/lib/ui/views/telegram_support/telegram_support_view.dart +++ b/lib/ui/views/telegram_support/telegram_support_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; +import 'package:yimaru_app/ui/common/app_constants.dart'; import 'package:yimaru_app/ui/widgets/circular_icon.dart'; import '../../common/app_colors.dart'; @@ -91,20 +92,16 @@ class TelegramSupportView extends StackedView { Widget _buildIcon() => const CircularIcon(icon: Icons.telegram, size: 50, color: kcSkyBlue); - Widget _buildTitle() => const Text( + Widget _buildTitle() => Text( 'Join Yimaru Academy on Telegram', + style: style25DG600, textAlign: TextAlign.center, - style: TextStyle( - fontSize: 25, - color: kcDarkGrey, - fontWeight: FontWeight.w600, - ), ); - Widget _buildSubtitle() => const Text( + Widget _buildSubtitle() => Text( 'Connect with our support team instantly on Telegram for quick assistance and community updates', + style: style14MG400, textAlign: TextAlign.center, - style: TextStyle(color: kcMediumGrey), ); Widget _buildLowerColumn(TelegramSupportViewModel viewModel) => Column( @@ -123,31 +120,24 @@ class TelegramSupportView extends StackedView { ]; Widget _buildContinueButton(TelegramSupportViewModel viewModel) => - const CustomElevatedButton( + CustomElevatedButton( height: 55, borderRadius: 12, - leadingIcon: Icons.telegram, text: 'Open in Telegram', foregroundColor: kcWhite, + leadingIcon: Icons.telegram, backgroundColor: kcPrimaryColor, + onTap: () async => await viewModel.launchTelegram(), ); Widget _buildOptionTextDivider() => const OptionTextDivider(); - Widget _buildSearchText() => const Text.rich( - TextSpan( - text: 'Search for', - style: TextStyle( - color: kcDarkGrey, - ), - children: [ - TextSpan( - text: ' @YimaruSupport', - style: TextStyle( - color: kcPrimaryColor, - fontWeight: FontWeight.w600, - ), - ) - ]), + Widget _buildSearchText() => Text.rich( + TextSpan(text: 'Search for', style: style14DG500, children: [ + TextSpan( + style: style14P600, + text: ' $kTelegramSupport', + ) + ]), ); } diff --git a/lib/ui/views/telegram_support/telegram_support_viewmodel.dart b/lib/ui/views/telegram_support/telegram_support_viewmodel.dart index c9f6120..34fc720 100644 --- a/lib/ui/views/telegram_support/telegram_support_viewmodel.dart +++ b/lib/ui/views/telegram_support/telegram_support_viewmodel.dart @@ -2,8 +2,19 @@ import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; import '../../../app/app.locator.dart'; +import '../../../services/url_launcher_service.dart'; +import '../../common/app_constants.dart'; class TelegramSupportViewModel extends BaseViewModel { + // Dependency injection final _navigationService = locator(); + + final _urlLauncherService = locator(); + + // Launch telegram + Future launchTelegram() => + _urlLauncherService.launchUri(kTelegramSupport); + + // Navigation void pop() => _navigationService.back(); } diff --git a/lib/ui/widgets/app_bar_pattern.dart b/lib/ui/widgets/app_bar_pattern.dart new file mode 100644 index 0000000..c6506b1 --- /dev/null +++ b/lib/ui/widgets/app_bar_pattern.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:yimaru_app/ui/common/app_colors.dart'; + +class AppBarPattern extends StatelessWidget { + const AppBarPattern({ + super.key, + }); + + @override + Widget build(BuildContext context) => _buildDecorationImageWrapper(); + + Widget _buildDecorationImageWrapper() => ClipRRect( + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(24), + bottomRight: Radius.circular(24), + ), + child: _buildDecorationImage(), + ); + + Widget _buildDecorationImage() => SizedBox( + width: double.maxFinite, + height: double.maxFinite, + child: _buildPatternWrapper(), + ); + + Widget _buildPatternWrapper() => SizedBox( + width: double.maxFinite, + height: double.maxFinite, + child: _buildPatternMask(), + ); + + Widget _buildPatternMask() => ShaderMask( + shaderCallback: (Rect bounds) => const LinearGradient( + colors: [kcWhite, kcWhite], + ).createShader(bounds), + blendMode: BlendMode.modulate, + child: _buildPattern(), + ); + + Widget _buildPattern() => Image.asset( + 'assets/images/pattern.png', + fit: BoxFit.cover, + ); +} diff --git a/lib/ui/widgets/large_app_bar.dart b/lib/ui/widgets/large_app_bar.dart index c395baf..3886135 100644 --- a/lib/ui/widgets/large_app_bar.dart +++ b/lib/ui/widgets/large_app_bar.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:yimaru_app/ui/common/app_colors.dart'; +import 'package:yimaru_app/ui/widgets/app_bar_pattern.dart'; import 'package:yimaru_app/ui/widgets/language_button.dart'; class LargeAppBar extends StatelessWidget { @@ -18,12 +19,11 @@ class LargeAppBar extends StatelessWidget { required this.showLanguageSelection}); @override - Widget build(BuildContext context) => _buildAppBarWrapper(); + Widget build(BuildContext context) => _buildStackWrapper(); - Widget _buildAppBarWrapper() => Container( + Widget _buildStackWrapper() => Container( height: 125, width: double.maxFinite, - alignment: Alignment.bottomCenter, decoration: const BoxDecoration( color: kcPrimaryColor, borderRadius: BorderRadius.only( @@ -31,6 +31,18 @@ class LargeAppBar extends StatelessWidget { bottomRight: Radius.circular(24), ), ), + child: _buildStack(), + ); + + Widget _buildStack() => Stack( + children: [ _buildPattern(),_buildAppBarWrapper()], + ); + + Widget _buildAppBarWrapper() => Container( + color: kcTransparent, + width: double.maxFinite, + height: double.maxFinite, + alignment: Alignment.bottomCenter, padding: const EdgeInsets.only(bottom: 25, right: 15), child: _buildAppBarItems(), ); @@ -74,4 +86,6 @@ class LargeAppBar extends StatelessWidget { Icons.close, color: kcWhite, ); + + Widget _buildPattern() => const AppBarPattern(); } diff --git a/lib/ui/widgets/learn_course_tile.dart b/lib/ui/widgets/learn_course_tile.dart index 4c05990..493465f 100644 --- a/lib/ui/widgets/learn_course_tile.dart +++ b/lib/ui/widgets/learn_course_tile.dart @@ -143,7 +143,7 @@ class LearnCourseTile extends ViewModelWidget { height: 15, borderRadius: 12, onTap: onViewTap, - text: 'View Courses', + text: 'View Course', foregroundColor: kcWhite, backgroundColor: kcPrimaryColor, ); @@ -158,7 +158,7 @@ class LearnCourseTile extends ViewModelWidget { height: 15, borderRadius: 12, onTap: onPracticeTap, - text: 'Take Practices', + text: 'Take Practice', backgroundColor: kcWhite, borderColor: kcPrimaryColor, foregroundColor: kcPrimaryColor, diff --git a/lib/ui/widgets/learn_lesson_tile.dart b/lib/ui/widgets/learn_lesson_tile.dart index c296116..d710db6 100644 --- a/lib/ui/widgets/learn_lesson_tile.dart +++ b/lib/ui/widgets/learn_lesson_tile.dart @@ -12,12 +12,16 @@ import 'custom_elevated_button.dart'; import 'custom_linear_progress_indicator.dart'; class LearnLessonTile extends ViewModelWidget { + final int index; final LearnLesson lesson; final GestureTapCallback? onLessonTap; final GestureTapCallback? onPracticeTap; - const LearnLessonTile( - {super.key, this.onLessonTap, this.onPracticeTap, required this.lesson}); + const LearnLessonTile({super.key, + this.onLessonTap, + this.onPracticeTap, + required this.index, + required this.lesson}); @override Widget build(BuildContext context, LearnLessonViewModel viewModel) => @@ -52,8 +56,8 @@ class LearnLessonTile extends ViewModelWidget { expandedAlignment: Alignment.centerLeft, backgroundColor: kcPrimaryColor.withOpacity(0.1), controlAffinity: ListTileControlAffinity.trailing, - tilePadding: const EdgeInsets.fromLTRB(15, 15, 15, 0), expandedCrossAxisAlignment: CrossAxisAlignment.start, + tilePadding: const EdgeInsets.fromLTRB(15, 15, 15, 15), collapsedBackgroundColor: kcPrimaryColor.withOpacity(0.1), childrenPadding: const EdgeInsets.fromLTRB(15, 0, 15, 15), // enabled: (lesson.access?.isAccessible ?? false), @@ -130,11 +134,12 @@ class LearnLessonTile extends ViewModelWidget { Widget _buildActionButtons(LearnLessonViewModel viewModel) => Row( mainAxisAlignment: MainAxisAlignment.end, - children: __buildActionButtonChildren(viewModel), + children: _buildActionButtonChildren(viewModel), ); - List __buildActionButtonChildren(LearnLessonViewModel viewModel) => [ - _buildPracticeButtonWrapper(viewModel), + List _buildActionButtonChildren(LearnLessonViewModel viewModel) => [ + if (index != viewModel.lessons.length - 1) + _buildPracticeButtonWrapper(viewModel), horizontalSpaceSmall, _buildLessonButtonWrapper(viewModel) ]; diff --git a/lib/ui/widgets/learn_practice_result_card.dart b/lib/ui/widgets/learn_practice_result_card.dart index b2fba0a..7a0a6a7 100644 --- a/lib/ui/widgets/learn_practice_result_card.dart +++ b/lib/ui/widgets/learn_practice_result_card.dart @@ -15,7 +15,7 @@ class LearnPracticeResultCard extends ViewModelWidget { _buildColumnWrapper(viewModel); Widget _buildColumnWrapper(LearnPracticeViewModel viewModel) => SizedBox( - height: 100, + height: 125, width: double.maxFinite, child: _buildColumn(viewModel), ); @@ -30,7 +30,8 @@ class LearnPracticeResultCard extends ViewModelWidget { [_buildQuestion(viewModel), verticalSpaceSmall, _buildRow()]; Widget _buildQuestion(LearnPracticeViewModel viewModel) => Text( - answer['sample_text_answer'], + answer['question_text'], + maxLines: 2, style: style14DG400, ); diff --git a/lib/ui/widgets/mini_thumbnail.dart b/lib/ui/widgets/mini_thumbnail.dart index 8aecf3b..63bb4a0 100644 --- a/lib/ui/widgets/mini_thumbnail.dart +++ b/lib/ui/widgets/mini_thumbnail.dart @@ -14,8 +14,7 @@ class MiniThumbnail extends StatelessWidget { Widget build(BuildContext context) => _buildWrapper(); Widget _buildWrapper() => SizedBox( - width: 75, - height: double.maxFinite, + width: 80, child: _buildLeadingClipper(), ); @@ -40,15 +39,17 @@ class MiniThumbnail extends StatelessWidget { : _buildNetworkImage(); Widget _buildNetworkImage() => CachedNetworkImage( + fit: BoxFit.cover, imageUrl: thumbnail, - fit: BoxFit.fill, width: double.maxFinite, + height: double.maxFinite, ); Widget _buildLocalImage() => Image.asset( thumbnail, - fit: BoxFit.fill, + fit: BoxFit.cover, width: double.maxFinite, + height: double.maxFinite, ); Widget _buildPlayButtonWrapper() => Align( diff --git a/pubspec.yaml b/pubspec.yaml index 0d06ac0..56b6d86 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: yimaru_app -version: 0.1.13+15 +version: 0.1.14+16 publish_to: 'none' description: A new Flutter project.